├── .eslintrc ├── .gitignore ├── .jscs.json ├── .npmignore ├── .travis.yml ├── ChangeLog ├── LICENSE.BSD ├── README.md ├── bin ├── esparse.js └── esvalidate.js ├── component.json ├── esprima.js ├── examples ├── detectnestedternary.js ├── findbooleantrap.js └── tokendist.js ├── package.json ├── test ├── 3rdparty │ ├── XMLHttpRequest.js │ ├── acorn.js │ ├── angular-1.0.2.js │ ├── backbone-0.9.2.js │ ├── benchmark.js │ ├── codemirror-2.34.js │ ├── escodegen.browser.js │ ├── esmangle.browser.js │ ├── esmorph.js │ ├── harmonizr.js │ ├── jquery-1.8.2.js │ ├── jquery.mobile-1.2.0.js │ ├── modifier.js │ ├── mootools-1.4.1.js │ ├── parse-js.js │ ├── threejs-r51.js │ └── underscore-1.4.1.js ├── benchmarks.js ├── compare.js ├── compat.js ├── fbtest.js ├── fbtest.rec.js ├── harmonymodulestest.js ├── harmonytest.js ├── index.html ├── module.js ├── reflect.js ├── run.js ├── runner.js └── test.js └── tools ├── check-version.js ├── generate-fbtest.js ├── generate-test-fixture.js ├── generate-unicode-regex.py ├── list-complexity.js ├── stringify.js └── update-coverage.sh /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "amd": true 6 | }, 7 | 8 | "rules": { 9 | "no-alert": 2, 10 | "no-array-constructor": 2, 11 | "no-caller": 2, 12 | "no-bitwise": 0, 13 | "no-catch-shadow": 2, 14 | "no-console": 2, 15 | "no-comma-dangle": 2, 16 | "no-control-regex": 2, 17 | "no-debugger": 2, 18 | "no-div-regex": 2, 19 | "no-dupe-keys": 2, 20 | "no-else-return": 2, 21 | "no-empty": 2, 22 | "no-empty-class": 2, 23 | "no-eq-null": 2, 24 | "no-eval": 2, 25 | "no-ex-assign": 2, 26 | "no-func-assign": 0, 27 | "no-floating-decimal": 2, 28 | "no-implied-eval": 2, 29 | "no-with": 2, 30 | "no-fallthrough": 2, 31 | "no-unreachable": 2, 32 | "no-undef": 2, 33 | "no-undef-init": 2, 34 | "no-unused-expressions": 2, 35 | "no-octal": 2, 36 | "no-octal-escape": 2, 37 | "no-obj-calls": 2, 38 | "no-multi-str": 2, 39 | "no-new-wrappers": 2, 40 | "no-new": 2, 41 | "no-new-func": 2, 42 | "no-native-reassign": 2, 43 | "no-plusplus": 0, 44 | "no-delete-var": 2, 45 | "no-return-assign": 2, 46 | "no-new-object": 2, 47 | "no-label-var": 2, 48 | "no-ternary": 0, 49 | "no-self-compare": 2, 50 | "no-sync": 2, 51 | "no-underscore-dangle": 2, 52 | "no-loop-func": 2, 53 | "no-empty-label": 2, 54 | "no-unused-vars": 1, 55 | "no-script-url": 2, 56 | "no-proto": 2, 57 | "no-iterator": 2, 58 | "no-mixed-requires": [0, false], 59 | "no-wrap-func": 2, 60 | "no-shadow": 2, 61 | "no-use-before-define": 0, 62 | "no-redeclare": 2, 63 | "no-regex-spaces": 2, 64 | 65 | "brace-style": 0, 66 | "block-scoped-var": 0, 67 | "camelcase": 2, 68 | "complexity": [0, 11], 69 | "consistent-this": [0, "that"], 70 | "curly": 2, 71 | "dot-notation": 2, 72 | "eqeqeq": 2, 73 | "guard-for-in": 0, 74 | "max-depth": [0, 4], 75 | "max-len": [0, 80, 4], 76 | "max-params": [0, 3], 77 | "max-statements": [0, 10], 78 | "new-cap": 2, 79 | "new-parens": 2, 80 | "one-var": 0, 81 | "quotes": [2, "single"], 82 | "quote-props": 0, 83 | "radix": 0, 84 | "semi": 2, 85 | "strict": 2, 86 | "unnecessary-strict": 0, 87 | "use-isnan": 2, 88 | "valid-typeof": 0, 89 | "wrap-iife": 2, 90 | "wrap-regex": 0 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | node_modules 3 | -------------------------------------------------------------------------------- /.jscs.json: -------------------------------------------------------------------------------- 1 | { 2 | "requireCurlyBraces": [ 3 | "if", 4 | "else", 5 | "for", 6 | "while", 7 | "do", 8 | "try", 9 | "catch" 10 | ], 11 | "requireSpaceAfterKeywords": [ 12 | "if", 13 | "else", 14 | "for", 15 | "while", 16 | "do", 17 | "switch", 18 | "case", 19 | "return", 20 | "try", 21 | "catch", 22 | "function", 23 | "typeof" 24 | ], 25 | "requireSpaceBeforeBlockStatements": true, 26 | "requireParenthesesAroundIIFE": true, 27 | "requireSpacesInConditionalExpression": true, 28 | "disallowSpacesInNamedFunctionExpression": { 29 | "beforeOpeningRoundBrace": true 30 | }, 31 | "disallowSpacesInFunctionDeclaration": { 32 | "beforeOpeningRoundBrace": true 33 | }, 34 | "requireMultipleVarDecl": "onevar", 35 | "requireBlocksOnNewline": true, 36 | "disallowEmptyBlocks": true, 37 | "disallowSpacesInsideParentheses": true, 38 | "disallowDanglingUnderscores": true, 39 | "requireCommaBeforeLineBreak": true, 40 | "disallowSpaceAfterPrefixUnaryOperators": true, 41 | "disallowSpaceBeforePostfixUnaryOperators": true, 42 | "disallowSpaceBeforeBinaryOperators": [ 43 | "," 44 | ], 45 | "requireSpacesInForStatement": true, 46 | "requireSpaceBeforeBinaryOperators": true, 47 | "requireSpaceAfterBinaryOperators": true, 48 | "disallowKeywords": [ 49 | "with", 50 | "continue" 51 | ], 52 | "validateIndentation": 4, 53 | "disallowMixedSpacesAndTabs": true, 54 | "disallowTrailingWhitespace": true, 55 | "disallowTrailingComma": true, 56 | "disallowKeywordsOnNewLine": [ 57 | "else" 58 | ], 59 | "requireLineFeedAtFileEnd": true, 60 | "requireCapitalizedConstructors": true, 61 | "requireDotNotation": true, 62 | "disallowNewlineBeforeBlockStatements": true, 63 | "disallowMultipleLineStrings": true 64 | } 65 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git 2 | .travis.yml 3 | /node_modules/ 4 | /assets/ 5 | /coverage/ 6 | /demo/ 7 | /test/3rdparty 8 | /tools/ 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | install: "npm update -g npm; npm install" 3 | node_js: 4 | - "0.11" 5 | - "0.10" 6 | - "0.8" 7 | 8 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2012-11-02: Version 1.0.2 2 | 3 | Improvement: 4 | 5 | * Fix esvalidate JUnit output upon a syntax error (issue 374) 6 | 7 | 2012-10-28: Version 1.0.1 8 | 9 | Improvements: 10 | 11 | * esvalidate understands shebang in a Unix shell script (issue 361) 12 | * esvalidate treats fatal parsing failure as an error (issue 361) 13 | * Reduce Node.js package via .npmignore (issue 362) 14 | 15 | 2012-10-22: Version 1.0.0 16 | 17 | Initial release. 18 | -------------------------------------------------------------------------------- /LICENSE.BSD: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions are met: 3 | 4 | * Redistributions of source code must retain the above copyright 5 | notice, this list of conditions and the following disclaimer. 6 | * Redistributions in binary form must reproduce the above copyright 7 | notice, this list of conditions and the following disclaimer in the 8 | documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 11 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 12 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 13 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 14 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 15 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 16 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 17 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 19 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | - - - 2 | 3 | **_This project is not actively maintained. Proceed at your own risk!_** 4 | 5 | - - - 6 | 7 | **Esprima** ([esprima.org](http://esprima.org), BSD license) is a high performance, 8 | standard-compliant [ECMAScript](http://www.ecma-international.org/publications/standards/Ecma-262.htm) 9 | parser written in ECMAScript (also popularly known as 10 | [JavaScript](http://en.wikipedia.org/wiki/JavaScript>JavaScript)). 11 | Esprima is created and maintained by [Ariya Hidayat](http://twitter.com/ariyahidayat), 12 | with the help of [many contributors](https://github.com/ariya/esprima/contributors). 13 | 14 | **Esprima-FB** is a fork of the [Harmony branch](https://github.com/ariya/esprima/tree/harmony) of Esprima that implements [JSX specification](https://github.com/facebook/jsx) on top of ECMAScript syntax. 15 | 16 | ### Features 17 | 18 | - Full support for ECMAScript 5.1 ([ECMA-262](http://www.ecma-international.org/publications/standards/Ecma-262.htm)) 19 | - Experimental support for ES6/Harmony (module, class, destructuring, ...) 20 | - Full support for [JSX syntax extensions](https://github.com/facebook/jsx). 21 | - Sensible [syntax tree format](https://github.com/facebook/jsx/blob/master/AST.md) compatible with Mozilla 22 | [Parser AST](https://developer.mozilla.org/en/SpiderMonkey/Parser_API) 23 | - Optional tracking of syntax node location (index-based and line-column) 24 | - Heavily tested (> 650 [unit tests](http://esprima.org/test/) with [full code coverage](http://esprima.org/test/coverage.html)) 25 | - Ongoing support for ES6/Harmony (module, class, destructuring, ...) 26 | 27 | ### Versioning rules 28 | 29 | In order to follow semver rules and keep reference to original Esprima versions at the same time, we left 3 digits of each version part to refer to upstream harmony branch. We then take the most significant digit. 30 | 31 | **Example:** 4001.3001.0000-dev-harmony-fb aligns with 1.1.0-dev-harmony (aka 001.001.000-dev-harmony) in upstream, with our own changes on top. 32 | 33 | Esprima-FB serves as a **building block** for JSX language tools and transpiler implementations (such as [React](https://github.com/facebook/react) or [JSXDOM](https://github.com/vjeux/jsxdom)). 34 | 35 | Esprima-FB runs on many popular web browsers, as well as other ECMAScript platforms such as 36 | [Rhino](http://www.mozilla.org/rhino) and [Node.js](https://npmjs.org/package/esprima). 37 | 38 | For more information on original Esprima, check the web site [esprima.org](http://esprima.org). 39 | -------------------------------------------------------------------------------- /bin/esparse.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | Copyright (C) 2012 Ariya Hidayat 4 | Copyright (C) 2011 Ariya Hidayat 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | /*jslint sloppy:true node:true rhino:true */ 28 | 29 | var fs, esprima, fname, content, options, syntax; 30 | 31 | if (typeof require === 'function') { 32 | fs = require('fs'); 33 | esprima = require('esprima'); 34 | } else if (typeof load === 'function') { 35 | try { 36 | load('esprima.js'); 37 | } catch (e) { 38 | load('../esprima.js'); 39 | } 40 | } 41 | 42 | // Shims to Node.js objects when running under Rhino. 43 | if (typeof console === 'undefined' && typeof process === 'undefined') { 44 | console = { log: print }; 45 | fs = { readFileSync: readFile }; 46 | process = { argv: arguments, exit: quit }; 47 | process.argv.unshift('esparse.js'); 48 | process.argv.unshift('rhino'); 49 | } 50 | 51 | function showUsage() { 52 | console.log('Usage:'); 53 | console.log(' esparse [options] file.js'); 54 | console.log(); 55 | console.log('Available options:'); 56 | console.log(); 57 | console.log(' --comment Gather all line and block comments in an array'); 58 | console.log(' --loc Include line-column location info for each syntax node'); 59 | console.log(' --range Include index-based range for each syntax node'); 60 | console.log(' --raw Display the raw value of literals'); 61 | console.log(' --tokens List all tokens in an array'); 62 | console.log(' --tolerant Tolerate errors on a best-effort basis (experimental)'); 63 | console.log(' -v, --version Shows program version'); 64 | console.log(); 65 | process.exit(1); 66 | } 67 | 68 | if (process.argv.length <= 2) { 69 | showUsage(); 70 | } 71 | 72 | options = {}; 73 | 74 | process.argv.splice(2).forEach(function (entry) { 75 | 76 | if (entry === '-h' || entry === '--help') { 77 | showUsage(); 78 | } else if (entry === '-v' || entry === '--version') { 79 | console.log('ECMAScript Parser (using Esprima version', esprima.version, ')'); 80 | console.log(); 81 | process.exit(0); 82 | } else if (entry === '--comment') { 83 | options.comment = true; 84 | } else if (entry === '--loc') { 85 | options.loc = true; 86 | } else if (entry === '--range') { 87 | options.range = true; 88 | } else if (entry === '--raw') { 89 | options.raw = true; 90 | } else if (entry === '--tokens') { 91 | options.tokens = true; 92 | } else if (entry === '--tolerant') { 93 | options.tolerant = true; 94 | } else if (entry.slice(0, 2) === '--') { 95 | console.log('Error: unknown option ' + entry + '.'); 96 | process.exit(1); 97 | } else if (typeof fname === 'string') { 98 | console.log('Error: more than one input file.'); 99 | process.exit(1); 100 | } else { 101 | fname = entry; 102 | } 103 | }); 104 | 105 | if (typeof fname !== 'string') { 106 | console.log('Error: no input file.'); 107 | process.exit(1); 108 | } 109 | 110 | try { 111 | content = fs.readFileSync(fname, 'utf-8'); 112 | syntax = esprima.parse(content, options); 113 | console.log(JSON.stringify(syntax, null, 4)); 114 | } catch (e) { 115 | console.log('Error: ' + e.message); 116 | process.exit(1); 117 | } 118 | -------------------------------------------------------------------------------- /bin/esvalidate.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | Copyright (C) 2012 Ariya Hidayat 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /*jslint sloppy:true plusplus:true node:true rhino:true */ 27 | /*global phantom:true */ 28 | 29 | var fs, system, esprima, options, fnames, count; 30 | 31 | if (typeof esprima === 'undefined') { 32 | // PhantomJS can only require() relative files 33 | if (typeof phantom === 'object') { 34 | fs = require('fs'); 35 | system = require('system'); 36 | esprima = require('./esprima'); 37 | } else if (typeof require === 'function') { 38 | fs = require('fs'); 39 | esprima = require('esprima'); 40 | } else if (typeof load === 'function') { 41 | try { 42 | load('esprima.js'); 43 | } catch (e) { 44 | load('../esprima.js'); 45 | } 46 | } 47 | } 48 | 49 | // Shims to Node.js objects when running under PhantomJS 1.7+. 50 | if (typeof phantom === 'object') { 51 | fs.readFileSync = fs.read; 52 | process = { 53 | argv: [].slice.call(system.args), 54 | exit: phantom.exit 55 | }; 56 | process.argv.unshift('phantomjs'); 57 | } 58 | 59 | // Shims to Node.js objects when running under Rhino. 60 | if (typeof console === 'undefined' && typeof process === 'undefined') { 61 | console = { log: print }; 62 | fs = { readFileSync: readFile }; 63 | process = { argv: arguments, exit: quit }; 64 | process.argv.unshift('esvalidate.js'); 65 | process.argv.unshift('rhino'); 66 | } 67 | 68 | function showUsage() { 69 | console.log('Usage:'); 70 | console.log(' esvalidate [options] file.js'); 71 | console.log(); 72 | console.log('Available options:'); 73 | console.log(); 74 | console.log(' --format=type Set the report format, plain (default) or junit'); 75 | console.log(' -v, --version Print program version'); 76 | console.log(); 77 | process.exit(1); 78 | } 79 | 80 | if (process.argv.length <= 2) { 81 | showUsage(); 82 | } 83 | 84 | options = { 85 | format: 'plain' 86 | }; 87 | 88 | fnames = []; 89 | 90 | process.argv.splice(2).forEach(function (entry) { 91 | 92 | if (entry === '-h' || entry === '--help') { 93 | showUsage(); 94 | } else if (entry === '-v' || entry === '--version') { 95 | console.log('ECMAScript Validator (using Esprima version', esprima.version, ')'); 96 | console.log(); 97 | process.exit(0); 98 | } else if (entry.slice(0, 9) === '--format=') { 99 | options.format = entry.slice(9); 100 | if (options.format !== 'plain' && options.format !== 'junit') { 101 | console.log('Error: unknown report format ' + options.format + '.'); 102 | process.exit(1); 103 | } 104 | } else if (entry.slice(0, 2) === '--') { 105 | console.log('Error: unknown option ' + entry + '.'); 106 | process.exit(1); 107 | } else { 108 | fnames.push(entry); 109 | } 110 | }); 111 | 112 | if (fnames.length === 0) { 113 | console.log('Error: no input file.'); 114 | process.exit(1); 115 | } 116 | 117 | if (options.format === 'junit') { 118 | console.log(''); 119 | console.log(''); 120 | } 121 | 122 | count = 0; 123 | fnames.forEach(function (fname) { 124 | var content, timestamp, syntax, name; 125 | try { 126 | content = fs.readFileSync(fname, 'utf-8'); 127 | 128 | if (content[0] === '#' && content[1] === '!') { 129 | content = '//' + content.substr(2, content.length); 130 | } 131 | 132 | timestamp = Date.now(); 133 | syntax = esprima.parse(content, { tolerant: true }); 134 | 135 | if (options.format === 'junit') { 136 | 137 | name = fname; 138 | if (name.lastIndexOf('/') >= 0) { 139 | name = name.slice(name.lastIndexOf('/') + 1); 140 | } 141 | 142 | console.log(''); 147 | 148 | syntax.errors.forEach(function (error) { 149 | var msg = error.message; 150 | msg = msg.replace(/^Line\ [0-9]*\:\ /, ''); 151 | console.log(' '); 153 | console.log(' ' + 154 | error.message + '(' + name + ':' + error.lineNumber + ')' + 155 | ''); 156 | console.log(' '); 157 | }); 158 | 159 | console.log(''); 160 | 161 | } else if (options.format === 'plain') { 162 | 163 | syntax.errors.forEach(function (error) { 164 | var msg = error.message; 165 | msg = msg.replace(/^Line\ [0-9]*\:\ /, ''); 166 | msg = fname + ':' + error.lineNumber + ': ' + msg; 167 | console.log(msg); 168 | ++count; 169 | }); 170 | 171 | } 172 | } catch (e) { 173 | ++count; 174 | if (options.format === 'junit') { 175 | console.log(''); 177 | console.log(' '); 178 | console.log(' ' + 179 | e.message + '(' + fname + ((e.lineNumber) ? ':' + e.lineNumber : '') + 180 | ')'); 181 | console.log(' '); 182 | console.log(''); 183 | } else { 184 | console.log('Error: ' + e.message); 185 | } 186 | } 187 | }); 188 | 189 | if (options.format === 'junit') { 190 | console.log(''); 191 | } 192 | 193 | if (count > 0) { 194 | process.exit(1); 195 | } 196 | 197 | if (count === 0 && typeof phantom === 'object') { 198 | process.exit(0); 199 | } 200 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esprima", 3 | "version": "15001.1001.0-dev-harmony-fb", 4 | "main": "./esprima.js", 5 | "dependencies": {}, 6 | "repository": { 7 | "type": "git", 8 | "url": "http://github.com/ariya/esprima.git" 9 | }, 10 | "licenses": [{ 11 | "type": "BSD", 12 | "url": "http://github.com/ariya/esprima/raw/master/LICENSE.BSD" 13 | }] 14 | } 15 | -------------------------------------------------------------------------------- /examples/detectnestedternary.js: -------------------------------------------------------------------------------- 1 | // Usage: node detectnestedternary.js /path/to/some/directory 2 | // For more details, please read http://esprima.org/doc/#nestedternary 3 | 4 | /*jslint node:true sloppy:true plusplus:true */ 5 | 6 | var fs = require('fs'), 7 | esprima = require('../esprima'), 8 | dirname = process.argv[2]; 9 | 10 | 11 | // Executes visitor on the object and its children (recursively). 12 | function traverse(object, visitor) { 13 | var key, child; 14 | 15 | visitor.call(null, object); 16 | for (key in object) { 17 | if (object.hasOwnProperty(key)) { 18 | child = object[key]; 19 | if (typeof child === 'object' && child !== null) { 20 | traverse(child, visitor); 21 | } 22 | } 23 | } 24 | } 25 | 26 | // http://stackoverflow.com/q/5827612/ 27 | function walk(dir, done) { 28 | var results = []; 29 | fs.readdir(dir, function (err, list) { 30 | if (err) { 31 | return done(err); 32 | } 33 | var i = 0; 34 | (function next() { 35 | var file = list[i++]; 36 | if (!file) { 37 | return done(null, results); 38 | } 39 | file = dir + '/' + file; 40 | fs.stat(file, function (err, stat) { 41 | if (stat && stat.isDirectory()) { 42 | walk(file, function (err, res) { 43 | results = results.concat(res); 44 | next(); 45 | }); 46 | } else { 47 | results.push(file); 48 | next(); 49 | } 50 | }); 51 | }()); 52 | }); 53 | } 54 | 55 | walk(dirname, function (err, results) { 56 | if (err) { 57 | console.log('Error', err); 58 | return; 59 | } 60 | 61 | results.forEach(function (filename) { 62 | var shortname, first, content, syntax; 63 | 64 | shortname = filename; 65 | first = true; 66 | 67 | if (shortname.substr(0, dirname.length) === dirname) { 68 | shortname = shortname.substr(dirname.length + 1, shortname.length); 69 | } 70 | 71 | function report(node, problem) { 72 | if (first === true) { 73 | console.log(shortname + ': '); 74 | first = false; 75 | } 76 | console.log(' Line', node.loc.start.line, ':', problem); 77 | } 78 | 79 | function checkConditional(node) { 80 | var condition; 81 | 82 | if (node.consequent.type === 'ConditionalExpression' || 83 | node.alternate.type === 'ConditionalExpression') { 84 | 85 | condition = content.substring(node.test.range[0], node.test.range[1]); 86 | if (condition.length > 20) { 87 | condition = condition.substring(0, 20) + '...'; 88 | } 89 | condition = '"' + condition + '"'; 90 | report(node, 'Nested ternary for ' + condition); 91 | } 92 | } 93 | 94 | try { 95 | content = fs.readFileSync(filename, 'utf-8'); 96 | syntax = esprima.parse(content, { tolerant: true, loc: true, range: true }); 97 | traverse(syntax, function (node) { 98 | if (node.type === 'ConditionalExpression') { 99 | checkConditional(node); 100 | } 101 | }); 102 | } catch (e) { 103 | } 104 | 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /examples/findbooleantrap.js: -------------------------------------------------------------------------------- 1 | // Usage: node findbooleantrap.js /path/to/some/directory 2 | // For more details, please read http://esprima.org/doc/#booleantrap. 3 | 4 | /*jslint node:true sloppy:true plusplus:true */ 5 | 6 | var fs = require('fs'), 7 | esprima = require('../esprima'), 8 | dirname = process.argv[2], 9 | doubleNegativeList = []; 10 | 11 | 12 | // Black-list of terms with double-negative meaning. 13 | doubleNegativeList = [ 14 | 'hidden', 15 | 'caseinsensitive', 16 | 'disabled' 17 | ]; 18 | 19 | 20 | // Executes visitor on the object and its children (recursively). 21 | function traverse(object, visitor) { 22 | var key, child; 23 | 24 | if (visitor.call(null, object) === false) { 25 | return; 26 | } 27 | for (key in object) { 28 | if (object.hasOwnProperty(key)) { 29 | child = object[key]; 30 | if (typeof child === 'object' && child !== null) { 31 | traverse(child, visitor); 32 | } 33 | } 34 | } 35 | } 36 | 37 | // http://stackoverflow.com/q/5827612/ 38 | function walk(dir, done) { 39 | var results = []; 40 | fs.readdir(dir, function (err, list) { 41 | if (err) { 42 | return done(err); 43 | } 44 | var i = 0; 45 | (function next() { 46 | var file = list[i++]; 47 | if (!file) { 48 | return done(null, results); 49 | } 50 | file = dir + '/' + file; 51 | fs.stat(file, function (err, stat) { 52 | if (stat && stat.isDirectory()) { 53 | walk(file, function (err, res) { 54 | results = results.concat(res); 55 | next(); 56 | }); 57 | } else { 58 | results.push(file); 59 | next(); 60 | } 61 | }); 62 | }()); 63 | }); 64 | } 65 | 66 | walk(dirname, function (err, results) { 67 | if (err) { 68 | console.log('Error', err); 69 | return; 70 | } 71 | 72 | results.forEach(function (filename) { 73 | var shortname, first, content, syntax; 74 | 75 | shortname = filename; 76 | first = true; 77 | 78 | if (shortname.substr(0, dirname.length) === dirname) { 79 | shortname = shortname.substr(dirname.length + 1, shortname.length); 80 | } 81 | 82 | function getFunctionName(node) { 83 | if (node.callee.type === 'Identifier') { 84 | return node.callee.name; 85 | } 86 | if (node.callee.type === 'MemberExpression') { 87 | return node.callee.property.name; 88 | } 89 | } 90 | 91 | function report(node, problem) { 92 | if (first === true) { 93 | console.log(shortname + ': '); 94 | first = false; 95 | } 96 | console.log(' Line', node.loc.start.line, 'in function', 97 | getFunctionName(node) + ':', problem); 98 | } 99 | 100 | function checkSingleArgument(node) { 101 | var args = node['arguments'], 102 | functionName = getFunctionName(node); 103 | 104 | if ((args.length !== 1) || (typeof args[0].value !== 'boolean')) { 105 | return; 106 | } 107 | 108 | // Check if the method is a setter, i.e. starts with 'set', 109 | // e.g. 'setEnabled(false)'. 110 | if (functionName.substr(0, 3) !== 'set') { 111 | report(node, 'Boolean literal with a non-setter function'); 112 | } 113 | 114 | // Does it contain a term with double-negative meaning? 115 | doubleNegativeList.forEach(function (term) { 116 | if (functionName.toLowerCase().indexOf(term.toLowerCase()) >= 0) { 117 | report(node, 'Boolean literal with confusing double-negative'); 118 | } 119 | }); 120 | } 121 | 122 | function checkMultipleArguments(node) { 123 | var args = node['arguments'], 124 | literalCount = 0; 125 | 126 | args.forEach(function (arg) { 127 | if (typeof arg.value === 'boolean') { 128 | literalCount++; 129 | } 130 | }); 131 | 132 | // At least two arguments must be Boolean literals. 133 | if (literalCount >= 2) { 134 | 135 | // Check for two different Boolean literals in one call. 136 | if (literalCount === 2 && args.length === 2) { 137 | if (args[0].value !== args[1].value) { 138 | report(node, 'Confusing true vs false'); 139 | return; 140 | } 141 | } 142 | 143 | report(node, 'Multiple Boolean literals'); 144 | } 145 | } 146 | 147 | function checkLastArgument(node) { 148 | var args = node['arguments']; 149 | 150 | if (args.length < 2) { 151 | return; 152 | } 153 | 154 | if (typeof args[args.length - 1].value === 'boolean') { 155 | report(node, 'Ambiguous Boolean literal as the last argument'); 156 | } 157 | } 158 | 159 | try { 160 | content = fs.readFileSync(filename, 'utf-8'); 161 | syntax = esprima.parse(content, { tolerant: true, loc: true }); 162 | traverse(syntax, function (node) { 163 | if (node.type === 'CallExpression') { 164 | checkSingleArgument(node); 165 | checkLastArgument(node); 166 | checkMultipleArguments(node); 167 | } 168 | }); 169 | } catch (e) { 170 | } 171 | 172 | }); 173 | }); 174 | -------------------------------------------------------------------------------- /examples/tokendist.js: -------------------------------------------------------------------------------- 1 | /*jslint node:true */ 2 | var fs = require('fs'), 3 | esprima = require('../esprima'), 4 | files = process.argv.splice(2), 5 | histogram, 6 | type; 7 | 8 | histogram = { 9 | Boolean: 0, 10 | Identifier: 0, 11 | Keyword: 0, 12 | Null: 0, 13 | Numeric: 0, 14 | Punctuator: 0, 15 | RegularExpression: 0, 16 | String: 0 17 | }; 18 | 19 | files.forEach(function (filename) { 20 | 'use strict'; 21 | var content = fs.readFileSync(filename, 'utf-8'), 22 | tokens = esprima.parse(content, { tokens: true }).tokens; 23 | 24 | tokens.forEach(function (token) { 25 | histogram[token.type] += 1; 26 | }); 27 | }); 28 | 29 | for (type in histogram) { 30 | if (histogram.hasOwnProperty(type)) { 31 | console.log(type, histogram[type]); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esprima-fb", 3 | "description": "Facebook-specific fork of the esprima project", 4 | "homepage": "https://github.com/facebook/esprima/tree/fb-harmony", 5 | "main": "esprima.js", 6 | "bin": { 7 | "esparse": "./bin/esparse.js", 8 | "esvalidate": "./bin/esvalidate.js" 9 | }, 10 | "version": "15001.1001.0-dev-harmony-fb", 11 | "files": [ 12 | "bin", 13 | "test/run.js", 14 | "test/runner.js", 15 | "test/test.js", 16 | "test/compat.js", 17 | "test/reflect.js", 18 | "esprima.js" 19 | ], 20 | "engines": { 21 | "node": ">=0.4.0" 22 | }, 23 | "author": { 24 | "name": "Ariya Hidayat", 25 | "email": "ariya.hidayat@gmail.com" 26 | }, 27 | "maintainers": [{ 28 | "name": "Jeff Morrison", 29 | "email": "jeffmo@fb.com", 30 | "web": "https://www.facebook.com/lbljeffmo" 31 | }], 32 | "repository": { 33 | "type": "git", 34 | "url": "http://github.com/facebook/esprima.git" 35 | }, 36 | "bugs": { 37 | "url": "http://issues.esprima.org" 38 | }, 39 | "licenses": [{ 40 | "type": "BSD", 41 | "url": "http://github.com/facebook/esprima/raw/master/LICENSE.BSD" 42 | }], 43 | "devDependencies": { 44 | "eslint": "~0.12.0", 45 | "jscs": "~1.10.0", 46 | "istanbul": "~0.2.6", 47 | "escomplex-js": "1.0.0", 48 | "complexity-report": "~1.1.1", 49 | "regenerate": "~0.5.4", 50 | "unicode-6.3.0": "~0.1.0", 51 | "json-diff": "~0.3.1", 52 | "commander": "~2.5.0" 53 | }, 54 | "scripts": { 55 | "generate-regex": "node tools/generate-identifier-regex.js", 56 | 57 | "test": "node test/run.js && npm run lint && npm run coverage", 58 | 59 | "lint": "npm run check-version && npm run eslint && npm run jscs && npm run complexity", 60 | "check-version": "node tools/check-version.js", 61 | "jscs": "jscs esprima.js test/*test.js", 62 | "eslint": "node node_modules/eslint/bin/eslint.js esprima.js", 63 | "complexity": "node tools/list-complexity.js && cr -s -l -w --maxcyc 18 esprima.js", 64 | 65 | "coverage": "npm run analyze-coverage && npm run check-coverage", 66 | "analyze-coverage": "node node_modules/istanbul/lib/cli.js cover test/runner.js", 67 | "check-coverage": "node node_modules/istanbul/lib/cli.js check-coverage --statement 100 --branch 100 --function 100", 68 | 69 | "benchmark": "node test/benchmarks.js", 70 | "benchmark-quick": "node test/benchmarks.js quick" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/3rdparty/XMLHttpRequest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * XMLHttpRequest.js Copyright (C) 2011 Sergey Ilinsky (http://www.ilinsky.com) 3 | * 4 | * This work is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU Lesser General Public License as published by 6 | * the Free Software Foundation; either version 2.1 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This work is distributed in the hope that it will be useful, 10 | * but without any warranty; without even the implied warranty of 11 | * merchantability or fitness for a particular purpose. See the 12 | * GNU Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public License 15 | * along with this library; if not, write to the Free Software Foundation, Inc., 16 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | */ 18 | 19 | (function () { 20 | 21 | // Save reference to earlier defined object implementation (if any) 22 | var oXMLHttpRequest = window.XMLHttpRequest; 23 | 24 | // Define on browser type 25 | var bGecko = !!window.controllers; 26 | var bIE = window.document.all && !window.opera; 27 | var bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/); 28 | 29 | // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()" 30 | function fXMLHttpRequest() { 31 | this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP"); 32 | this._listeners = []; 33 | } 34 | 35 | // Constructor 36 | function cXMLHttpRequest() { 37 | return new fXMLHttpRequest; 38 | } 39 | cXMLHttpRequest.prototype = fXMLHttpRequest.prototype; 40 | 41 | // BUGFIX: Firefox with Firebug installed would break pages if not executed 42 | if (bGecko && oXMLHttpRequest.wrapped) { 43 | cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped; 44 | } 45 | 46 | // Constants 47 | cXMLHttpRequest.UNSENT = 0; 48 | cXMLHttpRequest.OPENED = 1; 49 | cXMLHttpRequest.HEADERS_RECEIVED = 2; 50 | cXMLHttpRequest.LOADING = 3; 51 | cXMLHttpRequest.DONE = 4; 52 | 53 | // Public Properties 54 | cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT; 55 | cXMLHttpRequest.prototype.responseText = ''; 56 | cXMLHttpRequest.prototype.responseXML = null; 57 | cXMLHttpRequest.prototype.status = 0; 58 | cXMLHttpRequest.prototype.statusText = ''; 59 | 60 | // Priority proposal 61 | cXMLHttpRequest.prototype.priority = "NORMAL"; 62 | 63 | // Instance-level Events Handlers 64 | cXMLHttpRequest.prototype.onreadystatechange = null; 65 | 66 | // Class-level Events Handlers 67 | cXMLHttpRequest.onreadystatechange = null; 68 | cXMLHttpRequest.onopen = null; 69 | cXMLHttpRequest.onsend = null; 70 | cXMLHttpRequest.onabort = null; 71 | 72 | // Public Methods 73 | cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) { 74 | // Delete headers, required when object is reused 75 | delete this._headers; 76 | 77 | // When bAsync parameter value is omitted, use true as default 78 | if (arguments.length < 3) { 79 | bAsync = true; 80 | } 81 | 82 | // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests 83 | this._async = bAsync; 84 | 85 | // Set the onreadystatechange handler 86 | var oRequest = this; 87 | var nState = this.readyState; 88 | var fOnUnload = null; 89 | 90 | // BUGFIX: IE - memory leak on page unload (inter-page leak) 91 | if (bIE && bAsync) { 92 | fOnUnload = function() { 93 | if (nState != cXMLHttpRequest.DONE) { 94 | fCleanTransport(oRequest); 95 | // Safe to abort here since onreadystatechange handler removed 96 | oRequest.abort(); 97 | } 98 | }; 99 | window.attachEvent("onunload", fOnUnload); 100 | } 101 | 102 | // Add method sniffer 103 | if (cXMLHttpRequest.onopen) { 104 | cXMLHttpRequest.onopen.apply(this, arguments); 105 | } 106 | 107 | if (arguments.length > 4) { 108 | this._object.open(sMethod, sUrl, bAsync, sUser, sPassword); 109 | } else if (arguments.length > 3) { 110 | this._object.open(sMethod, sUrl, bAsync, sUser); 111 | } else { 112 | this._object.open(sMethod, sUrl, bAsync); 113 | } 114 | 115 | this.readyState = cXMLHttpRequest.OPENED; 116 | fReadyStateChange(this); 117 | 118 | this._object.onreadystatechange = function() { 119 | if (bGecko && !bAsync) { 120 | return; 121 | } 122 | 123 | // Synchronize state 124 | oRequest.readyState = oRequest._object.readyState; 125 | fSynchronizeValues(oRequest); 126 | 127 | // BUGFIX: Firefox fires unnecessary DONE when aborting 128 | if (oRequest._aborted) { 129 | // Reset readyState to UNSENT 130 | oRequest.readyState = cXMLHttpRequest.UNSENT; 131 | 132 | // Return now 133 | return; 134 | } 135 | 136 | if (oRequest.readyState == cXMLHttpRequest.DONE) { 137 | // Free up queue 138 | delete oRequest._data; 139 | 140 | // Uncomment these lines for bAsync 141 | /** 142 | * if (bAsync) { 143 | * fQueue_remove(oRequest); 144 | * } 145 | */ 146 | 147 | fCleanTransport(oRequest); 148 | 149 | // Uncomment this block if you need a fix for IE cache 150 | /** 151 | * // BUGFIX: IE - cache issue 152 | * if (!oRequest._object.getResponseHeader("Date")) { 153 | * // Save object to cache 154 | * oRequest._cached = oRequest._object; 155 | * 156 | * // Instantiate a new transport object 157 | * cXMLHttpRequest.call(oRequest); 158 | * 159 | * // Re-send request 160 | * if (sUser) { 161 | * if (sPassword) { 162 | * oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword); 163 | * } else { 164 | * oRequest._object.open(sMethod, sUrl, bAsync); 165 | * } 166 | * 167 | * oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0)); 168 | * // Copy headers set 169 | * if (oRequest._headers) { 170 | * for (var sHeader in oRequest._headers) { 171 | * // Some frameworks prototype objects with functions 172 | * if (typeof oRequest._headers[sHeader] == "string") { 173 | * oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]); 174 | * } 175 | * } 176 | * } 177 | * oRequest._object.onreadystatechange = function() { 178 | * // Synchronize state 179 | * oRequest.readyState = oRequest._object.readyState; 180 | * 181 | * if (oRequest._aborted) { 182 | * // 183 | * oRequest.readyState = cXMLHttpRequest.UNSENT; 184 | * 185 | * // Return 186 | * return; 187 | * } 188 | * 189 | * if (oRequest.readyState == cXMLHttpRequest.DONE) { 190 | * // Clean Object 191 | * fCleanTransport(oRequest); 192 | * 193 | * // get cached request 194 | * if (oRequest.status == 304) { 195 | * oRequest._object = oRequest._cached; 196 | * } 197 | * 198 | * // 199 | * delete oRequest._cached; 200 | * 201 | * // 202 | * fSynchronizeValues(oRequest); 203 | * 204 | * // 205 | * fReadyStateChange(oRequest); 206 | * 207 | * // BUGFIX: IE - memory leak in interrupted 208 | * if (bIE && bAsync) { 209 | * window.detachEvent("onunload", fOnUnload); 210 | * } 211 | * 212 | * } 213 | * }; 214 | * oRequest._object.send(null); 215 | * 216 | * // Return now - wait until re-sent request is finished 217 | * return; 218 | * }; 219 | */ 220 | 221 | // BUGFIX: IE - memory leak in interrupted 222 | if (bIE && bAsync) { 223 | window.detachEvent("onunload", fOnUnload); 224 | } 225 | 226 | // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice 227 | if (nState != oRequest.readyState) { 228 | fReadyStateChange(oRequest); 229 | } 230 | 231 | nState = oRequest.readyState; 232 | } 233 | }; 234 | }; 235 | 236 | cXMLHttpRequest.prototype.send = function(vData) { 237 | // Add method sniffer 238 | if (cXMLHttpRequest.onsend) { 239 | cXMLHttpRequest.onsend.apply(this, arguments); 240 | } 241 | 242 | if (!arguments.length) { 243 | vData = null; 244 | } 245 | 246 | // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required 247 | // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent 248 | // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard) 249 | if (vData && vData.nodeType) { 250 | vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml; 251 | if (!this._headers["Content-Type"]) { 252 | this._object.setRequestHeader("Content-Type", "application/xml"); 253 | } 254 | } 255 | 256 | this._data = vData; 257 | 258 | /** 259 | * // Add to queue 260 | * if (this._async) { 261 | * fQueue_add(this); 262 | * } else { */ 263 | fXMLHttpRequest_send(this); 264 | /** 265 | * } 266 | */ 267 | }; 268 | 269 | cXMLHttpRequest.prototype.abort = function() { 270 | // Add method sniffer 271 | if (cXMLHttpRequest.onabort) { 272 | cXMLHttpRequest.onabort.apply(this, arguments); 273 | } 274 | 275 | // BUGFIX: Gecko - unnecessary DONE when aborting 276 | if (this.readyState > cXMLHttpRequest.UNSENT) { 277 | this._aborted = true; 278 | } 279 | 280 | this._object.abort(); 281 | 282 | // BUGFIX: IE - memory leak 283 | fCleanTransport(this); 284 | 285 | this.readyState = cXMLHttpRequest.UNSENT; 286 | 287 | delete this._data; 288 | 289 | /* if (this._async) { 290 | * fQueue_remove(this); 291 | * } 292 | */ 293 | }; 294 | 295 | cXMLHttpRequest.prototype.getAllResponseHeaders = function() { 296 | return this._object.getAllResponseHeaders(); 297 | }; 298 | 299 | cXMLHttpRequest.prototype.getResponseHeader = function(sName) { 300 | return this._object.getResponseHeader(sName); 301 | }; 302 | 303 | cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) { 304 | // BUGFIX: IE - cache issue 305 | if (!this._headers) { 306 | this._headers = {}; 307 | } 308 | 309 | this._headers[sName] = sValue; 310 | 311 | return this._object.setRequestHeader(sName, sValue); 312 | }; 313 | 314 | // EventTarget interface implementation 315 | cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) { 316 | for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) { 317 | if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) { 318 | return; 319 | } 320 | } 321 | 322 | // Add listener 323 | this._listeners.push([sName, fHandler, bUseCapture]); 324 | }; 325 | 326 | cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) { 327 | for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) { 328 | if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) { 329 | break; 330 | } 331 | } 332 | 333 | // Remove listener 334 | if (oListener) { 335 | this._listeners.splice(nIndex, 1); 336 | } 337 | }; 338 | 339 | cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) { 340 | var oEventPseudo = { 341 | 'type': oEvent.type, 342 | 'target': this, 343 | 'currentTarget': this, 344 | 'eventPhase': 2, 345 | 'bubbles': oEvent.bubbles, 346 | 'cancelable': oEvent.cancelable, 347 | 'timeStamp': oEvent.timeStamp, 348 | 'stopPropagation': function() {}, // There is no flow 349 | 'preventDefault': function() {}, // There is no default action 350 | 'initEvent': function() {} // Original event object should be initialized 351 | }; 352 | 353 | // Execute onreadystatechange 354 | if (oEventPseudo.type == "readystatechange" && this.onreadystatechange) { 355 | (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]); 356 | } 357 | 358 | 359 | // Execute listeners 360 | for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) { 361 | if (oListener[0] == oEventPseudo.type && !oListener[2]) { 362 | (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]); 363 | } 364 | } 365 | 366 | }; 367 | 368 | // 369 | cXMLHttpRequest.prototype.toString = function() { 370 | return '[' + "object" + ' ' + "XMLHttpRequest" + ']'; 371 | }; 372 | 373 | cXMLHttpRequest.toString = function() { 374 | return '[' + "XMLHttpRequest" + ']'; 375 | }; 376 | 377 | /** 378 | * // Queue manager 379 | * var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]}, 380 | * aQueueRunning = []; 381 | * function fQueue_add(oRequest) { 382 | * oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest); 383 | * // 384 | * setTimeout(fQueue_process); 385 | * }; 386 | * 387 | * function fQueue_remove(oRequest) { 388 | * for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++) 389 | * if (bFound) { 390 | * aQueueRunning[nIndex - 1] = aQueueRunning[nIndex]; 391 | * } else { 392 | * if (aQueueRunning[nIndex] == oRequest) { 393 | * bFound = true; 394 | * } 395 | * } 396 | * 397 | * if (bFound) { 398 | * aQueueRunning.length--; 399 | * } 400 | * 401 | * 402 | * // 403 | * setTimeout(fQueue_process); 404 | * }; 405 | * 406 | * function fQueue_process() { 407 | * if (aQueueRunning.length < 6) { 408 | * for (var sPriority in oQueuePending) { 409 | * if (oQueuePending[sPriority].length) { 410 | * var oRequest = oQueuePending[sPriority][0]; 411 | * oQueuePending[sPriority] = oQueuePending[sPriority].slice(1); 412 | * // 413 | * aQueueRunning.push(oRequest); 414 | * // Send request 415 | * fXMLHttpRequest_send(oRequest); 416 | * break; 417 | * } 418 | * } 419 | * } 420 | * }; 421 | */ 422 | 423 | // Helper function 424 | function fXMLHttpRequest_send(oRequest) { 425 | oRequest._object.send(oRequest._data); 426 | 427 | // BUGFIX: Gecko - missing readystatechange calls in synchronous requests 428 | if (bGecko && !oRequest._async) { 429 | oRequest.readyState = cXMLHttpRequest.OPENED; 430 | 431 | // Synchronize state 432 | fSynchronizeValues(oRequest); 433 | 434 | // Simulate missing states 435 | while (oRequest.readyState < cXMLHttpRequest.DONE) { 436 | oRequest.readyState++; 437 | fReadyStateChange(oRequest); 438 | // Check if we are aborted 439 | if (oRequest._aborted) { 440 | return; 441 | } 442 | } 443 | } 444 | } 445 | 446 | function fReadyStateChange(oRequest) { 447 | // Sniffing code 448 | if (cXMLHttpRequest.onreadystatechange){ 449 | cXMLHttpRequest.onreadystatechange.apply(oRequest); 450 | } 451 | 452 | 453 | // Fake event 454 | oRequest.dispatchEvent({ 455 | 'type': "readystatechange", 456 | 'bubbles': false, 457 | 'cancelable': false, 458 | 'timeStamp': new Date + 0 459 | }); 460 | } 461 | 462 | function fGetDocument(oRequest) { 463 | var oDocument = oRequest.responseXML; 464 | var sResponse = oRequest.responseText; 465 | // Try parsing responseText 466 | if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) { 467 | oDocument = new window.ActiveXObject("Microsoft.XMLDOM"); 468 | oDocument.async = false; 469 | oDocument.validateOnParse = false; 470 | oDocument.loadXML(sResponse); 471 | } 472 | 473 | // Check if there is no error in document 474 | if (oDocument){ 475 | if ((bIE && oDocument.parseError !== 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror")) { 476 | return null; 477 | } 478 | } 479 | return oDocument; 480 | } 481 | 482 | function fSynchronizeValues(oRequest) { 483 | try { oRequest.responseText = oRequest._object.responseText; } catch (e) {} 484 | try { oRequest.responseXML = fGetDocument(oRequest._object); } catch (e) {} 485 | try { oRequest.status = oRequest._object.status; } catch (e) {} 486 | try { oRequest.statusText = oRequest._object.statusText; } catch (e) {} 487 | } 488 | 489 | function fCleanTransport(oRequest) { 490 | // BUGFIX: IE - memory leak (on-page leak) 491 | oRequest._object.onreadystatechange = new window.Function; 492 | } 493 | 494 | // Internet Explorer 5.0 (missing apply) 495 | if (!window.Function.prototype.apply) { 496 | window.Function.prototype.apply = function(oRequest, oArguments) { 497 | if (!oArguments) { 498 | oArguments = []; 499 | } 500 | oRequest.__func = this; 501 | oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]); 502 | delete oRequest.__func; 503 | }; 504 | } 505 | 506 | // Register new object with window 507 | window.XMLHttpRequest = cXMLHttpRequest; 508 | 509 | })(); 510 | -------------------------------------------------------------------------------- /test/3rdparty/esmorph.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2012 Ariya Hidayat 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | /*jslint node:true browser:true */ 26 | /*global esmorph:true,esprima:true */ 27 | 28 | (function (exports) { 29 | 'use strict'; 30 | 31 | var Syntax = { 32 | AssignmentExpression: 'AssignmentExpression', 33 | ArrayExpression: 'ArrayExpression', 34 | BlockStatement: 'BlockStatement', 35 | BinaryExpression: 'BinaryExpression', 36 | BreakStatement: 'BreakStatement', 37 | CallExpression: 'CallExpression', 38 | CatchClause: 'CatchClause', 39 | ConditionalExpression: 'ConditionalExpression', 40 | ContinueStatement: 'ContinueStatement', 41 | DoWhileStatement: 'DoWhileStatement', 42 | DebuggerStatement: 'DebuggerStatement', 43 | EmptyStatement: 'EmptyStatement', 44 | ExpressionStatement: 'ExpressionStatement', 45 | ForStatement: 'ForStatement', 46 | ForInStatement: 'ForInStatement', 47 | FunctionDeclaration: 'FunctionDeclaration', 48 | FunctionExpression: 'FunctionExpression', 49 | Identifier: 'Identifier', 50 | IfStatement: 'IfStatement', 51 | Literal: 'Literal', 52 | LabeledStatement: 'LabeledStatement', 53 | LogicalExpression: 'LogicalExpression', 54 | MemberExpression: 'MemberExpression', 55 | NewExpression: 'NewExpression', 56 | ObjectExpression: 'ObjectExpression', 57 | Program: 'Program', 58 | Property: 'Property', 59 | ReturnStatement: 'ReturnStatement', 60 | SequenceExpression: 'SequenceExpression', 61 | SwitchStatement: 'SwitchStatement', 62 | SwitchCase: 'SwitchCase', 63 | ThisExpression: 'ThisExpression', 64 | ThrowStatement: 'ThrowStatement', 65 | TryStatement: 'TryStatement', 66 | UnaryExpression: 'UnaryExpression', 67 | UpdateExpression: 'UpdateExpression', 68 | VariableDeclaration: 'VariableDeclaration', 69 | VariableDeclarator: 'VariableDeclarator', 70 | WhileStatement: 'WhileStatement', 71 | WithStatement: 'WithStatement' 72 | }; 73 | 74 | // Executes visitor on the object and its children (recursively). 75 | 76 | function traverse(object, visitor, master) { 77 | var key, child, parent, path; 78 | 79 | parent = (typeof master === 'undefined') ? [] : master; 80 | 81 | if (visitor.call(null, object, parent) === false) { 82 | return; 83 | } 84 | for (key in object) { 85 | if (object.hasOwnProperty(key)) { 86 | child = object[key]; 87 | path = [ object ]; 88 | path.push(parent); 89 | if (typeof child === 'object' && child !== null) { 90 | traverse(child, visitor, path); 91 | } 92 | } 93 | } 94 | } 95 | 96 | // Insert a prolog in the body of every function. 97 | // It will be in the form of a function call: 98 | // 99 | // traceName(object); 100 | // 101 | // where the object contains the following properties: 102 | // 103 | // 'name' holds the name of the function 104 | // 'lineNumber' holds the starting line number of the function block 105 | // 'range' contains the index-based range of the function 106 | // 107 | // The name of the function represents the associated reference for 108 | // the function (deduced on a best-effort basis if it is not 109 | // a function declaration). 110 | // 111 | // If traceName is a function instead of a string, it will be invoked and 112 | // the result will be used as the entire prolog. The arguments for the 113 | // invocation are the function name, range, and location info. 114 | 115 | function traceFunctionEntrance(traceName) { 116 | 117 | return function (code) { 118 | var tree, 119 | functionList, 120 | param, 121 | signature, 122 | pos, 123 | i; 124 | 125 | 126 | tree = esprima.parse(code, { range: true, loc: true }); 127 | 128 | functionList = []; 129 | traverse(tree, function (node, path) { 130 | var parent; 131 | if (node.type === Syntax.FunctionDeclaration) { 132 | functionList.push({ 133 | name: node.id.name, 134 | range: node.range, 135 | loc: node.loc, 136 | blockStart: node.body.range[0] 137 | }); 138 | } else if (node.type === Syntax.FunctionExpression) { 139 | parent = path[0]; 140 | if (parent.type === Syntax.AssignmentExpression) { 141 | if (typeof parent.left.range !== 'undefined') { 142 | functionList.push({ 143 | name: code.slice(parent.left.range[0], 144 | parent.left.range[1]).replace(/"/g, '\\"'), 145 | range: node.range, 146 | loc: node.loc, 147 | blockStart: node.body.range[0] 148 | }); 149 | } 150 | } else if (parent.type === Syntax.VariableDeclarator) { 151 | functionList.push({ 152 | name: parent.id.name, 153 | range: node.range, 154 | loc: node.loc, 155 | blockStart: node.body.range[0] 156 | }); 157 | } else if (parent.type === Syntax.CallExpression) { 158 | functionList.push({ 159 | name: parent.id ? parent.id.name : '[Anonymous]', 160 | range: node.range, 161 | loc: node.loc, 162 | blockStart: node.body.range[0] 163 | }); 164 | } else if (typeof parent.length === 'number') { 165 | functionList.push({ 166 | name: parent.id ? parent.id.name : '[Anonymous]', 167 | range: node.range, 168 | loc: node.loc, 169 | blockStart: node.body.range[0] 170 | }); 171 | } else if (typeof parent.key !== 'undefined') { 172 | if (parent.key.type === 'Identifier') { 173 | if (parent.value === node && parent.key.name) { 174 | functionList.push({ 175 | name: parent.key.name, 176 | range: node.range, 177 | loc: node.loc, 178 | blockStart: node.body.range[0] 179 | }); 180 | } 181 | } 182 | } 183 | } 184 | }); 185 | 186 | // Insert the instrumentation code from the last entry. 187 | // This is to ensure that the range for each entry remains valid) 188 | // (it won't shift due to some new inserting string before the range). 189 | for (i = functionList.length - 1; i >= 0; i -= 1) { 190 | param = { 191 | name: functionList[i].name, 192 | range: functionList[i].range, 193 | loc: functionList[i].loc 194 | }; 195 | if (typeof traceName === 'function') { 196 | signature = traceName.call(null, param); 197 | } else { 198 | signature = traceName + '({ '; 199 | signature += 'name: \'' + functionList[i].name + '\', '; 200 | if (typeof functionList[i].loc !== 'undefined') { 201 | signature += 'lineNumber: ' + functionList[i].loc.start.line + ', '; 202 | } 203 | signature += 'range: [' + functionList[i].range[0] + ', ' + 204 | functionList[i].range[1] + '] '; 205 | signature += '});'; 206 | } 207 | pos = functionList[i].blockStart + 1; 208 | code = code.slice(0, pos) + '\n' + signature + code.slice(pos, code.length); 209 | } 210 | 211 | return code; 212 | }; 213 | } 214 | 215 | function modify(code, modifiers) { 216 | var i; 217 | 218 | if (Object.prototype.toString.call(modifiers) === '[object Array]') { 219 | for (i = 0; i < modifiers.length; i += 1) { 220 | code = modifiers[i].call(null, code); 221 | } 222 | } else if (typeof modifiers === 'function') { 223 | code = modifiers.call(null, code); 224 | } else { 225 | throw new Error('Wrong use of esmorph.modify() function'); 226 | } 227 | 228 | return code; 229 | } 230 | 231 | // Sync with package.json. 232 | exports.version = '0.0.0-dev'; 233 | 234 | exports.modify = modify; 235 | 236 | exports.Tracer = { 237 | FunctionEntrance: traceFunctionEntrance 238 | }; 239 | 240 | }(typeof exports === 'undefined' ? (esmorph = {}) : exports)); 241 | -------------------------------------------------------------------------------- /test/3rdparty/harmonizr.js: -------------------------------------------------------------------------------- 1 | var harmonizr = function() {var parse = esprima.parse, Syntax = esprima.Syntax; 2 | var Modifier = modifier.Modifier; 3 | 4 | var Modifier = Modifier; 5 | 6 | function harmonize(src, options) { 7 | options = options || {}; 8 | var modifier = new Modifier(src); 9 | var stages = [ 10 | {name: 'shorthand properties', fn: processShorthands}, 11 | {name: 'shorthand methods', fn: processMethods}, 12 | {name: 'arrow functions', fn: processArrowFunctions}, 13 | {name: 'classes', fn: processClasses}, 14 | {name: 'destructuring assignment', fn: processDestructuringAssignments}, 15 | {name: 'modules', fn: processModules} 16 | ]; 17 | // just a bogus fail stage to get 100% test coverage 18 | if (options.fail) { 19 | stages.unshift({name: 'fail', fn: function(modifier) { 20 | modifier.insert({line: 1, column: 0}, '}'); 21 | }}); 22 | } 23 | var before; 24 | for (var i = 0; i < stages.length; i++) { 25 | if (i) { 26 | try { 27 | modifier.refresh(); 28 | } catch (e) { 29 | var err = new Error('Stage `' + stages[i - 1].name + '` created unparsable code: ' + e.message); 30 | // populate `actual` and `expected` so mocha shows us a diff 31 | err.actual = before; 32 | err.expected = modifier.finish(); 33 | throw err; 34 | } 35 | } 36 | var stage = stages[i]; 37 | before = modifier.finish(); 38 | stage.fn(modifier, options, moduleStyles[options.style]); 39 | } 40 | return modifier.finish(); 41 | } 42 | 43 | function processShorthands(modifier, options) { 44 | var ast = modifier.ast; 45 | var lines = modifier.lines; 46 | 47 | var shorthands = []; 48 | 49 | traverse(ast, function(node) { 50 | if (node.type === Syntax.Property && node.shorthand) { 51 | shorthands.push(node); 52 | } 53 | }); 54 | 55 | for (var i = shorthands.length - 1; i >= 0; i--) { 56 | var prop = shorthands[i]; 57 | var line = prop.value.loc.end.line - 1; 58 | var col = prop.value.loc.end.column; 59 | 60 | lines[line] = splice( 61 | lines[line], 62 | col, 63 | 0, // Delete nothing. 64 | ': ' + prop.value.name); 65 | } 66 | } 67 | 68 | function processMethods(modifier, options) { 69 | var ast = modifier.ast; 70 | var lines = modifier.lines; 71 | 72 | var methods = []; 73 | 74 | traverse(ast, function(node) { 75 | if (node.type === Syntax.Property && node.method) { 76 | methods.push(node); 77 | } 78 | }); 79 | 80 | for (var i = methods.length - 1; i >= 0; i--) { 81 | var prop = methods[i]; 82 | var line = prop.key.loc.end.line - 1; 83 | var col = prop.key.loc.end.column; 84 | 85 | if (prop.value.body.type !== Syntax.BlockStatement) { 86 | // It's a concise method definition. 87 | 88 | // Add the end of the function body first. 89 | var bodyEndLine = prop.value.loc.end.line - 1; 90 | var bodyEndCol = prop.value.loc.end.column; 91 | lines[bodyEndLine] = splice( 92 | lines[bodyEndLine], 93 | bodyEndCol, 94 | 0, 95 | '; }'); 96 | 97 | // Now add the beginning. 98 | var prefix = '{ '; 99 | if (prop.value.body.type !== Syntax.AssignmentExpression) { 100 | prefix += 'return '; 101 | } 102 | var bodyStartLine = prop.value.loc.start.line - 1; 103 | var bodyStartCol = prop.value.loc.start.column; 104 | lines[bodyStartLine] = splice( 105 | lines[bodyStartLine], 106 | bodyStartCol, 107 | 0, 108 | prefix); 109 | } 110 | 111 | lines[line] = splice( 112 | lines[line], 113 | col, 114 | 0, // Delete nothing. 115 | ': function'); 116 | } 117 | } 118 | 119 | function processArrowFunctions(modifier, options) { 120 | var ast = modifier.ast; 121 | var lines = modifier.lines; 122 | 123 | var arrowFunctions = []; 124 | 125 | traverse(ast, function(node) { 126 | if (node.type === Syntax.ArrowFunctionExpression) { 127 | arrowFunctions.push(node); 128 | } 129 | }); 130 | 131 | arrowFunctions.reverse(); 132 | 133 | arrowFunctions.forEach(function(node) { 134 | var bodyEndLine = node.body.loc.end.line - 1; 135 | var bodyEndCol = node.body.loc.end.column; 136 | 137 | var needsBind = containsThisExpression(node); 138 | 139 | if (needsBind) { 140 | // Bind it to the lexical this. 141 | lines[bodyEndLine] = splice( 142 | lines[bodyEndLine], 143 | bodyEndCol, 144 | 0, // Delete nothing. 145 | '.bind(this)'); 146 | } 147 | 148 | var needsCurly = lines[bodyEndLine].charAt(bodyEndCol - 1) !== '}'; 149 | 150 | if (needsCurly) { 151 | // Insert the closing of the function. 152 | lines[bodyEndLine] = splice( 153 | lines[bodyEndLine], 154 | bodyEndCol, 155 | 0, // Delete nothing. 156 | '; }'); 157 | } 158 | }); 159 | 160 | arrowFunctions.forEach(function(node) { 161 | var startLine = node.loc.start.line - 1; 162 | var startCol = node.loc.start.column; 163 | 164 | var lastParam, paramsEndLine, paramsEndCol; 165 | 166 | if (node.params.length) { 167 | lastParam = node.params[node.params.length - 1]; 168 | paramsEndLine = lastParam.loc.end.line - 1; 169 | paramsEndCol = lastParam.loc.end.column; 170 | } 171 | 172 | var bodyStartLine = node.body.loc.start.line - 1; 173 | var bodyStartCol = node.body.loc.start.column; 174 | 175 | var needsCurly = lines[bodyStartLine].charAt(bodyStartCol) !== '{'; 176 | 177 | if (needsCurly) { 178 | // Close the params and start the body. 179 | lines[bodyStartLine] = splice( 180 | lines[bodyStartLine], 181 | bodyStartCol, 182 | 0, // Delete nothing. 183 | '{ return '); 184 | } 185 | 186 | // Close the params and start the body. 187 | lines[bodyStartLine] = splice( 188 | lines[bodyStartLine], 189 | bodyStartCol, 190 | 0, // Delete nothing. 191 | ') '); 192 | 193 | if (node.params.length) { 194 | // Delete the => in between the params and the body. 195 | lines[paramsEndLine] = splice( 196 | lines[paramsEndLine], 197 | paramsEndCol, 198 | bodyStartCol - paramsEndCol, 199 | ''); 200 | 201 | // In case the => is not on the same line as the params or body. 202 | var n = paramsEndLine; 203 | while (n++ < bodyStartLine) { 204 | if (n < bodyStartLine) { 205 | lines[n] = ''; 206 | } else { 207 | lines[n] = splice( 208 | lines[n], 209 | 0, 210 | bodyStartCol, 211 | ''); 212 | } 213 | } 214 | 215 | // Delete the last ). 216 | var endsWithParen = lines[paramsEndLine].charAt(paramsEndCol - 1) === ')'; 217 | if (endsWithParen) { 218 | lines[paramsEndLine] = splice( 219 | lines[paramsEndLine], 220 | paramsEndCol - 1, 221 | 1, 222 | ''); 223 | } 224 | } else { 225 | // There are no params, so delete everything up to the body. 226 | lines[startLine] = splice( 227 | lines[startLine], 228 | startCol + 1, 229 | bodyStartCol - startCol - 1, 230 | ''); 231 | } 232 | 233 | // Delete the first (. 234 | if (lines[startLine].charAt(startCol) === '(') { 235 | lines[startLine] = splice( 236 | lines[startLine], 237 | startCol, 238 | 1, 239 | ''); 240 | } 241 | 242 | // Insert the opening of the function. 243 | lines[startLine] = splice( 244 | lines[startLine], 245 | startCol, 246 | 0, // Delete nothing. 247 | 'function('); 248 | }); 249 | 250 | function containsThisExpression(node) { 251 | var result = false; 252 | traverse(node, function(innerNode) { 253 | if (innerNode.type === Syntax.ThisExpression) { 254 | result = true; 255 | } else if (innerNode !== node && innerNode.type === Syntax.ArrowFunctionExpression) { 256 | return false; 257 | } 258 | }); 259 | return result; 260 | } 261 | } 262 | 263 | function processClasses(modifier, options) { 264 | var ast, classes; 265 | 266 | do { 267 | ast = modifier.ast; 268 | classes = findClasses(); 269 | 270 | if (!classes.length) { 271 | return; 272 | } 273 | 274 | var node = classes[0]; 275 | 276 | var name = node.id && node.id.name || '__klass'; 277 | var methods = []; 278 | traverse(node, findMethods); 279 | methods.reverse(); 280 | 281 | // Replace the trailing } with a return for the IIFE pattern 282 | modifier.replace({line: node.body.loc.end.line, column: node.body.loc.end.column - 1}, node.loc.end, 283 | '; return ' + name + ';})()' + (node.type === Syntax.ClassDeclaration ? ';' : '')); 284 | 285 | methods.forEach(processMethod); 286 | 287 | // In case we have no constructor, insert an empty function 288 | if (!hasConstructor()) { 289 | modifier.insert({line: node.body.loc.start.line, column: node.body.loc.start.column + 1}, 290 | 'function ' + name + '() {};'); 291 | } 292 | 293 | if (node.superClass) { 294 | // remove opening { 295 | modifier.remove(node.body.loc.start, 1); 296 | 297 | // Capture super expression in a variable 298 | // and use an Object.create to create the prototype of the new class 299 | modifier.insert(node.superClass.loc.end, 300 | 'var ' + name + '__prototype = (typeof ' + name + '__super !== "function" ? ' + 301 | name + '__super : ' + name + '__super.prototype);' + 302 | name + '.prototype = Object.create(' + name + '__prototype);'); 303 | modifier.insert(node.superClass.loc.end, 304 | ';'); 305 | modifier.insert(node.superClass.loc.start, 306 | 'var ' + name + '__super = '); 307 | 308 | // Replace start until the super class with IIFE pattern 309 | modifier.replace(node.loc.start, node.superClass.loc.start, 310 | (node.type === Syntax.ClassDeclaration ? 'var ' + name + ' = ' : '') + '(function () {'); 311 | } else { 312 | // Replace start of the ClassDeclaration with IIFE pattern 313 | modifier.replace(node.loc.start, node.body.loc.start, 314 | (node.type === Syntax.ClassDeclaration ? 'var ' + name + ' = ' : '') + '(function () '); 315 | } 316 | 317 | if (classes.length <= 1) { 318 | return; 319 | } 320 | modifier.refresh(); 321 | } while (true); 322 | 323 | function isClass(node) { 324 | return node.type === Syntax.ClassDeclaration || node.type === Syntax.ClassExpression; 325 | } 326 | function findClasses() { 327 | var classes = []; 328 | traverse(ast, function(node) { 329 | if (isClass(node)) { 330 | classes.push(node); 331 | } 332 | }); 333 | return classes; 334 | } 335 | 336 | function findMethods(innerNode) { 337 | if (innerNode.type === Syntax.MethodDefinition) { 338 | methods.push(innerNode); 339 | } else if (innerNode !== node && isClass(innerNode)) { 340 | return false; 341 | } 342 | } 343 | 344 | function processMethod(method) { 345 | var supers = []; 346 | // collect all `super` Identifiers of calls and `super.X` MemberExpressions 347 | traverse(method, function(innerNode) { 348 | if ((innerNode.type === Syntax.CallExpression && 349 | innerNode.callee.type === Syntax.Identifier && 350 | innerNode.callee.name === 'super') || 351 | (innerNode.type === Syntax.MemberExpression && 352 | innerNode.object.type === Syntax.Identifier && 353 | innerNode.object.name === 'super')) { 354 | supers.push(innerNode); 355 | } else if (innerNode !== node && isClass(innerNode)) { 356 | return false; 357 | } 358 | }); 359 | supers.reverse(); 360 | supers.forEach(function(superItem) { 361 | if (superItem.type === Syntax.CallExpression) { 362 | // super(X) -> X__super.bind(this)(X) 363 | modifier.replace(superItem.callee.loc.start, superItem.callee.loc.end, 364 | name + '__super.bind(this)'); 365 | } else { 366 | // super.method -> X__prototype.method.bind(this) 367 | modifier.insert(superItem.property.loc.end, 368 | '.bind(this)'); 369 | modifier.replace(superItem.object.loc.start, superItem.object.loc.end, 370 | name + '__prototype'); 371 | } 372 | }); 373 | 374 | if (method.key.name === 'constructor') { 375 | modifier.replace(method.key.loc.start, method.key.loc.end, 376 | 'function ' + name); 377 | } else { 378 | var prop = method.key.name; 379 | if (method.kind) { 380 | var otherkind = method.kind === 'set' ? 'get' : 'set'; 381 | var protoprop = name + '.prototype, "' + prop + '"'; 382 | modifier.insert(method.loc.end, 383 | '})'); 384 | modifier.replace(method.loc.start, method.key.loc.end, 385 | 'var __old_' + prop + ' = ' + 386 | 'Object.getOwnPropertyDescriptor(' + protoprop + '); ' + 387 | 'Object.defineProperty(' + protoprop + ', ' + 388 | '{configurable: true, ' + otherkind + ': __old_' + prop + 389 | ' && __old_' + prop + '.' + otherkind + ', ' + 390 | method.kind + ': function'); 391 | } else { 392 | modifier.replace(method.key.loc.start, method.key.loc.end, 393 | name + '.prototype.' + prop + ' = function'); 394 | } 395 | } 396 | } 397 | 398 | function hasConstructor() { 399 | return methods.some(function(method) { 400 | return method.key.name === 'constructor'; 401 | }); 402 | } 403 | 404 | } 405 | 406 | function processDestructuringAssignments(modifier, options) { 407 | var ast = modifier.ast; 408 | var lines = modifier.lines; 409 | 410 | var nodes = []; 411 | 412 | traverse(ast, function(node) { 413 | if (node.type === Syntax.AssignmentExpression && node.left.type === Syntax.ArrayPattern) { 414 | nodes.push(node); 415 | } 416 | }); 417 | 418 | nodes.reverse(); 419 | 420 | nodes.forEach(function(node) { 421 | var firstId = node.left.elements[0].name; 422 | 423 | var endLine = node.loc.end.line - 1; 424 | var endCol = node.loc.end.column; 425 | lines[endLine] = splice( 426 | lines[endLine], 427 | endCol, 428 | 0, // Delete nothing. 429 | ', ' + getAssignments()); 430 | 431 | var patternStartLine = node.left.loc.start.line - 1; 432 | var patternStartCol = node.left.loc.start.column; 433 | var patternEndCol = node.left.loc.end.column; 434 | lines[patternStartLine] = splice( 435 | lines[patternStartLine], 436 | patternStartCol, 437 | patternEndCol - patternStartCol, 438 | firstId); 439 | 440 | function getAssignments() { 441 | var all = []; 442 | for (var i = 1; i < node.left.elements.length; i++) { 443 | var id = node.left.elements[i].name; 444 | all.push(getAssignment(id, i)); 445 | } 446 | all.push(getAssignment(firstId, 0)); 447 | return all.join(', '); 448 | } 449 | 450 | function getAssignment(id, index) { 451 | return id + ' = ' + firstId + '[' + index + ']'; 452 | } 453 | }); 454 | } 455 | 456 | function processModules(modifier, options, style) { 457 | if (!style) { 458 | return; 459 | } 460 | 461 | if (options.module) { 462 | modifier.insert(modifier.ast.loc.start, 'module ' + options.module + '{'); 463 | modifier.lines.push('}'); 464 | modifier.refresh(); 465 | } 466 | 467 | var ast = modifier.ast; 468 | var lines = modifier.lines; 469 | 470 | var modules = []; 471 | 472 | traverse(ast, function(node) { 473 | if (node.type === Syntax.ModuleDeclaration) { 474 | modules.push(node); 475 | return false; 476 | } 477 | }); 478 | 479 | modules.forEach(function(mod) { 480 | 481 | options.indent = detectIndent(mod, lines); 482 | 483 | var imps = []; 484 | 485 | traverse(mod, function(node) { 486 | if (node.type === Syntax.ModuleDeclaration) { 487 | if (node.id && node.from) { 488 | imps.push(node); 489 | } else if (node !== mod) { 490 | return false; 491 | } 492 | } else if (node.type === Syntax.ImportDeclaration && 493 | node.specifiers[0].type !== Syntax.Glob) { 494 | imps.push(node); 495 | } 496 | }); 497 | 498 | var moduleStartLine = mod.loc.start.line - 1; 499 | var moduleStartColumn = mod.loc.start.column; 500 | var moduleEndLine = mod.loc.end.line - 1; 501 | var moduleEndColumn = mod.loc.end.column; 502 | var bodyStartColumn = mod.body.loc.start.column; 503 | var bodyEndColumn = mod.body.loc.end.column; 504 | 505 | // Modify the end first in case it's on the same line as the start. 506 | lines[moduleEndLine] = splice( 507 | lines[moduleEndLine], 508 | bodyEndColumn - 1, 509 | 1, // Delete the closing brace. 510 | style.endModule(mod, options)); 511 | 512 | imps.forEach(function(imp) { 513 | var importStartLine = imp.loc.start.line - 1; 514 | var importStartColumn = imp.loc.start.column; 515 | var importEndColumn = imp.loc.end.column; 516 | lines[importStartLine] = splice( 517 | lines[importStartLine], 518 | importStartColumn, 519 | importEndColumn - importStartColumn, 520 | imp.type === Syntax.ModuleDeclaration ? 521 | style.importModuleDeclaration(mod, imp, options) : 522 | style.importDeclaration(mod, imp, options)); 523 | }); 524 | 525 | var exps = []; 526 | 527 | traverse(mod, function(node) { 528 | if (node.type === Syntax.ModuleDeclaration && node !== mod) { 529 | return false; 530 | } else if (node.type === Syntax.ExportDeclaration) { 531 | exps.push(node); 532 | } 533 | }); 534 | 535 | exps.forEach(function(exp) { 536 | var exportStartLine = exp.loc.start.line - 1; 537 | var exportStartColumn = exp.loc.start.column; 538 | var declarationStartColumn = exp.declaration.loc.start.column; 539 | lines[exportStartLine] = splice( 540 | lines[exportStartLine], 541 | exportStartColumn, 542 | declarationStartColumn - exportStartColumn, // Delete the export keyword. 543 | ''); // Nothing to insert. 544 | }); 545 | 546 | if (exps.length) { 547 | lines[moduleEndLine] = splice( 548 | lines[moduleEndLine], 549 | moduleEndColumn - 1, 550 | 0, 551 | style.exports(mod, exps, options)); 552 | } 553 | 554 | lines[moduleStartLine] = splice( 555 | lines[moduleStartLine], 556 | moduleStartColumn, 557 | bodyStartColumn - moduleStartColumn + 1, // Delete from start of module to opening brace. 558 | style.startModule(mod, imps, options)); 559 | }); 560 | } 561 | 562 | var moduleStyles = { 563 | amd: { 564 | startModule: function(mod, imps, options) { 565 | var header = 'define('; 566 | if (imps.length) { 567 | header += '[\'' + imps.map(function(imp) { return modulePath(importFrom(imp), options); }).join('\', \'') + '\'], '; 568 | } 569 | header += 'function('; 570 | if (imps.length) { 571 | header += imps.map(function(imp) { return importFrom(imp); }).join(', '); 572 | } 573 | header += ') {'; 574 | return header; 575 | }, 576 | importModuleDeclaration: function(mod, imp, options) { 577 | return 'var ' + imp.id.name + ' = ' + importFrom(imp) + ';'; 578 | }, 579 | importDeclaration: function(mod, imp, options) { 580 | return 'var ' + imp.specifiers.map(function(spec) { 581 | var id = spec.type === Syntax.Identifier ? spec.name : spec.id.name; 582 | var from = spec.from ? joinPath(spec.from) : id; 583 | return id + ' = ' + importFrom(imp) + '.' + from; 584 | }).join(', ') + ';'; 585 | }, 586 | exports: function(mod, exps, options) { 587 | var indent1 = options.module ? '' : options.indent; 588 | var indent2 = options.indent; 589 | var ret = '\n' + indent1; 590 | ret += 'return {'; 591 | if (exps.length) { 592 | ret += '\n'; 593 | ret += exps.map(function(exp) { 594 | var id = exportName(exp); 595 | return indent1 + indent2 + id + ': ' + id; 596 | }).join(',\n'); 597 | ret += '\n'; 598 | ret += indent1; 599 | } 600 | ret += '};\n'; 601 | return ret; 602 | }, 603 | endModule: function(mod, options) { 604 | return '});'; 605 | } 606 | }, 607 | 608 | node: { 609 | startModule: function(mod, imps, options) { 610 | return ''; 611 | }, 612 | importModuleDeclaration: function(mod, imp, options) { 613 | return 'var ' + imp.id.name + ' = require(\'' + importFrom(imp) + '\');'; 614 | }, 615 | importDeclaration: function(mod, imp, options) { 616 | return 'var ' + importFrom(imp) + 617 | ' = require(\'' + modulePath(importFrom(imp), options) + '\'), ' + 618 | imp.specifiers.map(function(spec) { 619 | var id = spec.type === Syntax.Identifier ? spec.name : spec.id.name; 620 | var from = spec.from ? joinPath(spec.from) : id; 621 | return id + ' = ' + importFrom(imp) + '.' + from; 622 | }).join(', ') + ';'; 623 | }, 624 | exports: function(mod, exps, options) { 625 | var indent1 = options.module ? '' : options.indent; 626 | var indent2 = options.indent; 627 | var returns = '\n' + indent1 + 'module.exports = {'; 628 | returns += '\n' + exps.map(function(exp) { 629 | var id = exportName(exp); 630 | return indent1 + indent2 + id + ': ' + id; 631 | }).join(',\n'); 632 | returns += '\n' + indent1; 633 | returns += '};\n'; 634 | return returns; 635 | }, 636 | endModule: function(mod, options) { 637 | return ''; 638 | } 639 | }, 640 | 641 | revealing: { 642 | startModule: function(mod, imps, options) { 643 | return 'var ' + mod.id.name + ' = function() {'; 644 | }, 645 | importModuleDeclaration: function(mod, imp, options) { 646 | return 'var ' + imp.id.name + ' = ' + importFrom(imp) + ';'; 647 | }, 648 | importDeclaration: function(mod, imp, options) { 649 | return 'var ' + imp.specifiers.map(function(spec) { 650 | var id = spec.type === Syntax.Identifier ? spec.name : spec.id.name; 651 | var from = spec.from ? joinPath(spec.from) : id; 652 | return id + ' = ' + importFrom(imp) + '.' + from; 653 | }).join(', ') + ';'; 654 | }, 655 | exports: function(mod, exps, options) { 656 | var indent1 = options.module ? '' : options.indent; 657 | var indent2 = options.indent; 658 | var returns = '\n' + indent1 + 'return {'; 659 | if (exps.length) { 660 | returns += '\n' + exps.map(function(exp) { 661 | var id = exportName(exp); 662 | return indent1 + indent2 + id + ': ' + id; 663 | }).join(',\n'); 664 | returns += '\n' + indent1; 665 | } 666 | returns += '};\n'; 667 | return returns; 668 | }, 669 | endModule: function(mod, options) { 670 | return '}();'; 671 | } 672 | } 673 | }; 674 | 675 | function traverse(node, visitor) { 676 | if (visitor(node) === false) { 677 | return; 678 | } 679 | 680 | Object.keys(node).forEach(function(key) { 681 | var child = node[key]; 682 | if (child && typeof child === 'object') { 683 | traverse(child, visitor); 684 | } 685 | }); 686 | } 687 | 688 | function modulePath(moduleName, options) { 689 | var isRelative = options.relatives && options.relatives.indexOf(moduleName) !== -1; 690 | return (isRelative ? './' : '') + moduleName; 691 | } 692 | 693 | function importFrom(imp) { 694 | if (imp.from.type === Syntax.Path) { 695 | return imp.from.body[0].name; 696 | } else { 697 | // Must be Literal. 698 | return imp.from.value; 699 | } 700 | } 701 | 702 | function exportName(exp) { 703 | if (exp.declaration.type === Syntax.VariableDeclaration) { 704 | return exp.declaration.declarations[0].id.name; 705 | } else if (exp.declaration.type === Syntax.FunctionDeclaration) { 706 | return exp.declaration.id.name; 707 | } 708 | } 709 | 710 | function joinPath(path) { 711 | return path.body.map(function(id) { return id.name; }).join('.'); 712 | } 713 | 714 | function splice(str, index, howMany, insert) { 715 | var a = str.split(''); 716 | a.splice(index, howMany, insert); 717 | return a.join(''); 718 | } 719 | 720 | function detectIndent(mod, lines) { 721 | var i = 0; 722 | while (i < lines.length) { 723 | var line = lines[i]; 724 | var m = line.match(/^(\s+)\S/); 725 | if (m) { 726 | return m[1]; 727 | } 728 | i++; 729 | } 730 | return ''; 731 | } 732 | /* vim: set sw=4 ts=4 et tw=80 : */ 733 | 734 | 735 | return { 736 | Modifier: Modifier, 737 | harmonize: harmonize, 738 | moduleStyles: moduleStyles 739 | }; 740 | }(); -------------------------------------------------------------------------------- /test/3rdparty/modifier.js: -------------------------------------------------------------------------------- 1 | var modifier = function() {var parse = esprima.parse, Syntax = esprima.Syntax; 2 | 3 | var Modifier = (function () { 4 | function Modifier(src) { 5 | this.ast = parse(src, { loc: true }); 6 | this.lines = src.split('\n'); 7 | } 8 | Modifier.prototype.finish = function() { 9 | return this.lines.join('\n'); 10 | }; 11 | Modifier.prototype.refresh = function() { 12 | this.ast = parse(this.finish(), { loc: true }); 13 | }; 14 | /** 15 | * Removes all text between `from` and `to`. 16 | * Removal is *inclusive* which means the char pointed to by `to` is removed 17 | * as well. This makes it possible to remove a complete expression by 18 | * calling remove(expr.start, expr.end); 19 | * `to` may also be the number of characters to remove 20 | */ 21 | Modifier.prototype.remove = function(from, to) { 22 | if (typeof to === 'number') { 23 | to = {line: from.line, column: from.column + to}; 24 | } 25 | if (from.line === to.line) { 26 | // same line 27 | var line = this.lines[from.line - 1]; 28 | this.lines[from.line - 1] = line.substring(0, from.column) + line.substring(to.column); 29 | } else { 30 | // different lines 31 | this.lines[from.line - 1] = this.lines[from.line - 1].substring(0, from.column); 32 | for (var lineno = from.line; lineno < to.line - 1; lineno++) { 33 | this.lines[lineno] = ''; 34 | } 35 | this.lines[to.line - 1] = this.lines[to.line - 1].substring(to.column); 36 | } 37 | }; 38 | Modifier.prototype.insert = function(pos, text) { 39 | var line = this.lines[pos.line - 1]; 40 | this.lines[pos.line - 1] = line.substring(0, pos.column) + 41 | text + line.substring(pos.column); 42 | }; 43 | Modifier.prototype.replace = function(from, to, text) { 44 | if (typeof to === 'number') { 45 | to = {line: from.line, column: from.column + to}; 46 | } 47 | this.insert(to, text); 48 | this.remove(from, to); 49 | } 50 | ; return Modifier;})(); 51 | 52 | var Modifier = Modifier; 53 | /* vim: set sw=4 ts=4 et tw=80 : */ 54 | 55 | 56 | return { 57 | Modifier: Modifier 58 | }; 59 | }(); -------------------------------------------------------------------------------- /test/benchmarks.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2012 Yusuke Suzuki 3 | Copyright (C) 2011 Ariya Hidayat 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | 27 | /*jslint browser: true node: true */ 28 | /*global load:true, print:true */ 29 | var setupBenchmarks, 30 | fullFixture, 31 | quickFixture; 32 | 33 | fullFixture = [ 34 | 'Underscore 1.4.1', 35 | 'Backbone 0.9.2', 36 | 'CodeMirror 2.34', 37 | 'MooTools 1.4.1', 38 | 'jQuery 1.8.2', 39 | 'jQuery.Mobile 1.2.0', 40 | 'Angular 1.0.2', 41 | 'three.js r51' 42 | ]; 43 | 44 | quickFixture = [ 45 | 'Backbone 0.9.2', 46 | 'jQuery 1.8.2', 47 | 'Angular 1.0.2' 48 | ]; 49 | 50 | function slug(name) { 51 | 'use strict'; 52 | return name.toLowerCase().replace(/\.js/g, 'js').replace(/\s/g, '-'); 53 | } 54 | 55 | function kb(bytes) { 56 | 'use strict'; 57 | return (bytes / 1024).toFixed(1); 58 | } 59 | 60 | if (typeof window !== 'undefined') { 61 | // Run all tests in a browser environment. 62 | setupBenchmarks = function () { 63 | 'use strict'; 64 | 65 | function id(i) { 66 | return document.getElementById(i); 67 | } 68 | 69 | function setText(id, str) { 70 | var el = document.getElementById(id); 71 | if (typeof el.innerText === 'string') { 72 | el.innerText = str; 73 | } else { 74 | el.textContent = str; 75 | } 76 | } 77 | 78 | function enableRunButtons() { 79 | id('runquick').disabled = false; 80 | id('runfull').disabled = false; 81 | } 82 | 83 | function disableRunButtons() { 84 | id('runquick').disabled = true; 85 | id('runfull').disabled = true; 86 | } 87 | 88 | function createTable() { 89 | var str = '', 90 | index, 91 | test, 92 | name; 93 | 94 | str += ''; 95 | str += ''; 96 | str += ''; 97 | str += ''; 98 | for (index = 0; index < fullFixture.length; index += 1) { 99 | test = fullFixture[index]; 100 | name = slug(test); 101 | str += ''; 102 | str += ''; 103 | str += ''; 104 | str += ''; 105 | str += ''; 106 | str += ''; 107 | } 108 | str += ''; 109 | str += ''; 110 | str += ''; 111 | str += ''; 112 | str += ''; 113 | str += '
SourceSize (KiB)Time (ms)Variance
' + test + '
Total
'; 114 | 115 | id('result').innerHTML = str; 116 | } 117 | 118 | function loadTests() { 119 | 120 | var index = 0, 121 | totalSize = 0; 122 | 123 | function load(test, callback) { 124 | var xhr = new XMLHttpRequest(), 125 | src = '3rdparty/' + test + '.js'; 126 | 127 | window.data = window.data || {}; 128 | window.data[test] = ''; 129 | 130 | try { 131 | xhr.timeout = 30000; 132 | xhr.open('GET', src, true); 133 | 134 | xhr.ontimeout = function () { 135 | setText('status', 'Error: time out while loading ' + test); 136 | callback.apply(); 137 | }; 138 | 139 | xhr.onreadystatechange = function () { 140 | var success = false, 141 | size = 0; 142 | 143 | if (this.readyState === XMLHttpRequest.DONE) { 144 | if (this.status === 200) { 145 | window.data[test] = this.responseText; 146 | size = this.responseText.length; 147 | totalSize += size; 148 | success = true; 149 | } 150 | } 151 | 152 | if (success) { 153 | setText(test + '-size', kb(size)); 154 | } else { 155 | setText('status', 'Please wait. Error loading ' + src); 156 | setText(test + '-size', 'Error'); 157 | } 158 | 159 | callback.apply(); 160 | }; 161 | 162 | xhr.send(null); 163 | } catch (e) { 164 | setText('status', 'Please wait. Error loading ' + src); 165 | callback.apply(); 166 | } 167 | } 168 | 169 | function loadNextTest() { 170 | var test; 171 | 172 | if (index < fullFixture.length) { 173 | test = fullFixture[index]; 174 | index += 1; 175 | setText('status', 'Please wait. Loading ' + test + 176 | ' (' + index + ' of ' + fullFixture.length + ')'); 177 | window.setTimeout(function () { 178 | load(slug(test), loadNextTest); 179 | }, 100); 180 | } else { 181 | setText('total-size', kb(totalSize)); 182 | setText('status', 'Ready.'); 183 | enableRunButtons(); 184 | } 185 | } 186 | 187 | loadNextTest(); 188 | } 189 | 190 | function runBenchmarks(suite) { 191 | 192 | var index = 0, 193 | totalTime = 0; 194 | 195 | function reset() { 196 | var i, name; 197 | for (i = 0; i < fullFixture.length; i += 1) { 198 | name = slug(fullFixture[i]); 199 | setText(name + '-time', ''); 200 | setText(name + '-variance', ''); 201 | } 202 | setText('total-time', ''); 203 | } 204 | 205 | function run() { 206 | var el, test, source, benchmark; 207 | 208 | if (index >= suite.length) { 209 | setText('total-time', (1000 * totalTime).toFixed(1)); 210 | setText('status', 'Ready.'); 211 | enableRunButtons(); 212 | return; 213 | } 214 | 215 | test = slug(suite[index]); 216 | el = id(test); 217 | source = window.data[test]; 218 | setText(test + '-time', 'Running...'); 219 | 220 | // Force the result to be held in this array, thus defeating any 221 | // possible "dead core elimination" optimization. 222 | window.tree = []; 223 | 224 | benchmark = new window.Benchmark(test, function (o) { 225 | var syntax = window.esprima.parse(source); 226 | window.tree.push(syntax.body.length); 227 | }, { 228 | 'onComplete': function () { 229 | setText(this.name + '-time', (1000 * this.stats.mean).toFixed(1)); 230 | setText(this.name + '-variance', (1000 * this.stats.variance).toFixed(1)); 231 | totalTime += this.stats.mean; 232 | } 233 | }); 234 | 235 | window.setTimeout(function () { 236 | benchmark.run(); 237 | index += 1; 238 | window.setTimeout(run, 211); 239 | }, 211); 240 | } 241 | 242 | 243 | disableRunButtons(); 244 | setText('status', 'Please wait. Running benchmarks...'); 245 | 246 | reset(); 247 | run(); 248 | } 249 | 250 | id('runquick').onclick = function () { 251 | runBenchmarks(quickFixture); 252 | }; 253 | 254 | id('runfull').onclick = function () { 255 | runBenchmarks(fullFixture); 256 | }; 257 | 258 | setText('benchmarkjs-version', ' version ' + window.Benchmark.version); 259 | setText('version', window.esprima.version); 260 | 261 | createTable(); 262 | disableRunButtons(); 263 | loadTests(); 264 | }; 265 | } else { 266 | 267 | (function (global) { 268 | 'use strict'; 269 | var Benchmark, 270 | esprima, 271 | dirname, 272 | option, 273 | fs, 274 | readFileSync, 275 | log; 276 | 277 | if (typeof require === 'undefined') { 278 | dirname = 'test'; 279 | load(dirname + '/3rdparty/benchmark.js'); 280 | 281 | load(dirname + '/../esprima.js'); 282 | 283 | Benchmark = global.Benchmark; 284 | esprima = global.esprima; 285 | readFileSync = global.read; 286 | log = print; 287 | } else { 288 | Benchmark = require('./3rdparty/benchmark'); 289 | esprima = require('../esprima'); 290 | fs = require('fs'); 291 | option = process.argv[2]; 292 | readFileSync = function readFileSync(filename) { 293 | return fs.readFileSync(filename, 'utf-8'); 294 | }; 295 | dirname = __dirname; 296 | log = console.log.bind(console); 297 | } 298 | 299 | function runTests(tests) { 300 | var index, 301 | tree = [], 302 | totalTime = 0, 303 | totalSize = 0; 304 | 305 | tests.reduce(function (suite, filename) { 306 | var source = readFileSync(dirname + '/3rdparty/' + slug(filename) + '.js'), 307 | size = source.length; 308 | totalSize += size; 309 | return suite.add(filename, function () { 310 | var syntax = esprima.parse(source); 311 | tree.push(syntax.body.length); 312 | }, { 313 | 'onComplete': function (event, bench) { 314 | log(this.name + 315 | ' size ' + kb(size) + 316 | ' time ' + (1000 * this.stats.mean).toFixed(1) + 317 | ' variance ' + (1000 * 1000 * this.stats.variance).toFixed(1)); 318 | totalTime += this.stats.mean; 319 | } 320 | }); 321 | }, new Benchmark.Suite()).on('complete', function () { 322 | log('Total size ' + kb(totalSize) + 323 | ' time ' + (1000 * totalTime).toFixed(1)); 324 | }).run(); 325 | } 326 | 327 | if (option === 'quick') { 328 | runTests(quickFixture); 329 | } else { 330 | runTests(fullFixture); 331 | } 332 | }(this)); 333 | } 334 | /* vim: set sw=4 ts=4 et tw=80 : */ 335 | -------------------------------------------------------------------------------- /test/compare.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2012 Ariya Hidayat 3 | Copyright (C) 2011 Ariya Hidayat 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /*jslint browser: true node: true */ 27 | /*global load:true, print:true */ 28 | var setupBenchmarks, 29 | parsers, 30 | fixtureList, 31 | suite; 32 | 33 | parsers = [ 34 | 'Esprima', 35 | 'parse-js', 36 | 'Acorn' 37 | ]; 38 | 39 | fixtureList = [ 40 | 'Underscore 1.4.1', 41 | 'Backbone 0.9.2', 42 | 'CodeMirror 2.34', 43 | 'jQuery 1.8.2' 44 | ]; 45 | 46 | function slug(name) { 47 | 'use strict'; 48 | return name.toLowerCase().replace(/\.js/g, 'js').replace(/\s/g, '-'); 49 | } 50 | 51 | function kb(bytes) { 52 | 'use strict'; 53 | return (bytes / 1024).toFixed(1); 54 | } 55 | 56 | function inject(fname) { 57 | 'use strict'; 58 | var head = document.getElementsByTagName('head')[0], 59 | script = document.createElement('script'); 60 | 61 | script.src = fname; 62 | script.type = 'text/javascript'; 63 | head.appendChild(script); 64 | } 65 | 66 | if (typeof window !== 'undefined') { 67 | 68 | // Run all tests in a browser environment. 69 | setupBenchmarks = function () { 70 | 'use strict'; 71 | 72 | function id(i) { 73 | return document.getElementById(i); 74 | } 75 | 76 | function setText(id, str) { 77 | var el = document.getElementById(id); 78 | if (typeof el.innerText === 'string') { 79 | el.innerText = str; 80 | } else { 81 | el.textContent = str; 82 | } 83 | } 84 | 85 | function enableRunButtons() { 86 | id('run').disabled = false; 87 | } 88 | 89 | function disableRunButtons() { 90 | id('run').disabled = true; 91 | } 92 | 93 | function createTable() { 94 | var str = '', 95 | i, 96 | index, 97 | test, 98 | name; 99 | 100 | str += ''; 101 | str += ''; 102 | for (i = 0; i < parsers.length; i += 1) { 103 | str += ''; 104 | } 105 | str += ''; 106 | str += ''; 107 | for (index = 0; index < fixtureList.length; index += 1) { 108 | test = fixtureList[index]; 109 | name = slug(test); 110 | str += ''; 111 | str += ''; 112 | if (window.data && window.data[name]) { 113 | str += ''; 114 | } else { 115 | str += ''; 116 | } 117 | for (i = 0; i < parsers.length; i += 1) { 118 | str += ''; 119 | } 120 | str += ''; 121 | } 122 | str += ''; 123 | str += ''; 124 | for (i = 0; i < parsers.length; i += 1) { 125 | str += ''; 126 | } 127 | str += ''; 128 | str += ''; 129 | str += '
SourceSize (KiB)' + parsers[i] + '
' + test + '' + kb(window.data[name].length) + '
Total
'; 130 | 131 | id('result').innerHTML = str; 132 | } 133 | 134 | function loadFixtures() { 135 | 136 | var index = 0, 137 | totalSize = 0; 138 | 139 | function load(test, callback) { 140 | var xhr = new XMLHttpRequest(), 141 | src = '3rdparty/' + test + '.js'; 142 | 143 | window.data = window.data || {}; 144 | window.data[test] = ''; 145 | 146 | try { 147 | xhr.timeout = 30000; 148 | xhr.open('GET', src, true); 149 | 150 | xhr.ontimeout = function () { 151 | setText('status', 'Error: time out while loading ' + test); 152 | callback.apply(); 153 | }; 154 | 155 | xhr.onreadystatechange = function () { 156 | var success = false, 157 | size = 0; 158 | 159 | if (this.readyState === XMLHttpRequest.DONE) { 160 | if (this.status === 200) { 161 | window.data[test] = this.responseText; 162 | size = this.responseText.length; 163 | totalSize += size; 164 | success = true; 165 | } 166 | } 167 | 168 | if (success) { 169 | setText(test + '-size', kb(size)); 170 | } else { 171 | setText('status', 'Please wait. Error loading ' + src); 172 | setText(test + '-size', 'Error'); 173 | } 174 | 175 | callback.apply(); 176 | }; 177 | 178 | xhr.send(null); 179 | } catch (e) { 180 | setText('status', 'Please wait. Error loading ' + src); 181 | callback.apply(); 182 | } 183 | } 184 | 185 | function loadNextTest() { 186 | var test; 187 | 188 | if (index < fixtureList.length) { 189 | test = fixtureList[index]; 190 | index += 1; 191 | setText('status', 'Please wait. Loading ' + test + 192 | ' (' + index + ' of ' + fixtureList.length + ')'); 193 | window.setTimeout(function () { 194 | load(slug(test), loadNextTest); 195 | }, 100); 196 | } else { 197 | setText('total-size', kb(totalSize)); 198 | setText('status', 'Ready.'); 199 | enableRunButtons(); 200 | } 201 | } 202 | 203 | loadNextTest(); 204 | } 205 | 206 | function setupParser() { 207 | var i, j; 208 | 209 | suite = []; 210 | for (i = 0; i < fixtureList.length; i += 1) { 211 | for (j = 0; j < parsers.length; j += 1) { 212 | suite.push({ 213 | fixture: fixtureList[i], 214 | parser: parsers[j] 215 | }); 216 | } 217 | } 218 | 219 | createTable(); 220 | } 221 | 222 | function runBenchmarks() { 223 | 224 | var index = 0, 225 | totalTime = {}; 226 | 227 | function reset() { 228 | var i, name; 229 | for (i = 0; i < suite.length; i += 1) { 230 | name = slug(suite[i].fixture) + '-' + slug(suite[i].parser); 231 | setText(name + '-time', ''); 232 | } 233 | } 234 | 235 | function run() { 236 | var fixture, parser, test, source, fn, benchmark; 237 | 238 | if (index >= suite.length) { 239 | setText('status', 'Ready.'); 240 | enableRunButtons(); 241 | return; 242 | } 243 | 244 | fixture = suite[index].fixture; 245 | parser = suite[index].parser; 246 | 247 | source = window.data[slug(fixture)]; 248 | 249 | test = slug(fixture) + '-' + slug(parser); 250 | setText(test + '-time', 'Running...'); 251 | 252 | setText('status', 'Please wait. Parsing ' + fixture + '...'); 253 | 254 | // Force the result to be held in this array, thus defeating any 255 | // possible "dead code elimination" optimization. 256 | window.tree = []; 257 | 258 | switch (parser) { 259 | case 'Esprima': 260 | fn = function () { 261 | var syntax = window.esprima.parse(source); 262 | window.tree.push(syntax.body.length); 263 | }; 264 | break; 265 | 266 | case 'parse-js': 267 | fn = function () { 268 | var syntax = window.parseJS.parse(source); 269 | window.tree.push(syntax.length); 270 | }; 271 | break; 272 | 273 | case 'Acorn': 274 | fn = function () { 275 | var syntax = window.acorn.parse(source); 276 | window.tree.push(syntax.body.length); 277 | }; 278 | break; 279 | 280 | default: 281 | throw 'Unknown parser type ' + parser; 282 | } 283 | 284 | benchmark = new window.Benchmark(test, fn, { 285 | 'onComplete': function () { 286 | setText(this.name + '-time', (1000 * this.stats.mean).toFixed(1)); 287 | if (!totalTime[parser]) { 288 | totalTime[parser] = this.stats.mean; 289 | } else { 290 | totalTime[parser] += this.stats.mean; 291 | } 292 | setText(slug(parser) + '-total', (1000 * totalTime[parser]).toFixed(1)); 293 | } 294 | }); 295 | 296 | window.setTimeout(function () { 297 | benchmark.run(); 298 | index += 1; 299 | window.setTimeout(run, 211); 300 | }, 211); 301 | } 302 | 303 | 304 | disableRunButtons(); 305 | setText('status', 'Please wait. Running benchmarks...'); 306 | 307 | reset(); 308 | run(); 309 | } 310 | 311 | id('run').onclick = function () { 312 | runBenchmarks(); 313 | }; 314 | 315 | setText('benchmarkjs-version', ' version ' + window.Benchmark.version); 316 | setText('version', window.esprima.version); 317 | 318 | setupParser(); 319 | createTable(); 320 | 321 | disableRunButtons(); 322 | loadFixtures(); 323 | }; 324 | } else { 325 | // TODO 326 | console.log('Not implemented yet!'); 327 | } 328 | /* vim: set sw=4 ts=4 et tw=80 : */ 329 | -------------------------------------------------------------------------------- /test/compat.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2012 Joost-Wim Boekesteijn 3 | Copyright (C) 2011 Ariya Hidayat 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /*jslint node: true */ 27 | /*global document: true, window:true, esprima: true, testReflect: true */ 28 | 29 | var runTests; 30 | 31 | function getContext(esprima, reportCase, reportFailure) { 32 | 'use strict'; 33 | 34 | var Reflect, Pattern; 35 | 36 | // Maps Mozilla Reflect object to our Esprima parser. 37 | Reflect = { 38 | parse: function (code) { 39 | var result; 40 | 41 | reportCase(code); 42 | 43 | try { 44 | result = esprima.parse(code); 45 | } catch (error) { 46 | result = error; 47 | } 48 | 49 | return result; 50 | } 51 | }; 52 | 53 | // This is used by Reflect test suite to match a syntax tree. 54 | Pattern = function (obj) { 55 | var pattern; 56 | 57 | // Poor man's deep object cloning. 58 | pattern = JSON.parse(JSON.stringify(obj)); 59 | 60 | // Special handling for regular expression literal since we need to 61 | // convert it to a string literal, otherwise it will be decoded 62 | // as object "{}" and the regular expression would be lost. 63 | if (obj.type && obj.type === 'Literal') { 64 | if (obj.value instanceof RegExp) { 65 | pattern = { 66 | type: obj.type, 67 | value: obj.value.toString() 68 | }; 69 | } 70 | } 71 | 72 | // Special handling for branch statement because SpiderMonkey 73 | // prefers to put the 'alternate' property before 'consequent'. 74 | if (obj.type && obj.type === 'IfStatement') { 75 | pattern = { 76 | type: pattern.type, 77 | test: pattern.test, 78 | consequent: pattern.consequent, 79 | alternate: pattern.alternate 80 | }; 81 | } 82 | 83 | // Special handling for do while statement because SpiderMonkey 84 | // prefers to put the 'test' property before 'body'. 85 | if (obj.type && obj.type === 'DoWhileStatement') { 86 | pattern = { 87 | type: pattern.type, 88 | body: pattern.body, 89 | test: pattern.test 90 | }; 91 | } 92 | 93 | function adjustRegexLiteralAndRaw(key, value) { 94 | if (key === 'value' && value instanceof RegExp) { 95 | value = value.toString(); 96 | } else if (key === 'raw' && typeof value === "string") { 97 | // Ignore Esprima-specific 'raw' property. 98 | return undefined; 99 | } else if (key === 'regex' && typeof value === "object") { 100 | // Ignore Esprima-specific 'regex' property. 101 | return undefined; 102 | } 103 | return value; 104 | } 105 | 106 | if (obj.type && (obj.type === 'Program')) { 107 | pattern.assert = function (tree) { 108 | var actual, expected; 109 | actual = JSON.stringify(tree, adjustRegexLiteralAndRaw, 4); 110 | expected = JSON.stringify(obj, null, 4); 111 | 112 | if (expected !== actual) { 113 | reportFailure(expected, actual); 114 | } 115 | }; 116 | } 117 | 118 | return pattern; 119 | }; 120 | 121 | return { 122 | Reflect: Reflect, 123 | Pattern: Pattern 124 | }; 125 | } 126 | 127 | if (typeof window !== 'undefined') { 128 | // Run all tests in a browser environment. 129 | runTests = function () { 130 | 'use strict'; 131 | 132 | var total = 0, 133 | failures = 0; 134 | 135 | function setText(el, str) { 136 | if (typeof el.innerText === 'string') { 137 | el.innerText = str; 138 | } else { 139 | el.textContent = str; 140 | } 141 | } 142 | 143 | function reportCase(code) { 144 | var report, e; 145 | report = document.getElementById('report'); 146 | e = document.createElement('pre'); 147 | e.setAttribute('class', 'code'); 148 | setText(e, code); 149 | report.appendChild(e); 150 | total += 1; 151 | } 152 | 153 | function reportFailure(expected, actual) { 154 | var report, e; 155 | 156 | failures += 1; 157 | 158 | report = document.getElementById('report'); 159 | 160 | e = document.createElement('p'); 161 | setText(e, 'Expected'); 162 | report.appendChild(e); 163 | 164 | e = document.createElement('pre'); 165 | e.setAttribute('class', 'expected'); 166 | setText(e, expected); 167 | report.appendChild(e); 168 | 169 | e = document.createElement('p'); 170 | setText(e, 'Actual'); 171 | report.appendChild(e); 172 | 173 | e = document.createElement('pre'); 174 | e.setAttribute('class', 'actual'); 175 | setText(e, actual); 176 | report.appendChild(e); 177 | } 178 | 179 | setText(document.getElementById('version'), esprima.version); 180 | 181 | window.setTimeout(function () { 182 | var tick, context = getContext(esprima, reportCase, reportFailure); 183 | 184 | tick = new Date(); 185 | testReflect(context.Reflect, context.Pattern); 186 | tick = (new Date()) - tick; 187 | 188 | if (failures > 0) { 189 | document.getElementById('status').className = 'alert-box alert'; 190 | setText(document.getElementById('status'), total + ' tests. ' + 191 | 'Failures: ' + failures + '. ' + tick + ' ms'); 192 | } else { 193 | document.getElementById('status').className = 'alert-box success'; 194 | setText(document.getElementById('status'), total + ' tests. ' + 195 | 'No failure. ' + tick + ' ms'); 196 | } 197 | }, 11); 198 | }; 199 | } else { 200 | (function (global) { 201 | 'use strict'; 202 | var esprima = require('../esprima'), 203 | tick, 204 | total = 0, 205 | failures = [], 206 | header, 207 | current, 208 | context; 209 | 210 | function reportCase(code) { 211 | total += 1; 212 | current = code; 213 | } 214 | 215 | function reportFailure(expected, actual) { 216 | failures.push({ 217 | source: current, 218 | expected: expected.toString(), 219 | actual: actual.toString() 220 | }); 221 | } 222 | 223 | context = getContext(esprima, reportCase, reportFailure); 224 | 225 | tick = new Date(); 226 | require('./reflect').testReflect(context.Reflect, context.Pattern); 227 | tick = (new Date()) - tick; 228 | 229 | header = total + ' tests. ' + failures.length + ' failures. ' + 230 | tick + ' ms'; 231 | if (failures.length) { 232 | console.error(header); 233 | failures.forEach(function (failure) { 234 | console.error(failure.source + ': Expected\n ' + 235 | failure.expected.split('\n').join('\n ') + 236 | '\nto match\n ' + failure.actual); 237 | }); 238 | } else { 239 | console.log(header); 240 | } 241 | process.exit(failures.length === 0 ? 0 : 1); 242 | }(this)); 243 | } 244 | /* vim: set sw=4 ts=4 et tw=80 : */ 245 | -------------------------------------------------------------------------------- /test/fbtest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This test works differently than the other tests. 3 | * 4 | * test/fbtest.js 5 | * This file lists out the tests but not the expected ASTs/errors 6 | * 7 | * test/fbtest.rec.js 8 | * This AUTOGENERATED file contains the ASTs and errors for the 9 | * tests below. This is the test run by test/run.js 10 | * 11 | * tools/generate-fbtest.js 12 | * This script reads fbtest.js and generates fbtest.rec.js 13 | * 14 | * 15 | * TO ADD A TEST 16 | * 1) Add the string you want to parse to test/fbtest.js 17 | * 2) Run tools/generate-fbtest.js 18 | * 19 | * TO RE-RECORD AN EXISTING TEST 20 | * 1) Run tools/generate-fbtest.js 21 | * 22 | * TO DELETE A TEST 23 | * 1) Remove the test from test/fbtest.js 24 | * 2) Run tools/generate-fbtest.js 25 | */ 26 | 27 | /* jscs:disable disallowTrailingComma */ 28 | 29 | module.exports = { 30 | 'JSX': [ 31 | '', 32 | '', 33 | ' {value} ', 34 | '', 35 | '', 36 | '', 37 | '<日本語>', 38 | '\nbar\nbaz\r\n', 39 | ' : } />', 40 | '{}', 41 | '{\r\n}', 42 | '{/* this is a comment */}', 43 | '{/* this\nis\na\nmulti-line\ncomment */}', 44 | '
@test content
', 45 | '

7x invalid-js-identifier
', 46 | ' right=monkeys />', 47 | '', 48 | '', 49 | '(
) < x;', 50 | '
', 51 | '
', 52 | '
', 53 | ' ', 54 | '= == =', 55 | ], 56 | 'Invalid JSX Syntax': [ 57 | '', 58 | '', 59 | '<:a />', 60 | '', 61 | '', 62 | '', 63 | '', 77 | '', 78 | '
', 79 | '
', 80 | '
stuff
', 81 | '
stuff
', 82 | '
>', 83 | ' >', 84 | '', 85 | '', 86 | '}', 87 | '', 88 | ], 89 | 'Type Annotations': [ 90 | 'function foo(numVal: any){}', 91 | 'function foo(numVal: number){}', 92 | 'function foo(numVal: number, strVal: string){}', 93 | 'function foo(numVal: number, untypedVal){}', 94 | 'function foo(untypedVal, numVal: number){}', 95 | 'function foo(nullableNum: ?number){}', 96 | 'function foo(callback: () => void){}', 97 | 'function foo(callback: () => number){}', 98 | 'function foo(callback: (_:bool) => number){}', 99 | 'function foo(callback: (_1:bool, _2:string) => number){}', 100 | 'function foo(callback: (_1:bool, ...foo:Array) => number){}', 101 | 'function foo():number{}', 102 | 'function foo():() => void{}', 103 | 'function foo():(_:bool) => number{}', 104 | 'function foo():(_?:bool) => number{}', 105 | 'function foo(): {} {}', 106 | 'function foo() {}', 107 | 'function foo() {}', 108 | 'a=function() {}', 109 | 'a={set fooProp(value:number){}}', 110 | 'a={set fooProp(value:number):void{}}', 111 | 'a={get fooProp():number{}}', 112 | 'a={id(x: T): T {}}', 113 | 'a={*id(x: T): T {}}', 114 | 'a={async id(x: T): T {}}', 115 | 'a={123(x: T): T {}}', 116 | 'class Foo {set fooProp(value:number){}}', 117 | 'class Foo {set fooProp(value:number):void{}}', 118 | 'class Foo {get fooProp():number{}}', 119 | 'var numVal:number;', 120 | 'var numVal:number = otherNumVal;', 121 | 'var a: {numVal: number};', 122 | 'var a: {numVal: number;};', 123 | 'var a: {numVal: number; [indexer: string]: number};', 124 | 'var a: ?{numVal: number};', 125 | 'var a: {numVal: number; strVal: string}', 126 | 'var a: {numVal: number, strVal: string}', 127 | 'var a: {subObj: {strVal: string}}', 128 | 'var a: {subObj: ?{strVal: string}}', 129 | 'var a: {param1: number; param2: string}', 130 | 'var a: {param1: number; param2?: string}', 131 | 'var a: { [a: number]: string; [b: number]: string; };', 132 | 'var a: {add(x:number, ...y:Array): void}', 133 | 'var a: { id(x: T): T; }', 134 | 'var a:Array = [1, 2, 3]', 135 | 'a = class Foo { }', 136 | 'a = class Foo extends Bar { }', 137 | 'class Foo {}', 138 | 'class Foo extends Bar { }', 139 | 'class Foo extends mixin(Bar) { }', 140 | 'class Foo { bar():number { return 42; }}', 141 | 'class Foo { "bar"() { } }', 142 | 'function foo(requiredParam, optParam?) {}', 143 | 'class Foo { prop1:string; prop2:number; }', 144 | 'class Foo { static prop1:string; prop2:number; }', 145 | 'var x : number | string = 4;', 146 | 'class Array { concat(items:number | string) {}; }', 147 | 'var x : () => number | () => string = fn;', 148 | 'var x: typeof Y = Y;', 149 | 'var x: typeof Y | number = Y;', 150 | 'var {x}: {x: string; } = { x: "hello" };', 151 | 'var {x}: {x: string } = { x: "hello" };', 152 | 'var [x]: Array = [ "hello" ];', 153 | 'function foo({x}: { x: string; }) {}', 154 | 'function foo([x]: Array) {}', 155 | 'function foo(...rest: Array) {}', 156 | '(function (...rest: Array) {})', 157 | '((...rest: Array) => rest)', 158 | 'var a: Map >', 159 | 'var a: Map>', 160 | 'var a: Map>>', 161 | 'var a: number[]', 162 | 'var a: ?string[]', 163 | 'var a: Promise[]', 164 | 'var a:(...rest:Array) => number', 165 | 'var identity: (x: T) => T', 166 | 'var identity: (x: T, ...y:T[]) => T', 167 | 'import type foo from "bar";', 168 | 'import type {foo, bar} from "baz";', 169 | 'import type {foo as bar} from "baz";', 170 | 'import type from "foo";', 171 | 'import type, {foo} from "bar";', 172 | 'import type * as namespace from "bar";', 173 | 174 | 'import typeof foo from "bar";', 175 | 'import typeof {foo, bar} from "baz";', 176 | 'import typeof {foo as bar} from "baz";', 177 | 'import typeof * as namespace from "bar";', 178 | ], 179 | 'Invalid Type Annotations': [ 180 | 'function foo(callback:) {}', 181 | 'function foo(): {}', 182 | 'function foo(): { foo() }', 183 | 'function foo(callback:(string) => number) {}', 184 | 'a = {foo(): { return 42; }}', 185 | 'class Foo { get bar() { } }', 186 | 'var a:{a:number b:string}', 187 | 'var x: (number) => string', 188 | 'var y: return', 189 | ], 190 | 'Hacky Type Annotations': [ 191 | 'class Foo { constructor: Function; constructor(){} }', 192 | ], 193 | 'Array Types': [ 194 | 'var a: number[]', 195 | 'var a: ?number[]', 196 | 'var a: (?number)[]', 197 | 'var a: () => number[]', 198 | 'var a: (() => number)[]', 199 | 'var a: typeof A[]', 200 | ], 201 | 'Tuples': [ 202 | 'var a : [] = [];', 203 | 'var a : [Foo] = [foo];', 204 | 'var a : [number,] = [123,];', 205 | 'var a : [number, string] = [123, "duck"];' 206 | ], 207 | 'Type Alias': [ 208 | 'type FBID = number;', 209 | 'type Foo = Bar', 210 | 'export type Foo = number;', 211 | 'export type {Foo};', 212 | ], 213 | 'Interfaces (module and script)': [ 214 | 'interface A {}', 215 | 'interface A extends B {}', 216 | 'interface A extends B, C {}', 217 | 'interface A { foo: () => number; }', 218 | 'interface Dictionary { [index: string]: string; length: number; }', 219 | 'class Foo implements Bar {}', 220 | 'class Foo extends Bar implements Bat, Man {}', 221 | 'class Foo extends class Bar implements Bat {} {}', 222 | 'class Foo extends class Bar implements Bat {} implements Man {}', 223 | ], 224 | 'Type Grouping': [ 225 | 'var a: (number)', 226 | 'var a: (() => number) | () => string', 227 | 'var a: number & (string | bool)', 228 | 'var a: (typeof A)', 229 | ], 230 | 'Invalid Type Alias': [ 231 | 'if (true) type foo = number', 232 | ], 233 | 'Invalid Interfaces': [ 234 | 'interface {}', 235 | 'interface A extends {}', 236 | ], 237 | 'Call Properties': [ 238 | 'var a : { (): number }', 239 | 'var a : { (): number; }', 240 | 'var a : { (): number; y: string; (x: string): string }', 241 | 'var a : { (x: T): number; }', 242 | 'interface A { (): number; }', 243 | ], 244 | 'String Literal Types': [ 245 | 'function createElement(tagName: "div"): HTMLDivElement {}', 246 | 'function createElement(tagName: \'div\'): HTMLDivElement {}', 247 | ], 248 | 'Invalid String Literal Types': [ 249 | 'var a: "\\01"', 250 | ], 251 | 'Qualified Generic Type': [ 252 | 'var a : A.B', 253 | 'var a : A.B.C', 254 | 'var a : A.B', 255 | 'var a : typeof A.B', 256 | ], 257 | 'Declare Statements': [ 258 | 'declare var foo', 259 | 'declare var foo;', 260 | 'declare function foo(): void', 261 | 'declare function foo(): void;', 262 | 'declare function foo(): void;', 263 | 'declare function foo(x: number, y: string): void;', 264 | 'declare class A {}', 265 | 'declare class A extends B { x: number }', 266 | 'declare class A { static foo(): number; static x : string }', 267 | 'declare class A { static [ indexer: number]: string }', 268 | 'declare class A { static () : number }', 269 | ], 270 | 'Invalid Declare Statements': [ 271 | 'declare class A { "static" foo(): number }', 272 | 'declare class A { static : number }', 273 | 'declare function foo();', 274 | 'declare function foo(x): void', 275 | ], 276 | 'Declare Module': [ 277 | 'declare module A {}', 278 | 'declare module "./a/b.js" {}', 279 | 'declare module A { declare var x: number; }', 280 | 'declare module A { declare function foo(): number; }', 281 | 'declare module A { declare class B { foo(): number; } }', 282 | ], 283 | 'Invalid Declare Module': [ 284 | 'declare Module A {}', 285 | 'declare module {}', 286 | 'declare module A { declare module B {} }', 287 | 'declare module A { export default 1 +1; }', 288 | 'declare module A { function foo() {} }', 289 | '"use strict"; declare module "\\01" {}', 290 | ], 291 | 'Typecasts': [ 292 | '(xxx: number)', 293 | '({xxx: 0, yyy: "hey"}: {xxx: number; yyy: string})', 294 | // distinguish between function type params and typecasts 295 | '((xxx) => xxx + 1: (xxx: number) => number)', 296 | // parens disambiguate groups from casts 297 | '((xxx: number), (yyy: string))', 298 | ], 299 | 'Invalid Typecasts': [ 300 | // Must be parenthesized 301 | 'var x: number = 0: number;', 302 | // ...even within groups 303 | '(xxx: number, yyy: string)' 304 | ], 305 | 'Bounded Polymorphism': [ 306 | 'class A {}', 307 | 'function bar() {}', 308 | ], 309 | }; 310 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Esprima: Unit Tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 68 | 69 | 70 |
71 |
72 |

Unit Tests ensures the correct implementation

73 |
74 |
75 | 76 | 77 |
78 |
79 |
80 |
81 | 82 |
83 |
Please wait...
84 |
85 |

A software project is only as good as its QA workflow. This set of unit tests 86 | makes sure that no regression is ever sneaked in.

87 |

Version being tested: .

88 |
89 |
90 |
91 | 92 | 93 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /test/module.js: -------------------------------------------------------------------------------- 1 | /*jslint browser:true plusplus:true */ 2 | /*global require:true */ 3 | function runTests() { 4 | 'use strict'; 5 | 6 | function setText(el, str) { 7 | if (typeof el.innerText === 'string') { 8 | el.innerText = str; 9 | } else { 10 | el.textContent = str; 11 | } 12 | } 13 | 14 | function reportSuccess(code) { 15 | var report, e; 16 | report = document.getElementById('report'); 17 | e = document.createElement('pre'); 18 | e.setAttribute('class', 'code'); 19 | setText(e, code); 20 | report.appendChild(e); 21 | } 22 | 23 | function reportFailure(code, expected, actual) { 24 | var report, e; 25 | 26 | report = document.getElementById('report'); 27 | 28 | e = document.createElement('pre'); 29 | e.setAttribute('class', 'code'); 30 | setText(e, code); 31 | report.appendChild(e); 32 | 33 | e = document.createElement('p'); 34 | setText(e, 'Expected type: ' + expected); 35 | report.appendChild(e); 36 | 37 | e = document.createElement('p'); 38 | setText(e, 'Actual type: ' + actual); 39 | report.appendChild(e); 40 | } 41 | 42 | 43 | require(['../esprima'], function (ESParser) { 44 | var tick, total, failures, obj, variable, variables, i, entry, entries; 45 | 46 | // We check only the type of some members of ESParser. 47 | variables = { 48 | 'version': 'string', 49 | 'parse': 'function', 50 | 'Syntax': 'object', 51 | 'Syntax.AssignmentExpression': 'string', 52 | 'Syntax.ArrayExpression': 'string', 53 | 'Syntax.BlockStatement': 'string', 54 | 'Syntax.BinaryExpression': 'string', 55 | 'Syntax.BreakStatement': 'string', 56 | 'Syntax.CallExpression': 'string', 57 | 'Syntax.CatchClause': 'string', 58 | 'Syntax.ConditionalExpression': 'string', 59 | 'Syntax.ContinueStatement': 'string', 60 | 'Syntax.DoWhileStatement': 'string', 61 | 'Syntax.DebuggerStatement': 'string', 62 | 'Syntax.EmptyStatement': 'string', 63 | 'Syntax.ExpressionStatement': 'string', 64 | 'Syntax.ForStatement': 'string', 65 | 'Syntax.ForInStatement': 'string', 66 | 'Syntax.FunctionDeclaration': 'string', 67 | 'Syntax.FunctionExpression': 'string', 68 | 'Syntax.Identifier': 'string', 69 | 'Syntax.IfStatement': 'string', 70 | 'Syntax.Literal': 'string', 71 | 'Syntax.LabeledStatement': 'string', 72 | 'Syntax.LogicalExpression': 'string', 73 | 'Syntax.MemberExpression': 'string', 74 | 'Syntax.NewExpression': 'string', 75 | 'Syntax.ObjectExpression': 'string', 76 | 'Syntax.Program': 'string', 77 | 'Syntax.Property': 'string', 78 | 'Syntax.ReturnStatement': 'string', 79 | 'Syntax.SequenceExpression': 'string', 80 | 'Syntax.SwitchStatement': 'string', 81 | 'Syntax.SwitchCase': 'string', 82 | 'Syntax.ThisExpression': 'string', 83 | 'Syntax.ThrowStatement': 'string', 84 | 'Syntax.TryStatement': 'string', 85 | 'Syntax.UnaryExpression': 'string', 86 | 'Syntax.UpdateExpression': 'string', 87 | 'Syntax.VariableDeclaration': 'string', 88 | 'Syntax.VariableDeclarator': 'string', 89 | 'Syntax.WhileStatement': 'string', 90 | 'Syntax.WithStatement': 'string' 91 | }; 92 | 93 | total = failures = 0; 94 | tick = new Date(); 95 | 96 | for (variable in variables) { 97 | if (variables.hasOwnProperty(variable)) { 98 | entries = variable.split('.'); 99 | obj = ESParser; 100 | for (i = 0; i < entries.length; ++i) { 101 | entry = entries[i]; 102 | if (typeof obj[entry] !== 'undefined') { 103 | obj = obj[entry]; 104 | } else { 105 | obj = undefined; 106 | break; 107 | } 108 | } 109 | total++; 110 | if (typeof obj === variables[variable]) { 111 | reportSuccess(variable); 112 | } else { 113 | failures++; 114 | reportFailure(variable, variables[variable], typeof obj); 115 | } 116 | } 117 | } 118 | 119 | tick = (new Date()) - tick; 120 | 121 | if (failures > 0) { 122 | document.getElementById('status').className = 'alert-box alert'; 123 | setText(document.getElementById('status'), total + ' tests. ' + 124 | 'Failures: ' + failures + '. ' + tick + ' ms'); 125 | } else { 126 | document.getElementById('status').className = 'alert-box success'; 127 | setText(document.getElementById('status'), total + ' tests. ' + 128 | 'No failure. ' + tick + ' ms'); 129 | } 130 | }); 131 | } 132 | -------------------------------------------------------------------------------- /test/reflect.js: -------------------------------------------------------------------------------- 1 | // This is modified from Mozilla Reflect.parse test suite (the file is located 2 | // at js/src/tests/js1_8_5/extensions/reflect-parse.js in the source tree). 3 | // 4 | // Some notable changes: 5 | // * Removed unsupported features (destructuring, let, comprehensions...). 6 | // * Removed tests for E4X (ECMAScript for XML). 7 | // * Removed everything related to builder. 8 | // * Enclosed every 'Pattern' construct with a scope. 9 | // * Removed the test for bug 632030 and bug 632024. 10 | 11 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 12 | /* 13 | * Any copyright is dedicated to the Public Domain. 14 | * http://creativecommons.org/licenses/publicdomain/ 15 | */ 16 | 17 | (function (exports) { 18 | 19 | function testReflect(Reflect, Pattern) { 20 | 21 | function program(elts) { return Pattern({ type: "Program", body: elts }) } 22 | function exprStmt(expr) { return Pattern({ type: "ExpressionStatement", expression: expr }) } 23 | function throwStmt(expr) { return Pattern({ type: "ThrowStatement", argument: expr }) } 24 | function returnStmt(expr) { return Pattern({ type: "ReturnStatement", argument: expr }) } 25 | function yieldExpr(expr) { return Pattern({ type: "YieldExpression", argument: expr }) } 26 | function lit(val) { return Pattern({ type: "Literal", value: val }) } 27 | var thisExpr = Pattern({ type: "ThisExpression" }); 28 | function funDecl(id, params, body) { return Pattern({ type: "FunctionDeclaration", 29 | id: id, 30 | params: params, 31 | defaults: [], 32 | body: body, 33 | rest: null, 34 | generator: false, 35 | expression: false 36 | }) } 37 | function genFunDecl(id, params, body) { return Pattern({ type: "FunctionDeclaration", 38 | id: id, 39 | params: params, 40 | defaults: [], 41 | body: body, 42 | rest: null, 43 | generator: true, 44 | expression: false 45 | }) } 46 | function declarator(id, init) { return Pattern({ type: "VariableDeclarator", id: id, init: init }) } 47 | function varDecl(decls) { return Pattern({ type: "VariableDeclaration", declarations: decls, kind: "var" }) } 48 | function letDecl(decls) { return Pattern({ type: "VariableDeclaration", declarations: decls, kind: "let" }) } 49 | function constDecl(decls) { return Pattern({ type: "VariableDeclaration", declarations: decls, kind: "const" }) } 50 | function ident(name) { return Pattern({ type: "Identifier", name: name }) } 51 | function dotExpr(obj, id) { return Pattern({ type: "MemberExpression", computed: false, object: obj, property: id }) } 52 | function memExpr(obj, id) { return Pattern({ type: "MemberExpression", computed: true, object: obj, property: id }) } 53 | function forStmt(init, test, update, body) { return Pattern({ type: "ForStatement", init: init, test: test, update: update, body: body }) } 54 | function forInStmt(lhs, rhs, body) { return Pattern({ type: "ForInStatement", left: lhs, right: rhs, body: body, each: false }) } 55 | function forEachInStmt(lhs, rhs, body) { return Pattern({ type: "ForInStatement", left: lhs, right: rhs, body: body, each: true }) } 56 | function breakStmt(lab) { return Pattern({ type: "BreakStatement", label: lab }) } 57 | function continueStmt(lab) { return Pattern({ type: "ContinueStatement", label: lab }) } 58 | function blockStmt(body) { return Pattern({ type: "BlockStatement", body: body }) } 59 | var emptyStmt = Pattern({ type: "EmptyStatement" }); 60 | function ifStmt(test, cons, alt) { return Pattern({ type: "IfStatement", test: test, alternate: alt, consequent: cons }) } 61 | function labStmt(lab, stmt) { return Pattern({ type: "LabeledStatement", label: lab, body: stmt }) } 62 | function withStmt(obj, stmt) { return Pattern({ type: "WithStatement", object: obj, body: stmt }) } 63 | function whileStmt(test, stmt) { return Pattern({ type: "WhileStatement", test: test, body: stmt }) } 64 | function doStmt(stmt, test) { return Pattern({ type: "DoWhileStatement", test: test, body: stmt }) } 65 | function switchStmt(disc, cases) { return Pattern({ type: "SwitchStatement", discriminant: disc, cases: cases }) } 66 | function caseClause(test, stmts) { return Pattern({ type: "SwitchCase", test: test, consequent: stmts }) } 67 | function defaultClause(stmts) { return Pattern({ type: "SwitchCase", test: null, consequent: stmts }) } 68 | function catchClause(id, guard, body) { if (guard) { return Pattern({ type: "GuardedCatchClause", param: id, guard: guard, body: body }) } else { return Pattern({ type: "CatchClause", param: id, body: body }) } } 69 | function tryStmt(body, guarded, catches, fin) { return Pattern({ type: "TryStatement", block: body, guardedHandlers: guarded, handlers: catches, finalizer: fin }) } 70 | function letStmt(head, body) { return Pattern({ type: "LetStatement", head: head, body: body }) } 71 | function funExpr(id, args, body, gen) { return Pattern({ type: "FunctionExpression", 72 | id: id, 73 | params: args, 74 | defaults: [], 75 | body: body, 76 | rest: null, 77 | generator: false, 78 | expression: false 79 | }) } 80 | function genFunExpr(id, args, body) { return Pattern({ type: "FunctionExpression", 81 | id: id, 82 | params: args, 83 | defaults: [], 84 | body: body, 85 | rest: null, 86 | generator: true, 87 | expression: false 88 | }) } 89 | 90 | function unExpr(op, arg) { return Pattern({ type: "UnaryExpression", operator: op, argument: arg, prefix: true }) } 91 | function binExpr(op, left, right) { return Pattern({ type: "BinaryExpression", operator: op, left: left, right: right }) } 92 | function aExpr(op, left, right) { return Pattern({ type: "AssignmentExpression", operator: op, left: left, right: right }) } 93 | function updExpr(op, arg, prefix) { return Pattern({ type: "UpdateExpression", operator: op, argument: arg, prefix: prefix }) } 94 | function logExpr(op, left, right) { return Pattern({ type: "LogicalExpression", operator: op, left: left, right: right }) } 95 | 96 | function condExpr(test, cons, alt) { return Pattern({ type: "ConditionalExpression", test: test, consequent: cons, alternate: alt }) } 97 | function seqExpr(exprs) { return Pattern({ type: "SequenceExpression", expressions: exprs }) } 98 | function newExpr(callee, args) { return Pattern({ type: "NewExpression", callee: callee, arguments: args }) } 99 | function callExpr(callee, args) { return Pattern({ type: "CallExpression", callee: callee, arguments: args }) } 100 | function arrExpr(elts) { return Pattern({ type: "ArrayExpression", elements: elts }) } 101 | function objExpr(elts) { return Pattern({ type: "ObjectExpression", properties: elts }) } 102 | function objProp(key, value, kind) { return Pattern({ type: "Property", key: key, value: value, kind: kind, method: false, shorthand: false, computed: false }) } 103 | 104 | function arrPatt(elts) { return Pattern({ type: "ArrayPattern", elements: elts }) } 105 | function objPatt(elts) { return Pattern({ type: "ObjectPattern", properties: elts }) } 106 | 107 | function localSrc(src) { return "(function(){ " + src + " })" } 108 | function localPatt(patt) { return program([exprStmt(funExpr(null, [], blockStmt([patt])))]) } 109 | function blockSrc(src) { return "(function(){ { " + src + " } })" } 110 | function blockPatt(patt) { return program([exprStmt(funExpr(null, [], blockStmt([blockStmt([patt])])))]) } 111 | 112 | function assertBlockStmt(src, patt) { 113 | blockPatt(patt).assert(Reflect.parse(blockSrc(src))); 114 | } 115 | 116 | function assertBlockExpr(src, patt) { 117 | assertBlockStmt(src, exprStmt(patt)); 118 | } 119 | 120 | function assertBlockDecl(src, patt, builder) { 121 | blockPatt(patt).assert(Reflect.parse(blockSrc(src), {builder: builder})); 122 | } 123 | 124 | function assertLocalStmt(src, patt) { 125 | localPatt(patt).assert(Reflect.parse(localSrc(src))); 126 | } 127 | 128 | function assertLocalExpr(src, patt) { 129 | assertLocalStmt(src, exprStmt(patt)); 130 | } 131 | 132 | function assertLocalDecl(src, patt) { 133 | localPatt(patt).assert(Reflect.parse(localSrc(src))); 134 | } 135 | 136 | function assertGlobalStmt(src, patt, builder) { 137 | program([patt]).assert(Reflect.parse(src, {builder: builder})); 138 | } 139 | 140 | function assertGlobalExpr(src, patt, builder) { 141 | program([exprStmt(patt)]).assert(Reflect.parse(src, {builder: builder})); 142 | //assertStmt(src, exprStmt(patt)); 143 | } 144 | 145 | function assertGlobalDecl(src, patt) { 146 | program([patt]).assert(Reflect.parse(src)); 147 | } 148 | 149 | function assertProg(src, patt) { 150 | program(patt).assert(Reflect.parse(src)); 151 | } 152 | 153 | function assertStmt(src, patt) { 154 | assertLocalStmt(src, patt); 155 | assertGlobalStmt(src, patt); 156 | assertBlockStmt(src, patt); 157 | } 158 | 159 | function assertExpr(src, patt) { 160 | assertLocalExpr(src, patt); 161 | assertGlobalExpr(src, patt); 162 | assertBlockExpr(src, patt); 163 | } 164 | 165 | function assertDecl(src, patt) { 166 | assertLocalDecl(src, patt); 167 | assertGlobalDecl(src, patt); 168 | assertBlockDecl(src, patt); 169 | } 170 | 171 | function assertError(src, errorType) { 172 | try { 173 | Reflect.parse(src); 174 | } catch (e) { 175 | return; 176 | } 177 | throw new Error("expected " + errorType.name + " for " + uneval(src)); 178 | } 179 | 180 | 181 | // general tests 182 | 183 | // NB: These are useful but for now jit-test doesn't do I/O reliably. 184 | 185 | //program(_).assert(Reflect.parse(snarf('data/flapjax.txt'))); 186 | //program(_).assert(Reflect.parse(snarf('data/jquery-1.4.2.txt'))); 187 | //program(_).assert(Reflect.parse(snarf('data/prototype.js'))); 188 | //program(_).assert(Reflect.parse(snarf('data/dojo.js.uncompressed.js'))); 189 | //program(_).assert(Reflect.parse(snarf('data/mootools-1.2.4-core-nc.js'))); 190 | 191 | 192 | // declarations 193 | 194 | assertDecl("var x = 1, y = 2, z = 3", 195 | varDecl([declarator(ident("x"), lit(1)), 196 | declarator(ident("y"), lit(2)), 197 | declarator(ident("z"), lit(3))])); 198 | assertDecl("var x, y, z", 199 | varDecl([declarator(ident("x"), null), 200 | declarator(ident("y"), null), 201 | declarator(ident("z"), null)])); 202 | assertDecl("function foo() { }", 203 | funDecl(ident("foo"), [], blockStmt([]))); 204 | assertDecl("function foo() { return 42 }", 205 | funDecl(ident("foo"), [], blockStmt([returnStmt(lit(42))]))); 206 | 207 | 208 | // Bug 591437: rebound args have their defs turned into uses 209 | assertDecl("function f(a) { function a() { } }", 210 | funDecl(ident("f"), [ident("a")], blockStmt([funDecl(ident("a"), [], blockStmt([]))]))); 211 | assertDecl("function f(a,b,c) { function b() { } }", 212 | funDecl(ident("f"), [ident("a"),ident("b"),ident("c")], blockStmt([funDecl(ident("b"), [], blockStmt([]))]))); 213 | 214 | // expressions 215 | 216 | assertExpr("true", lit(true)); 217 | assertExpr("false", lit(false)); 218 | assertExpr("42", lit(42)); 219 | assertExpr("(/asdf/)", lit(/asdf/)); 220 | assertExpr("this", thisExpr); 221 | assertExpr("foo", ident("foo")); 222 | assertExpr("foo.bar", dotExpr(ident("foo"), ident("bar"))); 223 | assertExpr("foo[bar]", memExpr(ident("foo"), ident("bar"))); 224 | assertExpr("(function(){})", funExpr(null, [], blockStmt([]))); 225 | assertExpr("(function f() {})", funExpr(ident("f"), [], blockStmt([]))); 226 | assertExpr("(function f(x,y,z) {})", funExpr(ident("f"), [ident("x"),ident("y"),ident("z")], blockStmt([]))); 227 | assertExpr("(++x)", updExpr("++", ident("x"), true)); 228 | assertExpr("(x++)", updExpr("++", ident("x"), false)); 229 | assertExpr("(+x)", unExpr("+", ident("x"))); 230 | assertExpr("(-x)", unExpr("-", ident("x"))); 231 | assertExpr("(!x)", unExpr("!", ident("x"))); 232 | assertExpr("(~x)", unExpr("~", ident("x"))); 233 | assertExpr("(delete x)", unExpr("delete", ident("x"))); 234 | assertExpr("(typeof x)", unExpr("typeof", ident("x"))); 235 | assertExpr("(void x)", unExpr("void", ident("x"))); 236 | assertExpr("(x == y)", binExpr("==", ident("x"), ident("y"))); 237 | assertExpr("(x != y)", binExpr("!=", ident("x"), ident("y"))); 238 | assertExpr("(x === y)", binExpr("===", ident("x"), ident("y"))); 239 | assertExpr("(x !== y)", binExpr("!==", ident("x"), ident("y"))); 240 | assertExpr("(x < y)", binExpr("<", ident("x"), ident("y"))); 241 | assertExpr("(x <= y)", binExpr("<=", ident("x"), ident("y"))); 242 | assertExpr("(x > y)", binExpr(">", ident("x"), ident("y"))); 243 | assertExpr("(x >= y)", binExpr(">=", ident("x"), ident("y"))); 244 | assertExpr("(x << y)", binExpr("<<", ident("x"), ident("y"))); 245 | assertExpr("(x >> y)", binExpr(">>", ident("x"), ident("y"))); 246 | assertExpr("(x >>> y)", binExpr(">>>", ident("x"), ident("y"))); 247 | assertExpr("(x + y)", binExpr("+", ident("x"), ident("y"))); 248 | assertExpr("(w + x + y + z)", binExpr("+", binExpr("+", binExpr("+", ident("w"), ident("x")), ident("y")), ident("z"))); 249 | assertExpr("(x - y)", binExpr("-", ident("x"), ident("y"))); 250 | assertExpr("(w - x - y - z)", binExpr("-", binExpr("-", binExpr("-", ident("w"), ident("x")), ident("y")), ident("z"))); 251 | assertExpr("(x * y)", binExpr("*", ident("x"), ident("y"))); 252 | assertExpr("(x / y)", binExpr("/", ident("x"), ident("y"))); 253 | assertExpr("(x % y)", binExpr("%", ident("x"), ident("y"))); 254 | assertExpr("(x | y)", binExpr("|", ident("x"), ident("y"))); 255 | assertExpr("(x ^ y)", binExpr("^", ident("x"), ident("y"))); 256 | assertExpr("(x & y)", binExpr("&", ident("x"), ident("y"))); 257 | assertExpr("(x in y)", binExpr("in", ident("x"), ident("y"))); 258 | assertExpr("(x instanceof y)", binExpr("instanceof", ident("x"), ident("y"))); 259 | assertExpr("(x = y)", aExpr("=", ident("x"), ident("y"))); 260 | assertExpr("(x += y)", aExpr("+=", ident("x"), ident("y"))); 261 | assertExpr("(x -= y)", aExpr("-=", ident("x"), ident("y"))); 262 | assertExpr("(x *= y)", aExpr("*=", ident("x"), ident("y"))); 263 | assertExpr("(x /= y)", aExpr("/=", ident("x"), ident("y"))); 264 | assertExpr("(x %= y)", aExpr("%=", ident("x"), ident("y"))); 265 | assertExpr("(x <<= y)", aExpr("<<=", ident("x"), ident("y"))); 266 | assertExpr("(x >>= y)", aExpr(">>=", ident("x"), ident("y"))); 267 | assertExpr("(x >>>= y)", aExpr(">>>=", ident("x"), ident("y"))); 268 | assertExpr("(x |= y)", aExpr("|=", ident("x"), ident("y"))); 269 | assertExpr("(x ^= y)", aExpr("^=", ident("x"), ident("y"))); 270 | assertExpr("(x &= y)", aExpr("&=", ident("x"), ident("y"))); 271 | assertExpr("(x || y)", logExpr("||", ident("x"), ident("y"))); 272 | assertExpr("(x && y)", logExpr("&&", ident("x"), ident("y"))); 273 | assertExpr("(w || x || y || z)", logExpr("||", logExpr("||", logExpr("||", ident("w"), ident("x")), ident("y")), ident("z"))) 274 | assertExpr("(x ? y : z)", condExpr(ident("x"), ident("y"), ident("z"))); 275 | assertExpr("(x,y)", seqExpr([ident("x"),ident("y")])) 276 | assertExpr("(x,y,z)", seqExpr([ident("x"),ident("y"),ident("z")])) 277 | assertExpr("(a,b,c,d,e,f,g)", seqExpr([ident("a"),ident("b"),ident("c"),ident("d"),ident("e"),ident("f"),ident("g")])); 278 | assertExpr("(new Object)", newExpr(ident("Object"), [])); 279 | assertExpr("(new Object())", newExpr(ident("Object"), [])); 280 | assertExpr("(new Object(42))", newExpr(ident("Object"), [lit(42)])); 281 | assertExpr("(new Object(1,2,3))", newExpr(ident("Object"), [lit(1),lit(2),lit(3)])); 282 | assertExpr("(String())", callExpr(ident("String"), [])); 283 | assertExpr("(String(42))", callExpr(ident("String"), [lit(42)])); 284 | assertExpr("(String(1,2,3))", callExpr(ident("String"), [lit(1),lit(2),lit(3)])); 285 | assertExpr("[]", arrExpr([])); 286 | assertExpr("[1]", arrExpr([lit(1)])); 287 | assertExpr("[1,2]", arrExpr([lit(1),lit(2)])); 288 | assertExpr("[1,2,3]", arrExpr([lit(1),lit(2),lit(3)])); 289 | assertExpr("[1,,2,3]", arrExpr([lit(1),,lit(2),lit(3)])); 290 | assertExpr("[1,,,2,3]", arrExpr([lit(1),,,lit(2),lit(3)])); 291 | assertExpr("[1,,,2,,3]", arrExpr([lit(1),,,lit(2),,lit(3)])); 292 | assertExpr("[1,,,2,,,3]", arrExpr([lit(1),,,lit(2),,,lit(3)])); 293 | assertExpr("[,1,2,3]", arrExpr([,lit(1),lit(2),lit(3)])); 294 | assertExpr("[,,1,2,3]", arrExpr([,,lit(1),lit(2),lit(3)])); 295 | assertExpr("[,,,1,2,3]", arrExpr([,,,lit(1),lit(2),lit(3)])); 296 | assertExpr("[,,,1,2,3,]", arrExpr([,,,lit(1),lit(2),lit(3)])); 297 | assertExpr("[,,,1,2,3,,]", arrExpr([,,,lit(1),lit(2),lit(3),undefined])); 298 | assertExpr("[,,,1,2,3,,,]", arrExpr([,,,lit(1),lit(2),lit(3),undefined,undefined])); 299 | assertExpr("[,,,,,]", arrExpr([undefined,undefined,undefined,undefined,undefined])); 300 | assertExpr("({})", objExpr([])); 301 | assertExpr("({x:1})", objExpr([objProp(ident("x"), lit(1), "init")])); 302 | assertExpr("({x:1, y:2})", objExpr([objProp(ident("x"), lit(1), "init"), 303 | objProp(ident("y"), lit(2), "init")])); 304 | assertExpr("({x:1, y:2, z:3})", objExpr([objProp(ident("x"), lit(1), "init"), 305 | objProp(ident("y"), lit(2), "init"), 306 | objProp(ident("z"), lit(3), "init") ])); 307 | assertExpr("({x:1, 'y':2, z:3})", objExpr([objProp(ident("x"), lit(1), "init"), 308 | objProp(lit("y"), lit(2), "init"), 309 | objProp(ident("z"), lit(3), "init") ])); 310 | assertExpr("({'x':1, 'y':2, z:3})", objExpr([objProp(lit("x"), lit(1), "init"), 311 | objProp(lit("y"), lit(2), "init"), 312 | objProp(ident("z"), lit(3), "init") ])); 313 | assertExpr("({'x':1, 'y':2, 3:3})", objExpr([objProp(lit("x"), lit(1), "init"), 314 | objProp(lit("y"), lit(2), "init"), 315 | objProp(lit(3), lit(3), "init") ])); 316 | 317 | // Bug 571617: eliminate constant-folding 318 | assertExpr("2 + 3", binExpr("+", lit(2), lit(3))); 319 | 320 | // Bug 632026: constant-folding 321 | assertExpr("typeof(0?0:a)", unExpr("typeof", condExpr(lit(0), lit(0), ident("a")))); 322 | 323 | // Bug 632056: constant-folding 324 | program([exprStmt(ident("f")), 325 | ifStmt(lit(1), 326 | funDecl(ident("f"), [], blockStmt([])), 327 | null)]).assert(Reflect.parse("f; if (1) function f(){}")); 328 | 329 | // statements 330 | 331 | assertStmt("throw 42", throwStmt(lit(42))); 332 | assertStmt("for (;;) break", forStmt(null, null, null, breakStmt(null))); 333 | assertStmt("for (x; y; z) break", forStmt(ident("x"), ident("y"), ident("z"), breakStmt(null))); 334 | assertStmt("for (var x; y; z) break", forStmt(varDecl([declarator(ident("x"), null)]), ident("y"), ident("z"), breakStmt(null))); 335 | assertStmt("for (var x = 42; y; z) break", forStmt(varDecl([declarator(ident("x"), lit(42))]), ident("y"), ident("z"), breakStmt(null))); 336 | assertStmt("for (x; ; z) break", forStmt(ident("x"), null, ident("z"), breakStmt(null))); 337 | assertStmt("for (var x; ; z) break", forStmt(varDecl([declarator(ident("x"), null)]), null, ident("z"), breakStmt(null))); 338 | assertStmt("for (var x = 42; ; z) break", forStmt(varDecl([declarator(ident("x"), lit(42))]), null, ident("z"), breakStmt(null))); 339 | assertStmt("for (x; y; ) break", forStmt(ident("x"), ident("y"), null, breakStmt(null))); 340 | assertStmt("for (var x; y; ) break", forStmt(varDecl([declarator(ident("x"), null)]), ident("y"), null, breakStmt(null))); 341 | assertStmt("for (var x = 42; y; ) break", forStmt(varDecl([declarator(ident("x"),lit(42))]), ident("y"), null, breakStmt(null))); 342 | assertStmt("for (var x in y) break", forInStmt(varDecl([declarator(ident("x"),null)]), ident("y"), breakStmt(null))); 343 | assertStmt("for (x in y) break", forInStmt(ident("x"), ident("y"), breakStmt(null))); 344 | assertStmt("{ }", blockStmt([])); 345 | assertStmt("{ throw 1; throw 2; throw 3; }", blockStmt([ throwStmt(lit(1)), throwStmt(lit(2)), throwStmt(lit(3))])); 346 | assertStmt(";", emptyStmt); 347 | assertStmt("if (foo) throw 42;", ifStmt(ident("foo"), throwStmt(lit(42)), null)); 348 | assertStmt("if (foo) throw 42; else true;", ifStmt(ident("foo"), throwStmt(lit(42)), exprStmt(lit(true)))); 349 | assertStmt("if (foo) { throw 1; throw 2; throw 3; }", 350 | ifStmt(ident("foo"), 351 | blockStmt([throwStmt(lit(1)), throwStmt(lit(2)), throwStmt(lit(3))]), 352 | null)); 353 | assertStmt("if (foo) { throw 1; throw 2; throw 3; } else true;", 354 | ifStmt(ident("foo"), 355 | blockStmt([throwStmt(lit(1)), throwStmt(lit(2)), throwStmt(lit(3))]), 356 | exprStmt(lit(true)))); 357 | assertStmt("foo: for(;;) break foo;", labStmt(ident("foo"), forStmt(null, null, null, breakStmt(ident("foo"))))); 358 | assertStmt("foo: for(;;) continue foo;", labStmt(ident("foo"), forStmt(null, null, null, continueStmt(ident("foo"))))); 359 | assertStmt("with (obj) { }", withStmt(ident("obj"), blockStmt([]))); 360 | assertStmt("with (obj) { obj; }", withStmt(ident("obj"), blockStmt([exprStmt(ident("obj"))]))); 361 | assertStmt("while (foo) { }", whileStmt(ident("foo"), blockStmt([]))); 362 | assertStmt("while (foo) { foo; }", whileStmt(ident("foo"), blockStmt([exprStmt(ident("foo"))]))); 363 | assertStmt("do { } while (foo);", doStmt(blockStmt([]), ident("foo"))); 364 | assertStmt("do { foo; } while (foo)", doStmt(blockStmt([exprStmt(ident("foo"))]), ident("foo"))); 365 | assertStmt("switch (foo) { case 1: 1; break; case 2: 2; break; default: 3; }", 366 | switchStmt(ident("foo"), 367 | [ caseClause(lit(1), [ exprStmt(lit(1)), breakStmt(null) ]), 368 | caseClause(lit(2), [ exprStmt(lit(2)), breakStmt(null) ]), 369 | defaultClause([ exprStmt(lit(3)) ]) ])); 370 | assertStmt("switch (foo) { case 1: 1; break; case 2: 2; break; default: 3; case 42: 42; }", 371 | switchStmt(ident("foo"), 372 | [ caseClause(lit(1), [ exprStmt(lit(1)), breakStmt(null) ]), 373 | caseClause(lit(2), [ exprStmt(lit(2)), breakStmt(null) ]), 374 | defaultClause([ exprStmt(lit(3)) ]), 375 | caseClause(lit(42), [ exprStmt(lit(42)) ]) ])); 376 | assertStmt("try { } catch (e) { }", 377 | tryStmt(blockStmt([]), 378 | [], 379 | [ catchClause(ident("e"), null, blockStmt([])) ], 380 | null)); 381 | assertStmt("try { } catch (e) { } finally { }", 382 | tryStmt(blockStmt([]), 383 | [], 384 | [ catchClause(ident("e"), null, blockStmt([])) ], 385 | blockStmt([]))); 386 | assertStmt("try { } finally { }", 387 | tryStmt(blockStmt([]), 388 | [], 389 | [], 390 | blockStmt([]))); 391 | 392 | // redeclarations (TOK_NAME nodes with lexdef) 393 | 394 | assertStmt("function f() { function g() { } function g() { } }", 395 | funDecl(ident("f"), [], blockStmt([funDecl(ident("g"), [], blockStmt([])), 396 | funDecl(ident("g"), [], blockStmt([]))]))); 397 | 398 | assertStmt("function f() { function g() { } function g() { return 42 } }", 399 | funDecl(ident("f"), [], blockStmt([funDecl(ident("g"), [], blockStmt([])), 400 | funDecl(ident("g"), [], blockStmt([returnStmt(lit(42))]))]))); 401 | 402 | assertStmt("function f() { var x = 42; var x = 43; }", 403 | funDecl(ident("f"), [], blockStmt([varDecl([declarator(ident("x"),lit(42))]), 404 | varDecl([declarator(ident("x"),lit(43))])]))); 405 | 406 | // getters and setters 407 | 408 | assertExpr("({ get x() { return 42 } })", 409 | objExpr([ objProp(ident("x"), 410 | funExpr(null, [], blockStmt([returnStmt(lit(42))])), 411 | "get" ) ])); 412 | assertExpr("({ set x(v) { return 42 } })", 413 | objExpr([ objProp(ident("x"), 414 | funExpr(null, [ident("v")], blockStmt([returnStmt(lit(42))])), 415 | "set" ) ])); 416 | 417 | } 418 | 419 | exports.testReflect = testReflect; 420 | 421 | }(typeof exports === 'undefined' ? this : exports)); 422 | -------------------------------------------------------------------------------- /test/run.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2012 Yusuke Suzuki 3 | Copyright (C) 2012 Ariya Hidayat 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 18 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | /*jslint node:true */ 27 | 28 | (function () { 29 | 'use strict'; 30 | 31 | var child = require('child_process'), 32 | nodejs = '"' + process.execPath + '"', 33 | ret = 0, 34 | suites, 35 | index; 36 | 37 | suites = [ 38 | 'runner', 39 | 'compat' 40 | ]; 41 | 42 | function nextTest() { 43 | var suite = suites[index]; 44 | 45 | if (index < suites.length) { 46 | child.exec(nodejs + ' ./test/' + suite + '.js', function (err, stdout, stderr) { 47 | if (stdout) { 48 | process.stdout.write(suite + ': ' + stdout); 49 | } 50 | if (stderr) { 51 | process.stderr.write(suite + ': ' + stderr); 52 | } 53 | if (err) { 54 | ret = err.code; 55 | } 56 | index += 1; 57 | nextTest(); 58 | }); 59 | } else { 60 | process.exit(ret); 61 | } 62 | } 63 | 64 | index = 0; 65 | nextTest(); 66 | }()); 67 | -------------------------------------------------------------------------------- /test/runner.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2012 Ariya Hidayat 3 | Copyright (C) 2012 Joost-Wim Boekesteijn 4 | Copyright (C) 2012 Yusuke Suzuki 5 | Copyright (C) 2012 Arpad Borsos 6 | Copyright (C) 2011 Ariya Hidayat 7 | Copyright (C) 2011 Yusuke Suzuki 8 | Copyright (C) 2011 Arpad Borsos 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright 14 | notice, this list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright 16 | notice, this list of conditions and the following disclaimer in the 17 | documentation and/or other materials provided with the distribution. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | /*jslint browser:true node:true */ 32 | /*global esprima:true, testFixture:true */ 33 | 34 | var runTests; 35 | 36 | // Special handling for regular expression literals: remove their `value` 37 | // property since it may be `null` if it represents a regular expression 38 | // that is not supported in the current environment. The `regex` property 39 | // will be compared instead. 40 | function adjustRegexLiteral(key, value) { 41 | 'use strict'; 42 | if (key === 'value' && value instanceof RegExp) { 43 | value = value.toString(); 44 | } 45 | return value; 46 | } 47 | 48 | function NotMatchingError(expected, actual) { 49 | 'use strict'; 50 | Error.call(this, 'Expected '); 51 | this.expected = expected; 52 | this.actual = actual; 53 | } 54 | NotMatchingError.prototype = new Error(); 55 | 56 | function errorToObject(e) { 57 | 'use strict'; 58 | var msg = e.toString(); 59 | 60 | // Opera 9.64 produces an non-standard string in toString(). 61 | if (msg.substr(0, 6) !== 'Error:') { 62 | if (typeof e.message === 'string') { 63 | msg = 'Error: ' + e.message; 64 | } 65 | } 66 | 67 | return { 68 | index: e.index, 69 | lineNumber: e.lineNumber, 70 | column: e.column, 71 | message: msg 72 | }; 73 | } 74 | 75 | function needLoc(syntax) { 76 | var need = true; 77 | if (typeof syntax.tokens !== 'undefined' && syntax.tokens.length > 0) { 78 | need = (typeof syntax.tokens[0].loc !== 'undefined'); 79 | } 80 | if (typeof syntax.comments !== 'undefined' && syntax.comments.length > 0) { 81 | need = (typeof syntax.comments[0].loc !== 'undefined'); 82 | } 83 | return need; 84 | } 85 | 86 | function needRange(syntax) { 87 | var need = true; 88 | if (typeof syntax.tokens !== 'undefined' && syntax.tokens.length > 0) { 89 | need = (typeof syntax.tokens[0].range !== 'undefined'); 90 | } 91 | if (typeof syntax.comments !== 'undefined' && syntax.comments.length > 0) { 92 | need = (typeof syntax.comments[0].range !== 'undefined'); 93 | } 94 | return need; 95 | } 96 | 97 | function hasAttachedComment(syntax) { 98 | var key; 99 | for (key in syntax) { 100 | if (key === 'leadingComments' || key === 'trailingComments') { 101 | return true; 102 | } 103 | if (syntax[key] && typeof syntax[key] === 'object') { 104 | if (hasAttachedComment(syntax[key])) { 105 | return true; 106 | } 107 | } 108 | } 109 | return false; 110 | } 111 | 112 | function testParse(esprima, code, syntax, testOptions) { 113 | 'use strict'; 114 | var expected, tree, actual, options, StringObject, i, len, err; 115 | 116 | // alias, so that JSLint does not complain. 117 | StringObject = String; 118 | 119 | options = { 120 | comment: (typeof syntax.comments !== 'undefined'), 121 | range: needRange(syntax), 122 | loc: needLoc(syntax), 123 | tokens: (typeof syntax.tokens !== 'undefined'), 124 | raw: true, 125 | tolerant: (typeof syntax.errors !== 'undefined'), 126 | source: null, 127 | sourceType: testOptions.sourceType 128 | }; 129 | 130 | if (options.comment) { 131 | options.attachComment = hasAttachedComment(syntax); 132 | } 133 | 134 | if (options.loc) { 135 | options.source = syntax.loc.source; 136 | } 137 | 138 | expected = JSON.stringify(syntax, adjustRegexLiteral, 4); 139 | try { 140 | tree = esprima.parse(code, options); 141 | tree = (options.comment || options.tokens || options.tolerant) ? tree : tree.body[0]; 142 | 143 | if (options.tolerant) { 144 | for (i = 0, len = tree.errors.length; i < len; i += 1) { 145 | tree.errors[i] = errorToObject(tree.errors[i]); 146 | } 147 | } 148 | 149 | actual = JSON.stringify(tree, adjustRegexLiteral, 4); 150 | 151 | // Only to ensure that there is no error when using string object. 152 | esprima.parse(new StringObject(code), options); 153 | 154 | } catch (e) { 155 | throw new NotMatchingError(expected, e.toString()); 156 | } 157 | if (expected !== actual) { 158 | throw new NotMatchingError(expected, actual); 159 | } 160 | 161 | function filter(key, value) { 162 | if (key === 'value' && value instanceof RegExp) { 163 | value = value.toString(); 164 | } 165 | return (key === 'loc' || key === 'range') ? undefined : value; 166 | } 167 | 168 | if (options.tolerant) { 169 | return; 170 | } 171 | 172 | 173 | // Check again without any location info. 174 | options.range = false; 175 | options.loc = false; 176 | expected = JSON.stringify(syntax, filter, 4); 177 | try { 178 | tree = esprima.parse(code, options); 179 | tree = (options.comment || options.tokens) ? tree : tree.body[0]; 180 | 181 | if (options.tolerant) { 182 | for (i = 0, len = tree.errors.length; i < len; i += 1) { 183 | tree.errors[i] = errorToObject(tree.errors[i]); 184 | } 185 | } 186 | 187 | actual = JSON.stringify(tree, filter, 4); 188 | } catch (e) { 189 | throw new NotMatchingError(expected, e.toString()); 190 | } 191 | if (expected !== actual) { 192 | throw new NotMatchingError(expected, actual); 193 | } 194 | } 195 | 196 | function mustHaveLocRange(testName, node, needLoc, needRange, stack) { 197 | var error; 198 | if (node.hasOwnProperty('type')) { 199 | if (needLoc && !node.loc) { 200 | error = "doesn't have 'loc' property"; 201 | } 202 | if (needRange && !node.range) { 203 | error = "doesn't have 'range' property"; 204 | } 205 | if (error) { 206 | stack = stack.length ? ' at [' + stack.join('][') + ']' : ''; 207 | throw new Error("Test '" + testName + "'" + stack + " (type = " + node.type + ") " + error); 208 | } 209 | } 210 | for (i in node) { 211 | if (node.hasOwnProperty(i) && node[i] !== null && typeof node[i] === 'object') { 212 | stack.push(i); 213 | mustHaveLocRange(testName, node[i], needLoc, needRange, stack); 214 | stack.pop(); 215 | } 216 | } 217 | } 218 | 219 | function testTokenize(esprima, code, tokens, testOptions) { 220 | 'use strict'; 221 | var options, expected, actual, tree; 222 | 223 | options = { 224 | comment: true, 225 | tolerant: true, 226 | loc: true, 227 | range: true, 228 | sourceType: testOptions.sourceType 229 | }; 230 | 231 | expected = JSON.stringify(tokens, null, 4); 232 | 233 | try { 234 | tree = esprima.tokenize(code, options); 235 | actual = JSON.stringify(tree, null, 4); 236 | } catch (e) { 237 | throw new NotMatchingError(expected, e.toString()); 238 | } 239 | if (expected !== actual) { 240 | throw new NotMatchingError(expected, actual); 241 | } 242 | } 243 | 244 | function testError(esprima, code, exception, testOptions) { 245 | 'use strict'; 246 | var i, options, expected, actual, err, handleInvalidRegexFlag, tokenize, 247 | sourceType; 248 | 249 | // Different parsing options should give the same error. 250 | options = [ 251 | { sourceType: testOptions.sourceType }, 252 | { sourceType: testOptions.sourceType, comment: true }, 253 | { sourceType: testOptions.sourceType, raw: true }, 254 | { sourceType: testOptions.sourceType, raw: true, comment: true } 255 | ]; 256 | 257 | // If handleInvalidRegexFlag is true, an invalid flag in a regular expression 258 | // will throw an exception. In some old version of V8, this is not the case 259 | // and hence handleInvalidRegexFlag is false. 260 | handleInvalidRegexFlag = false; 261 | try { 262 | 'test'.match(new RegExp('[a-z]', 'x')); 263 | } catch (e) { 264 | handleInvalidRegexFlag = true; 265 | } 266 | 267 | exception.description = exception.message.replace(/Error: Line [0-9]+: /, ''); 268 | 269 | if (exception.tokenize) { 270 | tokenize = true; 271 | exception.tokenize = undefined; 272 | } 273 | expected = JSON.stringify(exception); 274 | 275 | for (i = 0; i < options.length; i += 1) { 276 | 277 | try { 278 | if (tokenize) { 279 | esprima.tokenize(code, options[i]) 280 | } else { 281 | esprima.parse(code, options[i]); 282 | } 283 | } catch (e) { 284 | err = errorToObject(e); 285 | err.description = e.description; 286 | actual = JSON.stringify(err); 287 | } 288 | 289 | if (expected !== actual) { 290 | 291 | // Compensate for old V8 which does not handle invalid flag. 292 | if (exception.message.indexOf('Invalid regular expression') > 0) { 293 | if (typeof actual === 'undefined' && !handleInvalidRegexFlag) { 294 | return; 295 | } 296 | } 297 | 298 | throw new NotMatchingError(expected, actual); 299 | } 300 | 301 | } 302 | } 303 | 304 | function testAPI(esprima, code, result) { 305 | 'use strict'; 306 | var expected, res, actual; 307 | 308 | expected = JSON.stringify(result.result, null, 4); 309 | try { 310 | if (typeof result.property !== 'undefined') { 311 | res = esprima[result.property]; 312 | } else { 313 | res = esprima[result.call].apply(esprima, result.args); 314 | } 315 | actual = JSON.stringify(res, adjustRegexLiteral, 4); 316 | } catch (e) { 317 | throw new NotMatchingError(expected, e.toString()); 318 | } 319 | if (expected !== actual) { 320 | throw new NotMatchingError(expected, actual); 321 | } 322 | } 323 | 324 | function runTest(esprima, code, result, options) { 325 | 'use strict'; 326 | if (result.hasOwnProperty('lineNumber')) { 327 | testError(esprima, code, result, options); 328 | } else if (result.hasOwnProperty('result')) { 329 | testAPI(esprima, code, result); 330 | } else if (result instanceof Array) { 331 | testTokenize(esprima, code, result, options); 332 | } else { 333 | testParse(esprima, code, result, options); 334 | } 335 | } 336 | 337 | if (typeof window !== 'undefined') { 338 | // Run all tests in a browser environment. 339 | runTests = function () { 340 | 'use strict'; 341 | var total = 0, 342 | failures = 0, 343 | category, 344 | fixture, 345 | source, 346 | tick, 347 | expected, 348 | index, 349 | len; 350 | 351 | function setText(el, str) { 352 | if (typeof el.innerText === 'string') { 353 | el.innerText = str; 354 | } else { 355 | el.textContent = str; 356 | } 357 | } 358 | 359 | function startCategory(category) { 360 | var report, e; 361 | report = document.getElementById('report'); 362 | e = document.createElement('h4'); 363 | setText(e, category); 364 | report.appendChild(e); 365 | } 366 | 367 | function reportSuccess(code) { 368 | var report, e; 369 | report = document.getElementById('report'); 370 | e = document.createElement('pre'); 371 | e.setAttribute('class', 'code'); 372 | setText(e, code); 373 | report.appendChild(e); 374 | } 375 | 376 | function reportFailure(code, expected, actual) { 377 | var report, e; 378 | 379 | report = document.getElementById('report'); 380 | 381 | e = document.createElement('p'); 382 | setText(e, 'Code:'); 383 | report.appendChild(e); 384 | 385 | e = document.createElement('pre'); 386 | e.setAttribute('class', 'code'); 387 | setText(e, code); 388 | report.appendChild(e); 389 | 390 | e = document.createElement('p'); 391 | setText(e, 'Expected'); 392 | report.appendChild(e); 393 | 394 | e = document.createElement('pre'); 395 | e.setAttribute('class', 'expected'); 396 | setText(e, expected); 397 | report.appendChild(e); 398 | 399 | e = document.createElement('p'); 400 | setText(e, 'Actual'); 401 | report.appendChild(e); 402 | 403 | e = document.createElement('pre'); 404 | e.setAttribute('class', 'actual'); 405 | setText(e, actual); 406 | report.appendChild(e); 407 | } 408 | 409 | setText(document.getElementById('version'), esprima.version); 410 | 411 | tick = new Date(); 412 | for (category in testFixture) { 413 | if (testFixture.hasOwnProperty(category)) { 414 | var categoryOptions = testFixtureOptions[category] || {}; 415 | startCategory(category); 416 | fixture = testFixture[category]; 417 | for (source in fixture) { 418 | if (fixture.hasOwnProperty(source)) { 419 | var sourceOptions = 420 | categoryOptions.hasOwnProperty(source) 421 | ? categoryOptions[source] 422 | : categoryOptions; 423 | 424 | expected = fixture[source]; 425 | total += 1; 426 | try { 427 | 428 | runTest(esprima, source, expected, sourceOptions); 429 | reportSuccess(source, JSON.stringify(expected, null, 4)); 430 | } catch (e) { 431 | failures += 1; 432 | reportFailure(source, e.expected, e.actual); 433 | } 434 | } 435 | } 436 | } 437 | } 438 | tick = (new Date()) - tick; 439 | 440 | if (failures > 0) { 441 | document.getElementById('status').className = 'alert-box alert'; 442 | setText(document.getElementById('status'), total + ' tests. ' + 443 | 'Failures: ' + failures + '. ' + tick + ' ms.'); 444 | } else { 445 | document.getElementById('status').className = 'alert-box success'; 446 | setText(document.getElementById('status'), total + ' tests. ' + 447 | 'No failure. ' + tick + ' ms.'); 448 | } 449 | }; 450 | } else { 451 | (function () { 452 | 'use strict'; 453 | 454 | var esprima = require('../esprima'), 455 | vm = require('vm'), 456 | fs = require('fs'), 457 | diff = require('json-diff').diffString, 458 | total = 0, 459 | failures = [], 460 | tick = new Date(), 461 | expected, 462 | header; 463 | 464 | vm.runInThisContext(fs.readFileSync(__dirname + '/test.js', 'utf-8')); 465 | vm.runInThisContext(fs.readFileSync(__dirname + '/harmonytest.js', 'utf-8')); 466 | vm.runInThisContext(fs.readFileSync(__dirname + '/fbtest.rec.js', 'utf-8')); 467 | vm.runInThisContext(fs.readFileSync(__dirname + '/harmonymodulestest.js', 'utf-8')); 468 | 469 | Object.keys(testFixture).forEach(function (category) { 470 | var categoryOptions = testFixtureOptions[category] || {}; 471 | Object.keys(testFixture[category]).forEach(function (source) { 472 | var sourceOptions = 473 | categoryOptions.hasOwnProperty(source) 474 | ? categoryOptions[source] 475 | : categoryOptions; 476 | total += 1; 477 | expected = testFixture[category][source]; 478 | if (!expected.hasOwnProperty('lineNumber') && !expected.hasOwnProperty('result')) { 479 | mustHaveLocRange(source, expected, needLoc(expected), needRange(expected), []); 480 | } 481 | try { 482 | runTest(esprima, source, expected, sourceOptions); 483 | } catch (e) { 484 | e.source = source; 485 | failures.push(e); 486 | } 487 | }); 488 | }); 489 | tick = (new Date()) - tick; 490 | 491 | header = total + ' tests. ' + failures.length + ' failures. ' + 492 | tick + ' ms'; 493 | if (failures.length) { 494 | console.error(header); 495 | failures.forEach(function (failure) { 496 | try { 497 | var expectedObject = JSON.parse(failure.expected); 498 | var actualObject = JSON.parse(failure.actual); 499 | 500 | console.error(failure.source + ': Expected\n ' + 501 | failure.expected.split('\n').join('\n ') + 502 | '\nto match\n ' + failure.actual + '\nDiff:\n' + 503 | diff(expectedObject, actualObject)); 504 | } catch (ex) { 505 | console.error(failure.source + ': Expected\n ' + 506 | failure.expected.split('\n').join('\n ') + 507 | '\nto match\n ' + failure.actual); 508 | } 509 | }); 510 | } else { 511 | console.log(header); 512 | } 513 | process.exit(failures.length === 0 ? 0 : 1); 514 | }()); 515 | } 516 | -------------------------------------------------------------------------------- /tools/check-version.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | var fs = require('fs'); 6 | 7 | function findCanonicalVersion() { 8 | var matcher, lines, version; 9 | 10 | matcher = /exports\.version\s+=\s+\'([0-9\.\-a-zA-Z]+)\'/; 11 | lines = fs.readFileSync('esprima.js', 'utf-8').split('\n'); 12 | lines.forEach(function (line) { 13 | if (matcher.test(line)) { 14 | version = matcher.exec(line)[1]; 15 | } 16 | }); 17 | 18 | return version; 19 | } 20 | 21 | function ensureVersion(manifestFile, expectedVersion) { 22 | var matcher, lines, version; 23 | 24 | console.log('Checking', manifestFile, '...'); 25 | matcher = /"version"\s*\:\s*"([0-9\.\-a-zA-Z]+)"/; 26 | lines = fs.readFileSync(manifestFile, 'utf-8').split('\n'); 27 | lines.forEach(function (line) { 28 | if (matcher.test(line)) { 29 | version = matcher.exec(line)[1]; 30 | } 31 | }); 32 | if (expectedVersion !== version) { 33 | console.log('ERROR: Wrong version for', manifestFile); 34 | console.log('Expected:', expectedVersion); 35 | console.log(' Actual:', version); 36 | process.exit(1); 37 | } 38 | } 39 | 40 | function checkVersion() { 41 | var version; 42 | 43 | console.log('Getting the canonical library version...'); 44 | version = findCanonicalVersion(); 45 | if (typeof version !== 'string') { 46 | console.log('ERROR: Can not get version number!', typeof version); 47 | process.exit(1); 48 | } 49 | console.log('Library version is', version); 50 | 51 | ensureVersion('package.json', version); 52 | ensureVersion('component.json', version); 53 | } 54 | 55 | 56 | checkVersion(); 57 | -------------------------------------------------------------------------------- /tools/generate-fbtest.js: -------------------------------------------------------------------------------- 1 | var tests = require("../test/fbtest.js"); 2 | var stringify = require("./stringify"); 3 | var esprima = require("../esprima.js"); 4 | var fs = require("fs"); 5 | var numTests = 0; 6 | 7 | var out = "/*\n" 8 | out += "* WARNING: This file is autogenerated by tools/generate-fbtest.js\n"; 9 | out += "* Do NOT modify this file directly! Instead, add your tests to\n"; 10 | out += "* tests/fbtest.js and run tools/generate-fbtest.js\n"; 11 | out += "*/\n\n"; 12 | 13 | for (section in tests) { 14 | if (tests.hasOwnProperty(section)) { 15 | numTests += tests[section].length; 16 | } 17 | } 18 | 19 | out += "var numTests = " + numTests + "\n"; 20 | 21 | var options = { 22 | comment: false, 23 | range: true, 24 | loc: true, 25 | sourceType: 'module', 26 | tokens: false, 27 | raw: true, 28 | tolerant: false 29 | }; 30 | 31 | function escape_content(content) { 32 | return content 33 | .replace(/[\\]/g, '\\\\') 34 | .replace(/[\b]/g, '\\b') 35 | .replace(/[\f]/g, '\\f') 36 | .replace(/[\n]/g, '\\n') 37 | .replace(/[\r]/g, '\\r') 38 | .replace(/[\t]/g, '\\t') 39 | .replace(/[']/g, "\\'"); 40 | } 41 | 42 | function logUnexpectedResult(section, test, expectedError) { 43 | var expected = expectedError ? 'error' : 'AST'; 44 | var result = expectedError ? 'AST' : 'error'; 45 | console.error( 46 | '[' + section + '] Expected "' + test + '" ' + 47 | 'to result in an ' + expected + ' but got an ' + result + ' instead.' 48 | ); 49 | } 50 | 51 | out += "var testFixture;\n\n"; 52 | out += "var fbTestFixture = {\n"; 53 | var ast, result; 54 | for (section in tests) { 55 | out += " '"+section+"': {\n"; 56 | var expectError = /^Invalid/.test(section); 57 | for (test in tests[section]) { 58 | test = tests[section][test]; 59 | out += " '"+escape_content(test)+"': {\n"; 60 | var gotError = false; 61 | try { 62 | ast = esprima.parse(test, options); 63 | result = stringify(ast.body[0]); 64 | result = result 65 | .substr(2, result.length-4) 66 | .split("\n") 67 | .map(function(x) { return " "+x; }); 68 | out += result.join("\n"); 69 | } catch (e) { 70 | gotError = true; 71 | 72 | out += " index: "+e.index+",\n"; 73 | out += " lineNumber: "+e.lineNumber+",\n"; 74 | out += " column: "+e.column+",\n"; 75 | out += " message: 'Error: "+e.message+"',\n"; 76 | out += " description: '"+e.description+"'\n"; 77 | } 78 | out += "\n"; 79 | out += " },\n"; 80 | 81 | if (expectError !== gotError) { 82 | logUnexpectedResult(section, escape_content(test), expectError); 83 | } 84 | } 85 | out += " },\n"; 86 | } 87 | 88 | out += "};\n\n\ 89 | \/\/ Merge fbTestFixture in to testFixture\n\ 90 | \n\ 91 | (function () {\n\ 92 | \n\ 93 | 'use strict';\n\ 94 | \n\ 95 | var i, j, fixtures;\n\ 96 | \n\ 97 | for (i in fbTestFixture) {\n\ 98 | if (fbTestFixture.hasOwnProperty(i)) {\n\ 99 | for (j in fbTestFixture[i]) {\n\ 100 | numTests--;\n\ 101 | }\n\ 102 | fixtures = fbTestFixture[i];\n\ 103 | if (i !== 'Syntax' && testFixture.hasOwnProperty(i)) {\n\ 104 | throw new Error('FB test should not replace existing test for ' + i);\n\ 105 | }\n\ 106 | testFixture[i] = fixtures;\n\ 107 | testFixtureOptions[i] = {sourceType: 'module'};\n\ 108 | \n\ 109 | if (/( \\(module and script\\)$)/.test(i)) {\n\ 110 | testFixture[i + ' (non-module)'] = fixtures;\n\ 111 | }\n\ 112 | }\n\ 113 | }\n\ 114 | \n\ 115 | if (numTests !== 0) {\n\ 116 | console.log(numTests);\n\ 117 | throw new Error(\n\ 118 | 'Our terrible checksum check failed. '+\n\ 119 | 'Did you modify fbtest.rec.js directly? '+\n\ 120 | 'Please add your test to fbtest.js instead.'\n\ 121 | );\n\ 122 | }\n\ 123 | \n\ 124 | }());\n" 125 | 126 | fs.writeFileSync(__dirname+'/../test/fbtest.rec.js', out); 127 | console.log("Recorded fbtest.js output into fbtest.rec.js"); 128 | -------------------------------------------------------------------------------- /tools/generate-test-fixture.js: -------------------------------------------------------------------------------- 1 | // #!/usr/bin/env node 2 | /* 3 | Copyright (C) 2012 Yusuke Suzuki 4 | Copyright (C) 2012 Ariya Hidayat 5 | Copyright (C) 2011 Ariya Hidayat 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /*jslint node:true */ 29 | /*global str:true */ 30 | (function () { 31 | 'use strict'; 32 | var fs = require('fs'), 33 | path = require('path'), 34 | stringify = require('./stringify.js'), 35 | root = path.join(path.dirname(fs.realpathSync(__filename)), '..'), 36 | esprima = require(path.join(root, 'esprima')), 37 | code, 38 | content, 39 | options; 40 | 41 | var cliArgs = require('commander') 42 | .option('-m, --module', 'Parse source as a module (rather than as a script)') 43 | .parse(process.argv); 44 | 45 | code = cliArgs.args[0]; 46 | 47 | if (code.length === 0) { 48 | console.log('Usage:'); 49 | console.log(' generate-test-fixture.js code'); 50 | process.exit(1); 51 | } 52 | 53 | 54 | content = code; 55 | 56 | options = { 57 | comment: false, 58 | range: true, 59 | loc: true, 60 | tokens: false, 61 | raw: true, 62 | tolerant: false, 63 | sourceType: cliArgs.module ? 'module' : 'script' 64 | }; 65 | 66 | console.log(stringify(esprima.parse(content, options))); 67 | })(); 68 | /* vim: set sw=4 ts=4 et tw=80 : */ 69 | -------------------------------------------------------------------------------- /tools/generate-unicode-regex.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # By Yusuke Suzuki 4 | # Modified by Mathias Bynens 5 | # http://code.google.com/p/esprima/issues/detail?id=110 6 | 7 | import sys 8 | import string 9 | import re 10 | 11 | class RegExpGenerator(object): 12 | def __init__(self, detector): 13 | self.detector = detector 14 | 15 | def generate_identifier_start(self): 16 | r = [ ch for ch in range(0xFFFF + 1) if self.detector.is_identifier_start(ch)] 17 | return self._generate_range(r) 18 | 19 | def generate_identifier_part(self): 20 | r = [ ch for ch in range(0xFFFF + 1) if self.detector.is_identifier_part(ch)] 21 | return self._generate_range(r) 22 | 23 | def generate_non_ascii_identifier_start(self): 24 | r = [ ch for ch in xrange(0x0080, 0xFFFF + 1) if self.detector.is_identifier_start(ch)] 25 | return self._generate_range(r) 26 | 27 | def generate_non_ascii_identifier_part(self): 28 | r = [ ch for ch in range(0x0080, 0xFFFF + 1) if self.detector.is_identifier_part(ch)] 29 | return self._generate_range(r) 30 | 31 | def generate_non_ascii_separator_space(self): 32 | r = [ ch for ch in range(0x0080, 0xFFFF + 1) if self.detector.is_separator_space(ch)] 33 | return self._generate_range(r) 34 | 35 | def _generate_range(self, r): 36 | if len(r) == 0: 37 | return '[]' 38 | 39 | buf = [] 40 | start = r[0] 41 | end = r[0] 42 | predict = start + 1 43 | r = r[1:] 44 | 45 | for code in r: 46 | if predict == code: 47 | end = code 48 | predict = code + 1 49 | continue 50 | else: 51 | if start == end: 52 | buf.append("\\u%04X" % start) 53 | elif end == start + 1: 54 | buf.append("\\u%04X\\u%04X" % (start, end)) 55 | else: 56 | buf.append("\\u%04X-\\u%04X" % (start, end)) 57 | start = code 58 | end = code 59 | predict = code + 1 60 | 61 | if start == end: 62 | buf.append("\\u%04X" % start) 63 | else: 64 | buf.append("\\u%04X-\\u%04X" % (start, end)) 65 | 66 | return '[' + ''.join(buf) + ']' 67 | 68 | 69 | class Detector(object): 70 | def __init__(self, data): 71 | self.data = data 72 | 73 | def is_ascii(self, ch): 74 | return ch < 0x80 75 | 76 | def is_ascii_alpha(self, ch): 77 | v = ch | 0x20 78 | return v >= ord('a') and v <= ord('z') 79 | 80 | def is_decimal_digit(self, ch): 81 | return ch >= ord('0') and ch <= ord('9') 82 | 83 | def is_octal_digit(self, ch): 84 | return ch >= ord('0') and ch <= ord('7') 85 | 86 | def is_hex_digit(self, ch): 87 | v = ch | 0x20 88 | return self.is_decimal_digit(c) or (v >= ord('a') and v <= ord('f')) 89 | 90 | def is_digit(self, ch): 91 | return self.is_decimal_digit(ch) or self.data[ch] == 'Nd' 92 | 93 | def is_ascii_alphanumeric(self, ch): 94 | return self.is_decimal_digit(ch) or self.is_ascii_alpha(ch) 95 | 96 | def _is_non_ascii_identifier_start(self, ch): 97 | c = self.data[ch] 98 | return c == 'Lu' or c == 'Ll' or c == 'Lt' or c == 'Lm' or c == 'Lo' or c == 'Nl' 99 | 100 | def _is_non_ascii_identifier_part(self, ch): 101 | c = self.data[ch] 102 | return c == 'Lu' or c == 'Ll' or c == 'Lt' or c == 'Lm' or c == 'Lo' or c == 'Nl' or c == 'Mn' or c == 'Mc' or c == 'Nd' or c == 'Pc' or ch == 0x200C or ch == 0x200D 103 | 104 | def is_separator_space(self, ch): 105 | return self.data[ch] == 'Zs' 106 | 107 | def is_white_space(self, ch): 108 | return ch == ord(' ') or ch == ord("\t") or ch == 0xB or ch == 0xC or ch == 0x00A0 or ch == 0xFEFF or self.is_separator_space(ch) 109 | 110 | def is_line_terminator(self, ch): 111 | return ch == 0x000D or ch == 0x000A or self.is_line_or_paragraph_terminator(ch) 112 | 113 | def is_line_or_paragraph_terminator(self, ch): 114 | return ch == 0x2028 or ch == 0x2029 115 | 116 | def is_identifier_start(self, ch): 117 | if self.is_ascii(ch): 118 | return ch == ord('$') or ch == ord('_') or ch == ord('\\') or self.is_ascii_alpha(ch) 119 | return self._is_non_ascii_identifier_start(ch) 120 | 121 | def is_identifier_part(self, ch): 122 | if self.is_ascii(ch): 123 | return ch == ord('$') or ch == ord('_') or ch == ord('\\') or self.is_ascii_alphanumeric(ch) 124 | return self._is_non_ascii_identifier_part(ch) 125 | 126 | def analyze(source): 127 | data = [] 128 | dictionary = {} 129 | with open(source) as uni: 130 | flag = False 131 | first = 0 132 | for line in uni: 133 | d = string.split(line.strip(), ";") 134 | val = int(d[0], 16) 135 | if flag: 136 | if re.compile("<.+, Last>").match(d[1]): 137 | # print "%s : u%X" % (d[1], val) 138 | flag = False 139 | for t in range(first, val+1): 140 | dictionary[t] = str(d[2]) 141 | else: 142 | raise "Database Exception" 143 | else: 144 | if re.compile("<.+, First>").match(d[1]): 145 | # print "%s : u%X" % (d[1], val) 146 | flag = True 147 | first = val 148 | else: 149 | dictionary[val] = str(d[2]) 150 | for i in range(0xFFFF + 1): 151 | if dictionary.get(i) == None: 152 | data.append("Un") 153 | else: 154 | data.append(dictionary[i]) 155 | return RegExpGenerator(Detector(data)) 156 | 157 | def main(source): 158 | generator = analyze(source) 159 | print generator.generate_non_ascii_identifier_start() 160 | print generator.generate_non_ascii_identifier_part() 161 | print generator.generate_non_ascii_separator_space() 162 | 163 | if __name__ == '__main__': 164 | main(sys.argv[1]) 165 | -------------------------------------------------------------------------------- /tools/list-complexity.js: -------------------------------------------------------------------------------- 1 | var escomplex = require('escomplex-js'), 2 | content = require('fs').readFileSync('esprima.js', 'utf-8'), 3 | opt = { logicalor: false, switchcase: false }, 4 | list = []; 5 | 6 | escomplex.analyse(content, opt).functions.forEach(function (entry) { 7 | var name = (entry.name === '') ? (':' + entry.line) : entry.name; 8 | list.push({ name: name, value: entry.cyclomatic }); 9 | }); 10 | 11 | list.sort(function (x, y) { 12 | return y.value - x.value; 13 | }); 14 | 15 | console.log('Most cyclomatic-complex functions:'); 16 | list.slice(0, 6).forEach(function (entry) { 17 | console.log(' ', entry.name, entry.value); 18 | }); 19 | -------------------------------------------------------------------------------- /tools/stringify.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2012 Yusuke Suzuki 3 | Copyright (C) 2012 Ariya Hidayat 4 | Copyright (C) 2011 Ariya Hidayat 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | /*jslint node:true */ 28 | /*global str:true */ 29 | // This function is based on ECMA262 section 15.12.3JSON.stringify algorithm with modification 30 | 'use strict'; 31 | function stringify(given) { 32 | var Regex, gap, top, indent; 33 | 34 | Regex = { 35 | NonAsciiIdentifierStart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'), 36 | NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]') 37 | }; 38 | 39 | function isIdentifierStart(ch) { 40 | return (ch === '$') || (ch === '_') || (ch === '\\') || 41 | (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || 42 | ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch)); 43 | } 44 | 45 | function isIdentifierPart(ch) { 46 | return (ch === '$') || (ch === '_') || (ch === '\\') || 47 | (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || 48 | ((ch >= '0') && (ch <= '9')) || 49 | ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch)); 50 | } 51 | 52 | // 7.6.1.2 Future Reserved Words 53 | 54 | function isFutureReservedWord(id) { 55 | switch (id) { 56 | 57 | // Future reserved words. 58 | case 'class': 59 | case 'enum': 60 | case 'export': 61 | case 'extends': 62 | case 'import': 63 | case 'super': 64 | return true; 65 | } 66 | 67 | return false; 68 | } 69 | 70 | function isStrictModeReservedWord(id) { 71 | switch (id) { 72 | 73 | // Strict Mode reserved words. 74 | case 'implements': 75 | case 'interface': 76 | case 'package': 77 | case 'private': 78 | case 'protected': 79 | case 'public': 80 | case 'static': 81 | case 'yield': 82 | case 'let': 83 | return true; 84 | } 85 | 86 | return false; 87 | } 88 | 89 | function isRestrictedWord(id) { 90 | return id === 'eval' || id === 'arguments'; 91 | } 92 | 93 | // 7.6.1.1 Keywords 94 | 95 | function isKeyword(id) { 96 | var keyword = false; 97 | switch (id.length) { 98 | case 2: 99 | keyword = (id === 'if') || (id === 'in') || (id === 'do'); 100 | break; 101 | case 3: 102 | keyword = (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try'); 103 | break; 104 | case 4: 105 | keyword = (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with'); 106 | break; 107 | case 5: 108 | keyword = (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw'); 109 | break; 110 | case 6: 111 | keyword = (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch'); 112 | break; 113 | case 7: 114 | keyword = (id === 'default') || (id === 'finally'); 115 | break; 116 | case 8: 117 | keyword = (id === 'function') || (id === 'continue') || (id === 'debugger'); 118 | break; 119 | case 10: 120 | keyword = (id === 'instanceof'); 121 | break; 122 | } 123 | 124 | if (keyword) { 125 | return true; 126 | } 127 | 128 | switch (id) { 129 | // Future reserved words. 130 | // 'const' is specialized as Keyword in V8. 131 | case 'const': 132 | return true; 133 | 134 | // For compatiblity to SpiderMonkey and ES.next 135 | case 'yield': 136 | case 'let': 137 | return true; 138 | } 139 | 140 | if (isStrictModeReservedWord(id)) { 141 | return true; 142 | } 143 | 144 | return isFutureReservedWord(id); 145 | } 146 | 147 | function isIdentifier(name) { 148 | var i, iz; 149 | // fallback for ES3 150 | if (isKeyword(name) || isRestrictedWord(name)) { 151 | return false; 152 | } 153 | if (name.length === 0) { 154 | return false; 155 | } 156 | if (!isIdentifierStart(name.charAt(0))) { 157 | return false; 158 | } 159 | for (i = 1, iz = name.length; i < iz; i += 1) { 160 | if (!isIdentifierPart(name.charAt(i))) { 161 | return false; 162 | } 163 | } 164 | return true; 165 | } 166 | 167 | function quote(string) { 168 | var i, iz, ch, product, hex; 169 | product = '\''; 170 | for (i = 0, iz = string.length; i < iz; i += 1) { 171 | ch = string[i]; 172 | if (ch === '\'' || ch === '\\') { 173 | product += '\\'; 174 | product += ch; 175 | } else { 176 | switch (ch) { 177 | case '\b': 178 | product += '\\b'; 179 | break; 180 | case '\f': 181 | product += '\\f'; 182 | break; 183 | case '\n': 184 | product += '\\n'; 185 | break; 186 | case '\r': 187 | product += '\\r'; 188 | break; 189 | case '\t': 190 | product += '\\t'; 191 | break; 192 | case '\u2028': 193 | product += '\u2028'; 194 | break; 195 | case '\u2029': 196 | product += '\u2028'; 197 | break; 198 | default: 199 | if (ch < ' ') { 200 | hex = ch.toString(16); 201 | product += '\\u' + '0000'.slice(hex.length) + hex; 202 | } else { 203 | product += ch; 204 | } 205 | } 206 | } 207 | } 208 | return product + '\''; 209 | } 210 | 211 | function ja(array) { 212 | return '[' + array.map(function (elm, key) { 213 | return str(key, array); 214 | }).join(', ') + ']'; 215 | } 216 | 217 | function jo(object, key) { 218 | var stepback, partial, fin, separator; 219 | 220 | stepback = indent; 221 | indent += gap; 222 | 223 | if (key === 'loc') { 224 | // specialized for loc 225 | fin = '{\n' + 226 | indent + 'start: { line: ' + stringify(object.start.line) + ', column: ' + stringify(object.start.column) + ' },\n' + 227 | indent + 'end: { line: ' + stringify(object.end.line) + ', column: ' + stringify(object.end.column) + ' }\n' + 228 | stepback + '}'; 229 | } else { 230 | partial = Object.keys(object).reduce(function (partial, name) { 231 | var res; 232 | res = str(name, object); 233 | if (res !== undefined) { 234 | if (!isIdentifier(name)) { 235 | name = quote(name); 236 | } 237 | partial.push(name + ': ' + res); 238 | } 239 | return partial; 240 | }, []); 241 | 242 | if (!partial.length) { 243 | fin = '{}'; 244 | } else { 245 | fin = '{\n' + indent + partial.join(',\n' + indent) + '\n' + stepback + '}'; 246 | } 247 | } 248 | 249 | indent = stepback; 250 | return fin; 251 | } 252 | 253 | function str(key, holder) { 254 | var value; 255 | value = holder[key]; 256 | if (typeof value === 'object' && value !== null) { 257 | if (value instanceof Number) { 258 | value = Number(value); 259 | } 260 | if (value instanceof String) { 261 | value = String(value); 262 | } 263 | if (value instanceof Boolean) { 264 | value = Boolean(value); 265 | } 266 | } 267 | switch (typeof value) { 268 | case 'object': 269 | if (value === null) { 270 | return 'null'; 271 | } 272 | if (Array.isArray(value)) { 273 | return ja(value); 274 | } 275 | return jo(value, key); 276 | 277 | case 'boolean': 278 | return String(value); 279 | case 'string': 280 | return quote(value); 281 | case 'number': 282 | return JSON.stringify(value); 283 | } 284 | } 285 | 286 | gap = ' '; 287 | indent = ''; 288 | top = {}; 289 | top[''] = given; 290 | 291 | return str('', top); 292 | } 293 | 294 | module.exports = stringify; 295 | -------------------------------------------------------------------------------- /tools/update-coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ `command -v istanbul` ]; then 4 | 5 | REVISION=`git log -1 --pretty=%h` 6 | DATE=`git log -1 --pretty=%cD | cut -c 6-16` 7 | 8 | echo "Running coverage analysis..." 9 | istanbul cover test/runner.js 10 | grep -v 'class="path' coverage/lcov-report/esprima/esprima.js.html | grep -v "class='meta" > test/esprima.js.html 11 | 12 | else 13 | echo "Please install Istanbul first!" 14 | fi 15 | --------------------------------------------------------------------------------
17 |
18 | 66 |
67 |