├── .gitignore ├── LICENSE.txt ├── README.md ├── cfg ├── README.md ├── lib │ ├── cfg.js │ └── dominators.js └── test │ └── test-cfg.js ├── common ├── README.md └── lib │ ├── ast.js │ ├── position.js │ └── sets.js ├── normalizer-rhino ├── .classpath ├── .gitignore ├── .project ├── README.md ├── build.xml ├── js │ └── normalize.js └── src │ └── normalizer │ └── Normalizer.java ├── normalizer ├── README.md ├── doc │ └── normalization.md ├── find_suspicious_positions.js ├── lib │ ├── cflow.js │ ├── decls.js │ ├── normalizer.js │ ├── scope.js │ └── util.js └── test │ ├── data │ ├── cond.js │ ├── empty.js │ ├── fnbody.js │ ├── function_expr1.js │ ├── function_expr2.js │ ├── function_expr3.js │ ├── function_expr4.js │ ├── global_assign.js │ ├── global_decl_without_init.js │ ├── global_fundecl.js │ ├── global_this.js │ ├── globalvar.js │ ├── literal.js │ ├── normalized.cond.js │ ├── normalized.empty.js │ ├── normalized.fnbody.js │ ├── normalized.function_expr1.js │ ├── normalized.function_expr2.js │ ├── normalized.function_expr3.js │ ├── normalized.function_expr4.js │ ├── normalized.global_assign.js │ ├── normalized.global_decl_without_init.js │ ├── normalized.global_fundecl.js │ ├── normalized.global_this.js │ ├── normalized.globalvar.js │ ├── normalized.literal.js │ ├── normalized.test1.js │ ├── normalized.test10.js │ ├── normalized.test11.js │ ├── normalized.test12.js │ ├── normalized.test13.js │ ├── normalized.test14.js │ ├── normalized.test15.js │ ├── normalized.test16.js │ ├── normalized.test17.js │ ├── normalized.test18.js │ ├── normalized.test19.js │ ├── normalized.test2.js │ ├── normalized.test20.js │ ├── normalized.test21.js │ ├── normalized.test22.js │ ├── normalized.test23.js │ ├── normalized.test24.js │ ├── normalized.test25.js │ ├── normalized.test26.js │ ├── normalized.test27.js │ ├── normalized.test28.js │ ├── normalized.test29.js │ ├── normalized.test3.js │ ├── normalized.test30.js │ ├── normalized.test31.js │ ├── normalized.test32.js │ ├── normalized.test33.js │ ├── normalized.test34.js │ ├── normalized.test35.js │ ├── normalized.test36.js │ ├── normalized.test37.js │ ├── normalized.test38.js │ ├── normalized.test39.js │ ├── normalized.test4.js │ ├── normalized.test40.js │ ├── normalized.test41.js │ ├── normalized.test42.js │ ├── normalized.test43.js │ ├── normalized.test44.js │ ├── normalized.test45.js │ ├── normalized.test46.js │ ├── normalized.test47.js │ ├── normalized.test48.js │ ├── normalized.test49.js │ ├── normalized.test5.js │ ├── normalized.test50.js │ ├── normalized.test51.js │ ├── normalized.test52.js │ ├── normalized.test53.js │ ├── normalized.test54.js │ ├── normalized.test55.js │ ├── normalized.test56.js │ ├── normalized.test57.js │ ├── normalized.test58.js │ ├── normalized.test59.js │ ├── normalized.test6.js │ ├── normalized.test60.js │ ├── normalized.test61.js │ ├── normalized.test62.js │ ├── normalized.test63.js │ ├── normalized.test64.js │ ├── normalized.test65.js │ ├── normalized.test66.js │ ├── normalized.test67.js │ ├── normalized.test68.js │ ├── normalized.test69.js │ ├── normalized.test7.js │ ├── normalized.test70.js │ ├── normalized.test71.js │ ├── normalized.test72.js │ ├── normalized.test73.js │ ├── normalized.test74.js │ ├── normalized.test75.js │ ├── normalized.test76.js │ ├── normalized.test77.js │ ├── normalized.test78.js │ ├── normalized.test79.js │ ├── normalized.test8.js │ ├── normalized.test80.js │ ├── normalized.test81.js │ ├── normalized.test9.js │ ├── normalized.unify_ret1.js │ ├── normalized.unify_ret2.js │ ├── normalized.unify_ret3.js │ ├── normalized.unify_ret4.js │ ├── normalized.unify_ret5.js │ ├── test1.js │ ├── test10.js │ ├── test11.js │ ├── test12.js │ ├── test13.js │ ├── test14.js │ ├── test15.js │ ├── test16.js │ ├── test17.js │ ├── test18.js │ ├── test19.js │ ├── test2.js │ ├── test20.js │ ├── test21.js │ ├── test22.js │ ├── test23.js │ ├── test24.js │ ├── test25.js │ ├── test26.js │ ├── test27.js │ ├── test28.js │ ├── test29.js │ ├── test3.js │ ├── test30.js │ ├── test31.js │ ├── test32.js │ ├── test33.js │ ├── test34.js │ ├── test35.js │ ├── test36.js │ ├── test37.js │ ├── test38.js │ ├── test39.js │ ├── test4.js │ ├── test40.js │ ├── test41.js │ ├── test42.js │ ├── test43.js │ ├── test44.js │ ├── test45.js │ ├── test46.js │ ├── test47.js │ ├── test48.js │ ├── test49.js │ ├── test5.js │ ├── test50.js │ ├── test51.js │ ├── test52.js │ ├── test53.js │ ├── test54.js │ ├── test55.js │ ├── test56.js │ ├── test57.js │ ├── test58.js │ ├── test59.js │ ├── test6.js │ ├── test60.js │ ├── test61.js │ ├── test62.js │ ├── test63.js │ ├── test64.js │ ├── test65.js │ ├── test66.js │ ├── test67.js │ ├── test68.js │ ├── test69.js │ ├── test7.js │ ├── test70.js │ ├── test71.js │ ├── test72.js │ ├── test73.js │ ├── test74.js │ ├── test75.js │ ├── test76.js │ ├── test77.js │ ├── test78.js │ ├── test79.js │ ├── test8.js │ ├── test80.js │ ├── test81.js │ ├── test9.js │ ├── unify_ret1.js │ ├── unify_ret2.js │ ├── unify_ret3.js │ ├── unify_ret4.js │ └── unify_ret5.js │ ├── normalize.js │ └── test-normalizer.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .project 3 | .settings 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | 14 | b) in the case of each subsequent Contributor: 15 | 16 | i) changes to the Program, and 17 | 18 | ii) additions to the Program; 19 | 20 | where such changes and/or additions to the Program originate from and are 21 | distributed by that particular Contributor. A Contribution 'originates' from a 22 | Contributor if it was added to the Program by such Contributor itself or anyone 23 | acting on such Contributor's behalf. Contributions do not include additions to 24 | the Program which: (i) are separate modules of software distributed in 25 | conjunction with the Program under their own license agreement, and (ii) are not 26 | derivative works of the Program. 27 | 28 | "Contributor" means any person or entity that distributes the Program. 29 | 30 | "Licensed Patents" mean patent claims licensable by a Contributor which are 31 | necessarily infringed by the use or sale of its Contribution alone or when 32 | combined with the Program. 33 | 34 | "Program" means the Contributions distributed in accordance with this Agreement. 35 | 36 | "Recipient" means anyone who receives the Program under this Agreement, 37 | including all Contributors. 38 | 39 | 2. GRANT OF RIGHTS 40 | 41 | a) Subject to the terms of this Agreement, each Contributor hereby grants 42 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 43 | reproduce, prepare derivative works of, publicly display, publicly perform, 44 | distribute and sublicense the Contribution of such Contributor, if any, and such 45 | derivative works, in source code and object code form. 46 | 47 | b) Subject to the terms of this Agreement, each Contributor hereby grants 48 | Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed 49 | Patents to make, use, sell, offer to sell, import and otherwise transfer the 50 | Contribution of such Contributor, if any, in source code and object code 51 | form. This patent license shall apply to the combination of the Contribution and 52 | the Program if, at the time the Contribution is added by the Contributor, such 53 | addition of the Contribution causes such combination to be covered by the 54 | Licensed Patents. The patent license shall not apply to any other combinations 55 | which include the Contribution. No hardware per se is licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses to 58 | its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other intellectual 60 | property rights of any other entity. Each Contributor disclaims any liability to 61 | Recipient for claims brought by any other entity based on infringement of 62 | intellectual property rights or otherwise. As a condition to exercising the 63 | rights and licenses granted hereunder, each Recipient hereby assumes sole 64 | responsibility to secure any other intellectual property rights needed, if 65 | any. For example, if a third party patent license is required to allow Recipient 66 | to distribute the Program, it is Recipient's responsibility to acquire that 67 | license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient copyright 70 | rights in its Contribution, if any, to grant the copyright license set forth in 71 | this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under its 76 | own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title and 84 | non-infringement, and implied warranties or conditions of merchantability and 85 | fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered by 92 | that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such Contributor, 95 | and informs licensees how to obtain it in a reasonable manner on or through a 96 | medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within the 105 | Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, if 108 | any, in a manner that reasonably allows subsequent Recipients to identify the 109 | originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a manner 117 | which does not create potential liability for other Contributors. Therefore, if 118 | a Contributor includes the Program in a commercial product offering, such 119 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 120 | every other Contributor ("Indemnified Contributor") against any losses, damages 121 | and costs (collectively "Losses") arising from claims, lawsuits and other legal 122 | actions brought by a third party against the Indemnified Contributor to the 123 | extent caused by the acts or omissions of such Commercial Contributor in 124 | connection with its distribution of the Program in a commercial product 125 | offering. The obligations in this section do not apply to any claims or Losses 126 | relating to any actual or alleged intellectual property infringement. In order 127 | to qualify, an Indemnified Contributor must: a) promptly notify the Commercial 128 | Contributor in writing of such claim, and b) allow the Commercial Contributor to 129 | control, and cooperate with the Commercial Contributor in, the defense and any 130 | related settlement negotiations. The Indemnified Contributor may participate in 131 | any such claim at its own expense. 132 | 133 | For example, a Contributor might include the Program in a commercial product 134 | offering, Product X. That Contributor is then a Commercial Contributor. If that 135 | Commercial Contributor then makes performance claims, or offers warranties 136 | related to Product X, those performance claims and warranties are such 137 | Commercial Contributor's responsibility alone. Under this section, the 138 | Commercial Contributor would have to defend claims against the other 139 | Contributors related to those performance claims and warranties, and if a court 140 | requires any other Contributor to pay any damages as a result, the Commercial 141 | Contributor must pay those damages. 142 | 143 | 5. NO WARRANTY 144 | 145 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 146 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 147 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 148 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 149 | Recipient is solely responsible for determining the appropriateness of using and 150 | distributing the Program and assumes all risks associated with its exercise of 151 | rights under this Agreement , including but not limited to the risks and costs 152 | of program errors, compliance with applicable laws, damage to or loss of data, 153 | programs or equipment, and unavailability or interruption of operations. 154 | 155 | 6. DISCLAIMER OF LIABILITY 156 | 157 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 158 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 159 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 160 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 161 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 162 | OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS 163 | GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 164 | 165 | 7. GENERAL 166 | 167 | If any provision of this Agreement is invalid or unenforceable under applicable 168 | law, it shall not affect the validity or enforceability of the remainder of the 169 | terms of this Agreement, and without further action by the parties hereto, such 170 | provision shall be reformed to the minimum extent necessary to make such 171 | provision valid and enforceable. 172 | 173 | If Recipient institutes patent litigation against any entity (including a 174 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 175 | (excluding combinations of the Program with other software or hardware) 176 | infringes such Recipient's patent(s), then such Recipient's rights granted under 177 | Section 2(b) shall terminate as of the date such litigation is filed. 178 | 179 | All Recipient's rights under this Agreement shall terminate if it fails to 180 | comply with any of the material terms or conditions of this Agreement and does 181 | not cure such failure in a reasonable period of time after becoming aware of 182 | such noncompliance. If all Recipient's rights under this Agreement terminate, 183 | Recipient agrees to cease use and distribution of the Program as soon as 184 | reasonably practicable. However, Recipient's obligations under this Agreement 185 | and any licenses granted by Recipient relating to the Program shall continue and 186 | survive. 187 | 188 | Everyone is permitted to copy and distribute copies of this Agreement, but in 189 | order to avoid inconsistency the Agreement is copyrighted and may only be 190 | modified in the following manner. The Agreement Steward reserves the right to 191 | publish new versions (including revisions) of this Agreement from time to 192 | time. No one other than the Agreement Steward has the right to modify this 193 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse 194 | Foundation may assign the responsibility to serve as the Agreement Steward to a 195 | suitable separate entity. Each new version of the Agreement will be given a 196 | distinguishing version number. The Program (including Contributions) may always 197 | be distributed subject to the version of the Agreement under which it was 198 | received. In addition, after a new version of the Agreement is published, 199 | Contributor may elect to distribute the Program (including its Contributions) 200 | under the new version. Except as expressly stated in Sections 2(a) and 2(b) 201 | above, Recipient receives no rights or licenses to the intellectual property of 202 | any Contributor under this Agreement, whether expressly, by implication, 203 | estoppel or otherwise. All rights in the Program not expressly granted under 204 | this Agreement are reserved. 205 | 206 | This Agreement is governed by the laws of the State of New York and the 207 | intellectual property laws of the United States of America. No party to this 208 | Agreement will bring a legal action under this Agreement more than one year 209 | after the cause of action arose. Each party waives its rights to a jury trial in 210 | any resulting litigation. 211 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains WALA analyses and tools that are implemented in JavaScript. (This is not to be confused with the [JavaScript front-end](http://wala.sourceforge.net/wiki/index.php/Getting_Started:JavaScript_frontend) for the core WALA analysis framework, which is implemented in Java and is available in the [WALA repository](https://github.com/wala/WALA).) 2 | 3 | There are currently three packages: 4 | 5 | * `normalizer`: a normalizer that converts JavaScript programs into a simpler form to ease other analyses 6 | * `cfg`: a package for building intraprocedural control flow graphs for programs that are already normalized 7 | * `common`: common modules used by the other packages 8 | 9 | Package `normalizer-rhino` is a Java wrapper for `normalizer`. 10 | 11 | See the packages' individual READMEs for more details. 12 | 13 | We hope to add other analysis and instrumentation infrastructure in the future. 14 | 15 | All code is available under the [Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html). 16 | 17 | -------------------------------------------------------------------------------- /cfg/README.md: -------------------------------------------------------------------------------- 1 | Control Flow Graph Utilities 2 | ============================ 3 | 4 | This package contains some utilities for building control flow graphs for normalized JavaScript programs. 5 | 6 | * `cfg.js`: 7 | 8 | Module for constructing intraprocedural CFGs; exports method `buildCFG` that should be invoked 9 | on the root node of the AST. Every statement node will be given attributes `succ` and `pred` 10 | containing, respectively, its control flow successor and predecessor sets. 11 | 12 | * `dominators.js`: 13 | 14 | Module for building dominator and post-dominator trees; exports method `buildDominatorTrees` 15 | that should be invoked with the root node of the AST as its first argument. If the second 16 | argument is truthy, both the dominator and the post-dominator tree will be built, otherwise only 17 | the dominator tree. Every statement node is given an attribute `idom` containing its immediate 18 | dominator node (if any); the immediate post-dominator is stored in attribute `ipdom`. 19 | 20 | Every function additionally has a special entry node that serves to root the CFG, its successor 21 | is the first statement of the function; if there are statically unreachable statements within 22 | the function body (e.g., statements after a `throw` or `return`), they are also made to be 23 | successors of the entry node. This is necessary for standard CFG algorithms to work. 24 | 25 | The top-level program also has an an entry node. Additionally, function and program nodes 26 | serve as their own exit nodes: a function node is the immediate successor of any `return` 27 | statement in its body, as well as of any `throw` that appears outside a `try` statement, and 28 | similar for program nodes. -------------------------------------------------------------------------------- /cfg/lib/cfg.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | /** 13 | * Intra-procedural control flow graph construction. 14 | * 15 | * Every statement is given attributes 'succ' and 'pred' containing the sets of 16 | * successor/predecessor nodes, using the set ADT implemented in sets.js. 17 | * 18 | * Programs and functions are furthermore given an entry node that does not correspond 19 | * to any source-level statement, but simply serves to anchor the CFG. Unreachable 20 | * statements have these entry nodes as their only predecessors. 21 | */ 22 | 23 | if(typeof define !== 'function') { 24 | var define = require('amdefine')(module); 25 | } 26 | 27 | define(function(require, exports) { 28 | var ast = require('../../common/lib/ast'), 29 | sets = require('../../common/lib/sets'); 30 | 31 | function Entry() { 32 | this.type = 'Entry'; 33 | this.attr = {}; 34 | } 35 | Entry.children = []; 36 | 37 | /** Adds a CFG edge from src to dest, i.e., enters dest into src's succ set, and src 38 | * into dest's pred set. */ 39 | function addEdge(src, dest) { 40 | if(!src || !dest || !src.type || !dest.type) 41 | throw new Error("both src and dest must be nodes!"); 42 | ast.setAttribute(src, 'succ', sets.add(ast.getAttribute(src, 'succ'), dest)); 43 | ast.setAttribute(dest, 'pred', sets.add(ast.getAttribute(dest, 'pred'), src)); 44 | } 45 | 46 | /** Shortcut when adding edges from same source to multiple dests. */ 47 | function addEdges(src, dests) { 48 | sets.forEach(dests, function(dest) { addEdge(src, dest); }); 49 | } 50 | 51 | /** Traverses the context to find the successors of a break statement. 52 | * 53 | * @param label the label of the loop out of which to break; since we assume JSNF, 54 | * every break has an explicit label 55 | * 56 | * @param context the current context 57 | * 58 | * @return the (single) successor node, or null if none was found; this can 59 | * only happen for malformed programs */ 60 | function getBreakTarget(label, context) { 61 | for(var i=context.length-1;i>=0;--i) { 62 | var item = context[i]; 63 | if(item.type === 'program' || item.type === 'function') 64 | throw new Error("label of break statement not found"); 65 | if(item.type === 'label' && item.label === label || item.type === 'finally') 66 | return item.next; 67 | } 68 | throw new Error("malformed context"); 69 | } 70 | 71 | /** Traverses the context to find the successors of a return statement. 72 | * 73 | * @return the (single) successor node, or null if none was found; this can 74 | * only happen for malformed programs */ 75 | function getReturnTarget(context) { 76 | for(var i=context.length-1;i>=0;--i) { 77 | var item = context[i]; 78 | if(item.type === 'program') 79 | throw new Error("illegal return statement at toplevel"); 80 | if(item.type === 'function') 81 | return item.node; 82 | if(item.type === 'finally') 83 | return item.next; 84 | } 85 | throw new Error("malformed context"); 86 | } 87 | 88 | /** Traverses the context to find the successors of an exception throw. 89 | * 90 | * @return the (single) successor node */ 91 | function getExceptionTarget(context) { 92 | for(var i=context.length-1;i>=0;--i) { 93 | var item = context[i]; 94 | if(item.type === 'program' || item.type === 'function') 95 | return item.node; 96 | if(item.type === 'finally' || item.type === 'catch') 97 | return item.next; 98 | } 99 | throw new Error("malformed context"); 100 | } 101 | 102 | /** Traverses the context to find the entry node of the closest enclosing function or script. */ 103 | function getFakeRoot(context) { 104 | for(var i=context.length-1;i>=0;--i) { 105 | var item = context[i]; 106 | if(item.type === 'program' || item.type === 'function') 107 | return ast.getAttribute(item.node, 'fakeRoot'); 108 | } 109 | throw new Error("no enclosing function/script found"); 110 | } 111 | 112 | /** Conservatively checks whether an expression may throw an exception during evaluation. 113 | * Assumes JSNF, so subexpressions are always local variables. 114 | * 115 | * TODO: doesn't account for toString/valueOf */ 116 | function mayThrow(expr) { 117 | switch(expr.type) { 118 | case 'CallExpression': 119 | case 'NewExpression': 120 | case 'UpdateExpression': 121 | return true; 122 | case 'MemberExpression': 123 | return expr.object.name !== '__global'; 124 | case 'UnaryExpression': 125 | return expr.operator === 'delete'; 126 | case 'BinaryExpression': 127 | return expr.operator === 'in' || expr.operator === 'instanceof'; 128 | default: 129 | return false; 130 | } 131 | } 132 | 133 | /** Builds CFGs for program prog and every function inside it; this is the main entry point. */ 134 | function buildCFG(prog) { 135 | // set up fake root 136 | var fakeRoot = new Entry(); 137 | ast.setPosition(fakeRoot, ast.getPosition(prog)); 138 | ast.setAttribute(prog, 'fakeRoot', fakeRoot); 139 | addEdge(fakeRoot, prog.body[0]); 140 | 141 | // build CFGs for all statements 142 | var accu = { exn: false, ret: false, brk: [] }, 143 | context = [{ type: 'program', node: prog }]; 144 | buildBlockCFG(prog.body, prog, context, accu); 145 | } 146 | 147 | /** Builds CFGs for function fn and every one of its inner functions. */ 148 | function buildFunctionCFG(fn, context) { 149 | // set up fake root 150 | var fakeRoot = new Entry(); 151 | ast.setPosition(fakeRoot, ast.getPosition(fn)); 152 | ast.setAttribute(fn, 'fakeRoot', fakeRoot); 153 | addEdge(fakeRoot, fn.body.body[0]); 154 | 155 | // build CFGs for all statements 156 | var accu = { exn: false, ret: false, brk: [] }; 157 | context[context.length] = { type: 'function', node: fn }; 158 | buildStmtCFG(fn.body, sets.singleton(fn), context, accu); 159 | --context.length; 160 | } 161 | 162 | /** 163 | * Builds a CFG for a sequence of statements. For the meanings of the parameters, see buildStmtCFG. 164 | */ 165 | function buildBlockCFG(stmts, following, context, accu) { 166 | for(var i=0;i= 1; --i) { 115 | var n = vertices[i]; 116 | var p = ast.getAttribute(n, dfparent), 117 | pn = ast.getAttribute(p, dfnum); 118 | 119 | // calculate semi-dominator of n 120 | var s = p, s2; 121 | sets.forEach(ast.getAttribute(n, next), function(v) { 122 | if(!v) 123 | return; 124 | if (ast.getAttribute(v, dfnum) <= ast.getAttribute(n, dfnum)) 125 | s2 = v; 126 | else { 127 | var a = ancestorWithLowestSemi(v); 128 | if(!a) 129 | return; 130 | s2 = semi[ast.getAttribute(a, dfnum)]; 131 | } 132 | if (ast.getAttribute(s2, dfnum) < ast.getAttribute(s, dfnum)) 133 | s = s2; 134 | }); 135 | semi[i] = s; 136 | 137 | // defer computation of dominator of n until path from s to n has been linked 138 | var sn = ast.getAttribute(s, dfnum); 139 | bucket[sn] = sets.add(bucket[sn] || [], n); 140 | 141 | link(p, n); 142 | var p_bucket = bucket[pn] || []; 143 | for (var k = 0; k < p_bucket.length; ++k) { 144 | var v = p_bucket[k], 145 | vn = ast.getAttribute(v, dfnum); 146 | 147 | // calculate dominator of v if possible 148 | var y = ancestorWithLowestSemi(v); 149 | if (semi[ast.getAttribute(y, dfnum)] == semi[vn]) 150 | idom[vn] = p; 151 | else 152 | // defer until dominator of y is known 153 | samedom[vn] = y; 154 | } 155 | bucket[pn] = []; 156 | } 157 | 158 | // perform deferred dominator calculations 159 | for (i = 1; i < vertices.length; ++i) 160 | if (samedom[i]) 161 | idom[i] = idom[ast.getAttribute(samedom[i], dfnum)]; 162 | 163 | // enter dominator information into CFG 164 | for (i = 1; i < vertices.length; ++i) { 165 | ast.setAttribute(vertices[i], reverse ? 'ipdom' : 'idom', idom[i]); 166 | 167 | // reverse edge 168 | var doms = ast.getAttribute(idom[i], reverse ? 'ipdoms' : 'idoms'); 169 | if(!doms) 170 | ast.setAttribute(idom[i], reverse ? 'ipdoms' : 'idoms', doms = []); 171 | sets.add(doms, vertices[i]); 172 | } 173 | } 174 | 175 | // build dominator trees for whole program 176 | function buildDominatorTrees(root, also_postdom) { 177 | switch(root.type) { 178 | case 'Program': 179 | case 'FunctionExpression': 180 | var fakeRoot = ast.getAttribute(root, "fakeRoot"); 181 | buildDominatorTree(fakeRoot); 182 | if(also_postdom) 183 | buildDominatorTree(root, true); 184 | 185 | ast.setAttribute(root, 'vertices', ast.getAttribute(fakeRoot, 'vertices')); 186 | default: 187 | ast.forEachChild(root, function(nd) { 188 | if(nd) 189 | buildDominatorTrees(nd, also_postdom); 190 | }); 191 | } 192 | } 193 | 194 | exports.buildDominatorTrees = buildDominatorTrees; 195 | }); -------------------------------------------------------------------------------- /cfg/test/test-cfg.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | /** 13 | * Unit tests for CFG algorithms. 14 | */ 15 | 16 | var ast = require('../../common/lib/ast.js'), 17 | sets = require('../../common/lib/sets.js'), 18 | cfg = require('../lib/cfg'), 19 | dominators = require('../lib/dominators'), 20 | esprima = require('esprima'); 21 | 22 | // run an individual test given input and expected output 23 | function runtest(test, prog, facts, normalise) { 24 | var ast = esprima.parse(prog, { loc: true, range: true }); 25 | cfg.buildCFG(ast); 26 | dominators.buildDominatorTrees(ast, true); 27 | var actual = dumpCFG(ast); 28 | if(actual !== facts) { 29 | console.log("actual:\n" + actual); 30 | console.log("expected:\n" + facts); 31 | } 32 | test.equal(actual, facts); 33 | test.done(); 34 | } 35 | 36 | function iterCFG(nd, f) { 37 | function rec(nd) { 38 | iterCFG(nd, f); 39 | } 40 | 41 | if(!nd) 42 | return; 43 | 44 | switch(nd.type) { 45 | case 'Program': 46 | f(nd); 47 | f(ast.getAttribute(nd, 'fakeRoot')); 48 | nd.body.forEach(rec); 49 | break; 50 | 51 | case 'FunctionExpression': 52 | f(nd); 53 | f(ast.getAttribute(nd, 'fakeRoot')); 54 | rec(nd.body); 55 | break; 56 | 57 | case 'EmptyStatement': 58 | case 'DebuggerStatement': 59 | case 'VariableDeclaration': 60 | case 'ReturnStatement': 61 | case 'BreakStatement': 62 | case 'ThrowStatement': 63 | f(nd); 64 | break; 65 | 66 | case 'ExpressionStatement': 67 | f(nd); 68 | switch(nd.expression.type) { 69 | case 'CallExpression': 70 | f(nd.expression.callee); 71 | break; 72 | case 'AssignmentExpression': 73 | if(nd.expression.right.type === 'FunctionExpression') 74 | rec(nd.expression.right); 75 | break; 76 | default: 77 | throw new Error("unexpected expression statement"); 78 | } 79 | break; 80 | 81 | case 'IfStatement': 82 | f(nd); 83 | rec(nd.consequent); 84 | rec(nd.alternate); 85 | break; 86 | 87 | case 'WhileStatement': 88 | case 'ForInStatement': 89 | f(nd); 90 | rec(nd.body); 91 | break; 92 | 93 | case 'LabeledStatement': 94 | f(nd); 95 | rec(nd.body); 96 | break; 97 | 98 | case 'TryStatement': 99 | f(nd); 100 | rec(nd.block); 101 | if(nd.handlers && nd.handlers[0]) 102 | rec(nd.handlers[0].body); 103 | if(nd.finalizer) 104 | rec(nd.finalizer); 105 | break; 106 | 107 | case 'BlockStatement': 108 | for(var i=0;i"; 120 | var pos = ast.getPosition(nd); 121 | return nd.type + " at " + pos.start_line + ":" + pos.start_offset; 122 | } 123 | 124 | function dumpCFG(root) { 125 | var res = ""; 126 | iterCFG(root, function(nd) { 127 | var succs = ast.getAttribute(nd, 'succ'); 128 | idom = ast.getAttribute(nd, 'idom'), 129 | ipdom = ast.getAttribute(nd, 'ipdom'); 130 | if(sets.size(succs) === 0) { 131 | res += dumpNode(nd) + " --> []\n"; 132 | } else { 133 | res += dumpNode(nd) + " --> [" + sets.map(succs, dumpNode).join(', ') + "]\n"; 134 | } 135 | res += " immediate dominator: " + (idom ? dumpNode(idom) : "none") + "\n"; 136 | res += " immediate postdominator: " + (ipdom ? dumpNode(ipdom) : "none") + "\n"; 137 | }); 138 | return res; 139 | } 140 | 141 | exports.test1 = function(test) { 142 | runtest(test, 143 | "f = function(x) {\n" 144 | + " var t, y;\n" 145 | + " if(x) {\n" 146 | + " y = 42;\n" 147 | + " } else {\n" // line 5 148 | + " y = 23;\n" 149 | + " }\n" 150 | + " t = g();\n" 151 | + " return y;\n" 152 | + "};\n" // line 10 153 | + "g = function() {\n" 154 | + " var i, t1, t2;\n" 155 | + " i=0;\n" 156 | + " t1 = i < 10;\n" 157 | + " l: { while(t1) {\n" // line 15 158 | + " try {\n" 159 | + " t2 = alert(i);\n" 160 | + " } catch(e) {\n" 161 | + " break l;\n" 162 | + " }\n" // line 20 163 | + " i = i + 1;\n" 164 | + " t1 = i < 10;\n" 165 | + " } }\n" 166 | + " return null;\n" 167 | + "};\n" // line 25 168 | + "z = f(56);\n", 169 | "Program at 1:0 --> []\n" 170 | + " immediate dominator: ExpressionStatement at 26:305\n" 171 | + " immediate postdominator: none\n" 172 | + "Entry at 1:0 --> [ExpressionStatement at 1:0]\n" 173 | + " immediate dominator: none\n" 174 | + " immediate postdominator: ExpressionStatement at 1:0\n" 175 | + "ExpressionStatement at 1:0 --> [ExpressionStatement at 11:105]\n" 176 | + " immediate dominator: Entry at 1:0\n" 177 | + " immediate postdominator: ExpressionStatement at 11:105\n" 178 | + "FunctionExpression at 1:4 --> []\n" 179 | + " immediate dominator: ExpressionStatement at 8:81\n" 180 | + " immediate postdominator: none\n" 181 | + "Entry at 1:4 --> [VariableDeclaration at 2:20]\n" 182 | + " immediate dominator: none\n" 183 | + " immediate postdominator: VariableDeclaration at 2:20\n" 184 | + "VariableDeclaration at 2:20 --> [IfStatement at 3:32]\n" 185 | + " immediate dominator: Entry at 1:4\n" 186 | + " immediate postdominator: IfStatement at 3:32\n" 187 | + "IfStatement at 3:32 --> [ExpressionStatement at 4:44, ExpressionStatement at 6:67]\n" 188 | + " immediate dominator: VariableDeclaration at 2:20\n" 189 | + " immediate postdominator: ExpressionStatement at 8:81\n" 190 | + "ExpressionStatement at 4:44 --> [ExpressionStatement at 8:81]\n" 191 | + " immediate dominator: IfStatement at 3:32\n" 192 | + " immediate postdominator: ExpressionStatement at 8:81\n" 193 | + "ExpressionStatement at 6:67 --> [ExpressionStatement at 8:81]\n" 194 | + " immediate dominator: IfStatement at 3:32\n" 195 | + " immediate postdominator: ExpressionStatement at 8:81\n" 196 | + "ExpressionStatement at 8:81 --> [ReturnStatement at 9:92, FunctionExpression at 1:4]\n" 197 | + " immediate dominator: IfStatement at 3:32\n" 198 | + " immediate postdominator: FunctionExpression at 1:4\n" 199 | + "ReturnStatement at 9:92 --> [FunctionExpression at 1:4]\n" 200 | + " immediate dominator: ExpressionStatement at 8:81\n" 201 | + " immediate postdominator: FunctionExpression at 1:4\n" 202 | + "ExpressionStatement at 11:105 --> [ExpressionStatement at 26:305]\n" 203 | + " immediate dominator: ExpressionStatement at 1:0\n" 204 | + " immediate postdominator: ExpressionStatement at 26:305\n" 205 | + "FunctionExpression at 11:109 --> []\n" 206 | + " immediate dominator: ReturnStatement at 24:289\n" 207 | + " immediate postdominator: none\n" 208 | + "Entry at 11:109 --> [VariableDeclaration at 12:124]\n" 209 | + " immediate dominator: none\n" 210 | + " immediate postdominator: VariableDeclaration at 12:124\n" 211 | + "VariableDeclaration at 12:124 --> [ExpressionStatement at 13:141]\n" 212 | + " immediate dominator: Entry at 11:109\n" 213 | + " immediate postdominator: ExpressionStatement at 13:141\n" 214 | + "ExpressionStatement at 13:141 --> [ExpressionStatement at 14:148]\n" 215 | + " immediate dominator: VariableDeclaration at 12:124\n" 216 | + " immediate postdominator: ExpressionStatement at 14:148\n" 217 | + "ExpressionStatement at 14:148 --> [LabeledStatement at 15:163]\n" 218 | + " immediate dominator: ExpressionStatement at 13:141\n" 219 | + " immediate postdominator: LabeledStatement at 15:163\n" 220 | + "LabeledStatement at 15:163 --> [WhileStatement at 15:168]\n" 221 | + " immediate dominator: ExpressionStatement at 14:148\n" 222 | + " immediate postdominator: WhileStatement at 15:168\n" 223 | + "WhileStatement at 15:168 --> [TryStatement at 16:184, ReturnStatement at 24:289]\n" 224 | + " immediate dominator: LabeledStatement at 15:163\n" 225 | + " immediate postdominator: ReturnStatement at 24:289\n" 226 | + "TryStatement at 16:184 --> [ExpressionStatement at 17:196]\n" 227 | + " immediate dominator: WhileStatement at 15:168\n" 228 | + " immediate postdominator: ExpressionStatement at 17:196\n" 229 | + "ExpressionStatement at 17:196 --> [ExpressionStatement at 21:253, BreakStatement at 19:234]\n" 230 | + " immediate dominator: TryStatement at 16:184\n" 231 | + " immediate postdominator: ReturnStatement at 24:289\n" 232 | + "BreakStatement at 19:234 --> [ReturnStatement at 24:289]\n" 233 | + " immediate dominator: ExpressionStatement at 17:196\n" 234 | + " immediate postdominator: ReturnStatement at 24:289\n" 235 | + "ExpressionStatement at 21:253 --> [ExpressionStatement at 22:268]\n" 236 | + " immediate dominator: ExpressionStatement at 17:196\n" 237 | + " immediate postdominator: ExpressionStatement at 22:268\n" 238 | + "ExpressionStatement at 22:268 --> [WhileStatement at 15:168]\n" 239 | + " immediate dominator: ExpressionStatement at 21:253\n" 240 | + " immediate postdominator: WhileStatement at 15:168\n" 241 | + "ReturnStatement at 24:289 --> [FunctionExpression at 11:109]\n" 242 | + " immediate dominator: WhileStatement at 15:168\n" 243 | + " immediate postdominator: FunctionExpression at 11:109\n" 244 | + "ExpressionStatement at 26:305 --> [Program at 1:0]\n" 245 | + " immediate dominator: ExpressionStatement at 11:105\n" 246 | + " immediate postdominator: Program at 1:0\n"); 247 | }; 248 | 249 | exports.test2 = function(test) { 250 | runtest(test, 251 | "try {\n" 252 | + " while (x) {\n" 253 | + " ;\n" 254 | + " }\n" 255 | + "} finally {\n" // line 5 256 | + " ;\n" 257 | + "}\n", 258 | "Program at 1:0 --> []\n" 259 | + " immediate dominator: EmptyStatement at 6:44\n" 260 | + " immediate postdominator: none\n" 261 | + "Entry at 1:0 --> [TryStatement at 1:0]\n" 262 | + " immediate dominator: none\n" 263 | + " immediate postdominator: TryStatement at 1:0\n" 264 | + "TryStatement at 1:0 --> [WhileStatement at 2:8]\n" 265 | + " immediate dominator: Entry at 1:0\n" 266 | + " immediate postdominator: WhileStatement at 2:8\n" 267 | + "WhileStatement at 2:8 --> [EmptyStatement at 3:24, EmptyStatement at 6:44]\n" 268 | + " immediate dominator: TryStatement at 1:0\n" 269 | + " immediate postdominator: EmptyStatement at 6:44\n" 270 | + "EmptyStatement at 3:24 --> [WhileStatement at 2:8]\n" 271 | + " immediate dominator: WhileStatement at 2:8\n" 272 | + " immediate postdominator: WhileStatement at 2:8\n" 273 | + "EmptyStatement at 6:44 --> [Program at 1:0]\n" 274 | + " immediate dominator: WhileStatement at 2:8\n" 275 | + " immediate postdominator: Program at 1:0\n"); 276 | }; 277 | 278 | exports.test3 = function(test) { 279 | runtest(test, 280 | "throw null;\n" 281 | + ";\n", 282 | "Program at 1:0 --> []\n" 283 | + " immediate dominator: Entry at 1:0\n" 284 | + " immediate postdominator: none\n" 285 | + "Entry at 1:0 --> [ThrowStatement at 1:0, EmptyStatement at 2:12]\n" 286 | + " immediate dominator: none\n" 287 | + " immediate postdominator: Program at 1:0\n" 288 | + "ThrowStatement at 1:0 --> [Program at 1:0]\n" 289 | + " immediate dominator: Entry at 1:0\n" 290 | + " immediate postdominator: Program at 1:0\n" 291 | + "EmptyStatement at 2:12 --> [Program at 1:0]\n" 292 | + " immediate dominator: Entry at 1:0\n" 293 | + " immediate postdominator: Program at 1:0\n"); 294 | }; 295 | 296 | exports.test4 = function(test) { 297 | runtest(test, 298 | "tmp6 = function(x) {\n" 299 | + " var type;\n" 300 | + " tmp1: {\n" 301 | + " try {\n" 302 | + " for (type in x) {\n" 303 | + " if (x) {\n" 304 | + " break tmp1;\n" 305 | + " } else {\n" 306 | + " x = 1;\n" 307 | + " }\n" 308 | + " try {\n" 309 | + " x = 2;\n" 310 | + " } finally {\n" 311 | + " x = 3;\n" 312 | + " }\n" 313 | + " }\n" 314 | + " } finally {\n" 315 | + " x = 4;\n" 316 | + " }\n" 317 | + " }\n" 318 | + "};\n", 319 | "Program at 1:0 --> []\n" 320 | + " immediate dominator: ExpressionStatement at 1:0\n" 321 | + " immediate postdominator: none\n" 322 | + "Entry at 1:0 --> [ExpressionStatement at 1:0]\n" 323 | + " immediate dominator: none\n" 324 | + " immediate postdominator: ExpressionStatement at 1:0\n" 325 | + "ExpressionStatement at 1:0 --> [Program at 1:0]\n" 326 | + " immediate dominator: Entry at 1:0\n" 327 | + " immediate postdominator: Program at 1:0\n" 328 | + "FunctionExpression at 1:7 --> []\n" 329 | + " immediate dominator: ExpressionStatement at 18:386\n" 330 | + " immediate postdominator: none\n" 331 | + "Entry at 1:7 --> [VariableDeclaration at 2:25]\n" 332 | + " immediate dominator: none\n" 333 | + " immediate postdominator: VariableDeclaration at 2:25\n" 334 | + "VariableDeclaration at 2:25 --> [LabeledStatement at 3:39]\n" 335 | + " immediate dominator: Entry at 1:7\n" 336 | + " immediate postdominator: LabeledStatement at 3:39\n" 337 | + "LabeledStatement at 3:39 --> [TryStatement at 4:55]\n" 338 | + " immediate dominator: VariableDeclaration at 2:25\n" 339 | + " immediate postdominator: TryStatement at 4:55\n" 340 | + "TryStatement at 4:55 --> [ForInStatement at 5:73]\n" 341 | + " immediate dominator: LabeledStatement at 3:39\n" 342 | + " immediate postdominator: ForInStatement at 5:73\n" 343 | + "ForInStatement at 5:73 --> [IfStatement at 6:107, ExpressionStatement at 18:386]\n" 344 | + " immediate dominator: TryStatement at 4:55\n" 345 | + " immediate postdominator: ExpressionStatement at 18:386\n" 346 | + "IfStatement at 6:107 --> [BreakStatement at 7:136, ExpressionStatement at 9:193]\n" 347 | + " immediate dominator: ForInStatement at 5:73\n" 348 | + " immediate postdominator: ExpressionStatement at 18:386\n" 349 | + "BreakStatement at 7:136 --> [ExpressionStatement at 18:386]\n" 350 | + " immediate dominator: IfStatement at 6:107\n" 351 | + " immediate postdominator: ExpressionStatement at 18:386\n" 352 | + "ExpressionStatement at 9:193 --> [TryStatement at 11:234]\n" 353 | + " immediate dominator: IfStatement at 6:107\n" 354 | + " immediate postdominator: TryStatement at 11:234\n" 355 | + "TryStatement at 11:234 --> [ExpressionStatement at 12:260]\n" 356 | + " immediate dominator: ExpressionStatement at 9:193\n" 357 | + " immediate postdominator: ExpressionStatement at 12:260\n" 358 | + "ExpressionStatement at 12:260 --> [ExpressionStatement at 14:315]\n" 359 | + " immediate dominator: TryStatement at 11:234\n" 360 | + " immediate postdominator: ExpressionStatement at 14:315\n" 361 | + "ExpressionStatement at 14:315 --> [ForInStatement at 5:73, ExpressionStatement at 18:386]\n" 362 | + " immediate dominator: ExpressionStatement at 12:260\n" 363 | + " immediate postdominator: ExpressionStatement at 18:386\n" 364 | + "ExpressionStatement at 18:386 --> [FunctionExpression at 1:7]\n" 365 | + " immediate dominator: ForInStatement at 5:73\n" 366 | + " immediate postdominator: FunctionExpression at 1:7\n"); 367 | }; 368 | 369 | 370 | var reporter = require('nodeunit').reporters['default']; 371 | reporter.run({"test-cfg" : module.exports}); -------------------------------------------------------------------------------- /common/README.md: -------------------------------------------------------------------------------- 1 | This package contains some common utility modules used by the other packages: 2 | 3 | * `ast.js`: AST construction and manipulation 4 | * `position.js`: handling source positions 5 | * `sets.js`: simple representation of sets -------------------------------------------------------------------------------- /common/lib/ast.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | /** 13 | * Convenience functions for constructing and navigating ASTs. 14 | */ 15 | if(typeof define !== 'function') { 16 | var define = require('amdefine')(module); 17 | } 18 | 19 | define(function(require, exports) { 20 | var position = require('./position'); 21 | 22 | // constructor signatures; arguments in angle brackets are terminal children, the others subtrees 23 | var signatures = { 24 | AssignmentExpression: [ '', 'left', 'right'], 25 | ArrayExpression: [ 'elements' ], 26 | BlockStatement: [ 'body' ], 27 | BinaryExpression: [ '', 'left', 'right'], 28 | BreakStatement: [ 'label' ], 29 | CallExpression: [ 'callee', 'arguments' ], 30 | CatchClause: [ 'param', 'body' ], 31 | ConditionalExpression: [ 'test', 'consequent', 'alternate' ], 32 | ContinueStatement: [ 'label' ], 33 | DirectiveStatement: [ ], 34 | DoWhileStatement: [ 'body', 'test' ], 35 | DebuggerStatement: [ ], 36 | EmptyStatement: [ ], 37 | ExpressionStatement: [ 'expression' ], 38 | ForStatement: [ 'init', 'test', 'update', 'body' ], 39 | ForInStatement: [ 'left', 'right', 'body' ], 40 | FunctionDeclaration: [ 'id', 'params', 'body' ], 41 | FunctionExpression: [ 'id', 'params', 'body' ], 42 | Identifier: [ '' ], 43 | IfStatement: [ 'test', 'consequent', 'alternate' ], 44 | Literal: [ '' ], 45 | LabeledStatement: [ 'label', 'body' ], 46 | LogicalExpression: [ '', 'left', 'right' ], 47 | MemberExpression: [ 'object', 'property', '' ], 48 | NewExpression: [ 'callee', 'arguments' ], 49 | ObjectExpression: [ 'properties' ], 50 | Program: [ 'body' ], 51 | Property: [ 'key', 'value', '' ], 52 | ReturnStatement: [ 'argument' ], 53 | SequenceExpression: [ 'expressions' ], 54 | SwitchStatement: [ 'discriminant', 'cases' ], 55 | SwitchCase: [ 'test', 'consequent' ], 56 | ThisExpression: [ ], 57 | ThrowStatement: [ 'argument' ], 58 | TryStatement: [ 'block', 'guardedHandlers', 'handlers', 'finalizer' ], 59 | UnaryExpression: [ '', 'argument' ], 60 | UpdateExpression: [ '', 'argument', '' ], 61 | VariableDeclaration: [ 'declarations', '' ], 62 | VariableDeclarator: [ 'id', 'init' ], 63 | WhileStatement: [ 'test', 'body' ], 64 | WithStatement: [ 'object', 'body' ] 65 | }; 66 | 67 | // define a constructor from a signature 68 | function defconstructor(tpname, signature) { 69 | var child_names = [], nonterminal_children = []; 70 | for(var i=0;i"; 30 | this.start_line = start_line; 31 | this.start_offset = start_offset; 32 | this.end_line = end_line; 33 | this.end_offset = end_offset; 34 | } 35 | 36 | Position.prototype.toString = function(short) { 37 | if(short) 38 | return this.start_line + ":" + this.start_offset; 39 | return this.url + "/" + this.start_line + ":" + this.start_offset + "-" + this.end_line + ":" + this.end_offset; 40 | }; 41 | 42 | Position.prototype.clone = function() { 43 | return new Position(this.url, this.start_line, this.start_offset, this.end_line, this.end_offset); 44 | }; 45 | 46 | Position.prototype.equals = function(o) { 47 | if(!(o instanceof Position)) 48 | return false; 49 | return o.url === this.url && 50 | o.start_line === this.start_line && 51 | o.start_offset === this.start_offset && 52 | o.end_line === this.end_line && 53 | o.end_offset === this.end_offset; 54 | }; 55 | 56 | var DUMMY_POS = new Position(null, -1, -1, -1, -1); 57 | 58 | return { 59 | Position: Position, 60 | DUMMY_POS: DUMMY_POS 61 | }; 62 | }); -------------------------------------------------------------------------------- /common/lib/sets.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | /** 13 | * Simple set ADT. 14 | * 15 | * Empty sets are represented by null or some other falsy value; singleton sets 16 | * are represented by their only element, which may not be an array; sets of 17 | * size greater than one, or singleton elements whose only element is an array 18 | * or is falsy, are represented by an array containing their elements. 19 | */ 20 | if(typeof define !== 'function') { 21 | var define = require('amdefine')(module); 22 | } 23 | 24 | define(function(require, exports) { 25 | /** Wraps a single element into a set. */ 26 | exports.singleton = function(elt) { 27 | if(!elt || Array.isArray(elt)) 28 | return [elt]; 29 | return elt; 30 | }; 31 | 32 | /** The canonical empty set. */ 33 | exports.empty = null; 34 | 35 | /** Returns the size of the given set. */ 36 | exports.size = function(set) { 37 | if(!set) 38 | return 0; 39 | if(Array.isArray(set)) 40 | return set.length; 41 | return 1; 42 | }; 43 | 44 | /** Adds elt to set, possibly mutating set in the process, and returns the resulting set. */ 45 | exports.add = function(set, elt) { 46 | if(!set) 47 | return exports.singleton(elt); 48 | if(Array.isArray(set)) { 49 | for(var i=0;i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /normalizer-rhino/.gitignore: -------------------------------------------------------------------------------- 1 | bin/* -------------------------------------------------------------------------------- /normalizer-rhino/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | normalizer-rhino 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /normalizer-rhino/README.md: -------------------------------------------------------------------------------- 1 | Java Wrapper for JS_WALA Normalizer 2 | =================================== 3 | 4 | This is a Java wrapper for the JS_WALA normalizer. It uses Rhino to run the normalizer on a provided JavaScript file, returning the source code of the normalized script as a string. 5 | 6 | 7 | Setup 8 | ----- 9 | 10 | Use the default target of the provided Ant build file to pull in Rhino and the JavaScript source code of the normalizer. 11 | 12 | If you are using Eclipse, refresh the project to make sure that all JavaScript sources are copied to the output folder. Otherwise, you have to do so manually. 13 | 14 | The main API method for the normalizer is `Normalizer.normalize`. 15 | 16 | 17 | License 18 | ------- 19 | 20 | Like the rest of JS_WALA, this wrapper is distributed under the Eclipse Public License; see `LICENSE.txt` in the parent directory. 21 | -------------------------------------------------------------------------------- /normalizer-rhino/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 31 | 32 | 33 | 35 | 36 | 38 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /normalizer-rhino/js/normalize.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2013 Max Schaefer. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Max Schaefer - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | /* Helper script to run the normalizer from Rhino. */ 13 | 14 | // pull base directory and file to normalize out of arguments array to avoid confusing 15 | // requirejs below 16 | var base = arguments[0]; 17 | var file = arguments[1]; 18 | arguments.length = 0; 19 | 20 | // first load escodegen; doesn't seem to be available as a requirejs module... 21 | window = this; 22 | load(base + "escodegen.browser.js"); 23 | window = undefined; 24 | 25 | // now load requirejs 26 | var requirejsAsLib = true; 27 | load(base + "r.js"); 28 | 29 | // set up paths for loading the other modules 30 | requirejs.config({ 31 | baseUrl: base, 32 | paths: { 33 | "normalizer": "normalizer/lib" 34 | } 35 | }); 36 | 37 | // now load Esprima and normalizer 38 | requirejs(['esprima', 'normalizer/normalizer'], function(esprima, normalizer) { 39 | var src = readFile(file); 40 | var ast = esprima.parse(src); 41 | var normalized = normalizer.normalize(ast); 42 | print(escodegen.generate(normalized)); 43 | }); -------------------------------------------------------------------------------- /normalizer-rhino/src/normalizer/Normalizer.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2013 Max Schaefer. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Max Schaefer - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | /* Driver class for normalizing a JavaScript file via Rhino. */ 13 | 14 | package normalizer; 15 | 16 | import java.io.ByteArrayOutputStream; 17 | import java.io.PrintStream; 18 | 19 | import org.mozilla.javascript.tools.shell.Main; 20 | 21 | public class Normalizer { 22 | private final static String basedir = Normalizer.class.getResource("/").toString(); 23 | 24 | /** 25 | * Normalizes the given file. 26 | * 27 | * @param file name of the file to normalize 28 | * @return normalized code 29 | */ 30 | public static String normalize(String file) { 31 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 32 | PrintStream ps = new PrintStream(baos); 33 | try { 34 | Main.setOut(ps); 35 | Main.exec(new String[] { basedir + "normalize.js", basedir, file }); 36 | ps.flush(); 37 | return baos.toString(); 38 | } finally { 39 | ps.close(); 40 | } 41 | } 42 | 43 | // simple command line interface 44 | public static void main(String[] args) { 45 | if(args.length != 1) 46 | System.err.println("Usage: java Normalizer FILE.js"); 47 | else 48 | System.out.println(normalize(args[0])); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /normalizer/README.md: -------------------------------------------------------------------------------- 1 | JavaScript Normalizer 2 | ===================== 3 | 4 | This is a library for normalizing JavaScript. It takes an arbitrary JavaScript program and translates it into a more regular subset of JavaScript as described in `doc/normalization.md`. 5 | 6 | The main API entry point is function `normalizer` in module `lib/normalizer.js`; it takes the AST of the whole program as produced by [Esprima](http://www.esprima.org) and returns the AST of the normalized program. 7 | 8 | If the original AST has position information, the nodes of the normalized AST will have the same positions as the nodes they originated from. See `doc/normalization.md` for a description of the format in which positions are stored. 9 | 10 | Optionally, you can also pass an object with flags for customizing the normalization process. Currently, the following flags are supported: 11 | 12 | * `backwards_compatible`: normalize in a way compatible with a previous implementation; intentionally not documented (very much); will eventually go away 13 | * `reference_errors`: if set to true, read accesses to global variables will be normalized in such a way that they throw a ReferenceError for undefined and undeclared globals; since this leads to significant code bloat in programs using the DOM and the standard library, this flag is set to false by default, meaning that reads of undefined/undeclared globals return undefined and don't throw an exception 14 | * `unfold_ifs`: if set to true, if statements will be unfolded so that at most one branch is non-trivial 15 | * `unify_ret`: rewrite functions so that they only have one single `return` statement at the very end 16 | -------------------------------------------------------------------------------- /normalizer/doc/normalization.md: -------------------------------------------------------------------------------- 1 | Description of the Normalization 2 | ================================ 3 | 4 | 5 | JavaScript Normal Form 6 | ---------------------- 7 | 8 | A program in JavaScript Normal Form (JSNF) obeys the following grammar: 9 | 10 | Program ::= (function(__global) { Decl? Stmt* })(typeof global === 'undefined' ? this : global); 11 | 12 | Decl ::= var x1, x2, ..., xn; 13 | 14 | Stmt ::= x = (function f(y1, ..., yn) { Decl? Stmt+ }); 15 | | x = LITERAL; 16 | | x = null; 17 | | x = this; 18 | | x = [ y1, ..., yn ]; 19 | | x = { Prop* }; 20 | | x = y; 21 | | x = y[z]; 22 | | x[y] = z; 23 | | x = delete y; 24 | | x = delete y[z]; 25 | | x = UNOP y; 26 | | x = y BINOP z; 27 | | x = f(y1, ..., yn); 28 | | x = z[f](y1, ..., yn); 29 | | x = new f(y1, ..., yn); 30 | | return x; 31 | | return; 32 | | break l; 33 | | throw x; 34 | | ; 35 | | debugger; 36 | | l: Stmt 37 | | if(x) { Stmt+ } else { Stmt+ } 38 | | while(x) { Stmt+ } 39 | | for(x in y) { Stmt+ } 40 | | try { Stmt+ } catch(x) { Stmt+ } 41 | | try { Stmt+ } finally { Stmt+ } 42 | 43 | Prop ::= STRING : y 44 | | get p() { Stmt+ } 45 | | set p(x) { Stmt+ } 46 | 47 | Here, `x`, `y`, `z`, `f`, `p`, `l` and variants are names. The terminal `LITERAL` stands for a string, number, boolean or regular expression literal. 48 | `STRING` is a string literal, `UNOP` is a unary operator, and `BINOP` is a binary operator. 49 | 50 | Names always refer to local variables, whereas references to global variables are rewritten into appropriate property reads or writes on `__global`. 51 | If the normalizer is passed the `reference_errors` option, reads of global variables will further be rewritten to throw a `ReferenceError` exception if the variable in question has not been declared or defined. 52 | The sole exception to this are direct calls to `eval`, where the callee expression `eval` may be a reference to a global variable. 53 | This is necessary to preserve semantics. 54 | 55 | The translation to normal form introduces (lots of) temporary variables named `tmp0`, `tmp1` and so on. The normalizer does not check for name clashes with existing variables, although it undoubtedly should. 56 | 57 | Note that `for` and `do` loops are desugared into `while` loops, `continue` statements are converted into `break` statements. This sometimes results in (moderate amounts of) code duplication. All `break` statements in the normalized program have an explicit target label. 58 | 59 | If the normalizer is passed the `unfold_ifs` option, `if` statements are further simplified so that at most one of their branches is non-trivial, i.e., contains a non-empty statement. 60 | 61 | If the normalizer is passed the `unify_ret` option, every function only contains one single `return` statement at the very end of the function; all other `return` statements are converted into assignments to a result variable and a following `break` statement. The name of the return variable is stored in attribute `ret_var` of the function AST node. The normalized program will not contain empty return statements of the form `return;`. 62 | 63 | 64 | 65 | Position information 66 | -------------------- 67 | 68 | If the original AST has position information, then so does the normalized AST, with every normalized AST node having the same position as the node it originated from. 69 | 70 | However, we do not use the Esprima position annotations, which somewhat awkwardly distribute position information across several different properties. 71 | Instead, the position information for node `nd` is stored in `nd.attr.pos`, and uses the ADT defined in module `common/lib/position.js`. 72 | 73 | Source positions include the URL of their containing script, but Esprima ASTs do not usually store a program's URL. 74 | When the normalizer is passed an AST, it first checks whether the root node has the (non-standard) property `url`, and if so takes this to be the program's URL. 75 | Otherwise, it checks whether the `options` object (see above) has a `url` property. 76 | If neither is the case, the URL defaults to ``. 77 | -------------------------------------------------------------------------------- /normalizer/find_suspicious_positions.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var esprima = require('esprima'); 3 | var normalizer = require(__dirname + '/lib/normalizer'); 4 | var astutil = require(__dirname + '/../common/lib/ast.js'); 5 | 6 | var last_fun_offset = -1; 7 | 8 | function find_suspicious(nd) { 9 | if(!nd) 10 | return; 11 | 12 | if(nd.type === 'FunctionExpression' && nd.attr.pos) { 13 | last_fun_offset = nd.attr.pos.start_offset; 14 | } else if(nd.type === 'AssignmentExpression' && 15 | nd.right.type === 'MemberExpression' && 16 | nd.attr.pos.start_offset === last_fun_offset) { 17 | console.error("found suspicious position information (offset " + 18 | nd.attr.pos.start_offset + ")"); 19 | process.exit(-1); 20 | } 21 | 22 | astutil.forEachChild(nd, find_suspicious); 23 | } 24 | 25 | var ast = esprima.parse(fs.readFileSync(process.argv[2], 'utf-8'), 26 | { loc: true, range: true }); 27 | var normalized = normalizer.normalize(ast); 28 | //console.log(JSON.stringify(normalized)); 29 | find_suspicious(normalized); -------------------------------------------------------------------------------- /normalizer/lib/cflow.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | /** 13 | * Helper function for determining whether a piece of code may terminate normally, or whether 14 | * it always returns/breaks/throws an exception. 15 | */ 16 | if(typeof define !== 'function') { 17 | var define = require('amdefine')(module); 18 | } 19 | 20 | define(function(require, exports) { 21 | var mayCompleteNormally = exports.mayCompleteNormally = function(nd) { 22 | switch(nd.type) { 23 | case 'ReturnStatement': 24 | case 'BreakStatement': 25 | case 'ContinueStatement': 26 | case 'ThrowStatement': 27 | return false; 28 | case 'IfStatement': 29 | return mayCompleteNormally(nd.consequent) || nd.alternate && mayCompleteNormally(nd.alternate); 30 | case 'WithStatement': 31 | return mayCompleteNormally(nd.body); 32 | case 'BlockStatement': 33 | for(var i=0;i"; 136 | 137 | /** Utility function to copy position information of old_node onto new_nodes, 138 | * unless they already have positions. This copy is recursive, traversing 139 | * all child nodes of any new nodes that do not have positions yet. */ 140 | function inheritPosition(new_nodes, old_node) { 141 | var old_pos; 142 | if(ast.hasPosition(old_node)) { 143 | old_pos = ast.getPosition(old_node); 144 | } else if(old_node.range && old_node.loc) { 145 | old_pos = new position.Position(url, 146 | old_node.loc.start.line, old_node.range[0], 147 | old_node.loc.end.line, old_node.range[1]); 148 | } else { 149 | old_pos = position.DUMMY_POS; 150 | } 151 | 152 | function help(nd) { 153 | if(Array.isArray(nd)) 154 | nd.forEach(help); 155 | if(nd && typeof nd.type === 'string' && !ast.hasPosition(nd)) { 156 | ast.setPosition(nd, old_pos.clone()); 157 | ast.forEachChild(nd, help); 158 | } 159 | } 160 | 161 | help(new_nodes); 162 | return new_nodes; 163 | } 164 | 165 | /** Utility function for attaching source code comment to normalized statements. */ 166 | function attachComment(stmts) { 167 | if(!stmts.length) 168 | return; 169 | 170 | var comment_text = ""; 171 | for(var i=1;i 213 | * var ret_var; 214 | * ret_label: { 215 | * } 216 | * return ret_var; 217 | * 218 | * 219 | * Then, every return statement return e; is replaced by 220 | * ret_var = e; break ret_label;. */ 221 | var ret_label = null, ret_var = null; 222 | if(options.unify_ret && (root.type === 'FunctionDeclaration' || 223 | root.type === 'FunctionExpression')) { 224 | ret_label = genTmp(true); 225 | ret_var = genTmp(); 226 | } 227 | 228 | /** Normalize an expression with the given target variable. If the target is null, 229 | * a new temporary name is generated when necessary. */ 230 | function normalizeExpression(nd, target) { 231 | var res; 232 | function getTarget() { 233 | return target || (target = genTmp()); 234 | } 235 | 236 | switch(nd.type) { 237 | case 'Literal': 238 | // target = literal; 239 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), nd))]; 240 | break; 241 | 242 | case 'Identifier': 243 | var tmp = null; 244 | if(!isTmp(nd.name) && scope.isGlobal(nd.name)) { 245 | // global reads are rewritten into property accesses on __global; however, reading an undeclared, undefined global 246 | // should result in a ReferenceError, so we (optionally) introduce an if statement checking whether the global has 247 | // been declared/defined, and throw an error if not 248 | 249 | // temporary to hold the name of the global 250 | tmp = genTmp(); 251 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp), new ast.Literal(nd.name)))]; 252 | 253 | if(!options.reference_errors || scope.isDeclaredGlobal(nd.name)) { 254 | // target = __global[tmp]; 255 | res[1] = new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), 256 | new ast.MemberExpression(new ast.Identifier('__global'), new ast.Identifier(tmp), true))); 257 | } else { 258 | // check for shadowing of ReferenceError; give up if this happens 259 | // TODO: even if it isn't shadowed, some clown may have overwritten ReferenceError... 260 | if(!scope.isGlobal('ReferenceError')) 261 | throw new Error("global variable ReferenceError is shadowed"); 262 | 263 | /* tmp2 = x in __global; 264 | * if(tmp2) { 265 | * target = __global[tmp2]; 266 | * } else { 267 | * tmp3 = 'ReferenceError'; 268 | * tmp4 = __global[tmp3]; 269 | * tmp5 = new tmp4(); 270 | * throw tmp5; 271 | * } */ 272 | var tmp2 = genTmp(), tmp3 = genTmp(), tmp4 = genTmp(), tmp5 = genTmp(); 273 | res = res.concat(new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp2), new ast.BinaryExpression('in', new ast.Identifier(tmp), new ast.Identifier('__global')))), 274 | mkIf(tmp2, 275 | [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), new ast.MemberExpression(new ast.Identifier('__global'), new ast.Identifier(tmp), true)))], 276 | [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp3), new ast.Literal('ReferenceError'))), 277 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp4), new ast.MemberExpression(new ast.Identifier('__global'), new ast.Identifier(tmp3), true))), 278 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp5), new ast.NewExpression(new ast.Identifier(tmp4), []))), 279 | new ast.ThrowStatement(new ast.Identifier(tmp5))])); 280 | } 281 | } else { 282 | // locals are easy: target = x; 283 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), new ast.Identifier(nd.name)))]; 284 | } 285 | 286 | // handle possible 'with' bindings 287 | var with_bindings = scope.possibleWithBindings(nd.name); 288 | if(with_bindings.length) { 289 | var prelude = null; 290 | 291 | // load name of variable into 'tmp'; store code to do so into 'prelude' 292 | if(tmp) { 293 | prelude = res[0]; 294 | res.shift(); 295 | } else { 296 | tmp = genTmp(); 297 | prelude = new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp), new ast.Literal(nd.name))); 298 | } 299 | 300 | with_bindings.forEach(function(with_var) { 301 | /* 302 | * tmp2 = tmp in with_var; 303 | * if(tmp2) { 304 | * target = with_var[tmp]; 305 | * } else { 306 | * ... 307 | * } 308 | */ 309 | var tmp2 = genTmp(); 310 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp2), new ast.BinaryExpression('in', new ast.Identifier(tmp), new ast.Identifier(with_var)))), 311 | new ast.IfStatement(new ast.Identifier(tmp2), 312 | mkBlock([new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), new ast.MemberExpression(new ast.Identifier(with_var), new ast.Identifier(tmp), true)))]), 313 | mkBlock(res))]; 314 | }); 315 | 316 | res.unshift(prelude); 317 | } 318 | break; 319 | 320 | case 'ArrayExpression': 321 | // allocate one temporary variable per array element (y1, ..., yn) 322 | var elt_tmps = genTmps(nd.elements.length); 323 | // recursively normalize array element expressions, skipping over omitted elements; the temporary will then 324 | // keep its initial undefined value, which is what we want 325 | var elements = nd.elements.flatmap(function(elt, i) { return elt ? normalizeExpression(elt, elt_tmps[i]) : []; }); 326 | // target = [y1, ..., yn]; 327 | res = elements.concat(new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), 328 | new ast.ArrayExpression(elt_tmps.map(function(tmp) { return new ast.Identifier(tmp); }))))); 329 | break; 330 | 331 | case 'ObjectExpression': 332 | // allocate one temporary variable per property; we may not need all of them if there are getters or setters 333 | var prop_tmps = genTmps(nd.properties.length); 334 | var props = []; 335 | var body = nd.properties.flatmap(function(prop, i) { 336 | switch(prop.kind) { 337 | case 'init': 338 | props[props.length] = new ast.Property(prop.key, new ast.Identifier(prop_tmps[i]), 'init'); 339 | inheritPosition(props[props.length-1], prop); 340 | // recursively normalize property value expression 341 | return normalizeExpression(prop.value, prop_tmps[i]); 342 | case 'get': 343 | case 'set': 344 | // recursively normalize getter/setter 345 | var funexpr = normalizeEntity(prop.value, new scopes.FunctionScope(scope, prop.value)); 346 | props[props.length] = new ast.Property(prop.key, funexpr, prop.kind); 347 | inheritPosition(props[props.length-1], prop); 348 | return []; 349 | default: 350 | throw new Error(); 351 | } 352 | }); 353 | res = body.concat(new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), 354 | new ast.ObjectExpression(props)))); 355 | break; 356 | 357 | case 'MemberExpression': 358 | var base_tmp = genTmp(), index_tmp = genTmp(); 359 | var base = normalizeExpression(getBase(nd), base_tmp); 360 | var index = normalizeExpression(getIndex(nd), index_tmp); 361 | var idx = new ast.MemberExpression(new ast.Identifier(base_tmp), new ast.Identifier(index_tmp), true); 362 | // remember whether this member expression was a computed one originally 363 | if(nd.computed) 364 | ast.setAttribute(idx, 'isComputed', true); 365 | res = base.concat(index, 366 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), idx))); 367 | break; 368 | 369 | case 'ThisExpression': 370 | if(scope instanceof scopes.GlobalScope) 371 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), new ast.Identifier('__global')))]; 372 | else 373 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), new ast.ThisExpression()))]; 374 | break; 375 | 376 | case 'FunctionExpression': 377 | var fn = normalizeEntity(nd, new scopes.FunctionScope(scope, nd)); 378 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), fn))]; 379 | break; 380 | 381 | case 'AssignmentExpression': 382 | if(nd.operator === '=') { 383 | // simple assignments are handled similar to case 'Identifier' above 384 | if(nd.left.type === 'Identifier') { 385 | var res, tmp = null, right; 386 | var with_bindings = scope.possibleWithBindings(nd.left.name); 387 | if(!isTmp(nd.left.name) && scope.isGlobal(nd.left.name)) { 388 | tmp = genTmp(); 389 | right = normalizeExpression(nd.right, getTarget()); 390 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp), new ast.Literal(nd.left.name))), 391 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.MemberExpression(new ast.Identifier('__global'), new ast.Identifier(tmp), true), 392 | new ast.Identifier(getTarget())))]; 393 | } else { 394 | // mark variables that are written across scopes 395 | if(!scope.isLocal(nd.left.name)) 396 | ast.setAttribute(scope.lookup(nd.left.name), 'exposed', true); 397 | 398 | if(target || with_bindings.length) { 399 | right = normalizeExpression(nd.right, getTarget()); 400 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(nd.left.name), new ast.Identifier(getTarget())))]; 401 | } else { 402 | target = nd.left.name; 403 | right = normalizeExpression(nd.right, nd.left.name); 404 | res = []; 405 | } 406 | } 407 | 408 | // handle 'with' 409 | if(with_bindings.length) { 410 | var prelude = null; 411 | if(tmp) { 412 | prelude = res[0]; 413 | res.shift(); 414 | } else { 415 | tmp = genTmp(); 416 | prelude = new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp), new ast.Literal(nd.left.name))); 417 | } 418 | 419 | with_bindings.forEach(function(with_var) { 420 | var tmp2 = genTmp(); 421 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp2), new ast.BinaryExpression('in', new ast.Identifier(tmp), new ast.Identifier(with_var)))), 422 | new ast.IfStatement(new ast.Identifier(tmp2), 423 | mkBlock([new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.MemberExpression(new ast.Identifier(with_var), new ast.Identifier(tmp), true), new ast.Identifier(getTarget())))]), 424 | mkBlock(res))]; 425 | }); 426 | 427 | res.unshift(prelude); 428 | } 429 | 430 | res = right.concat(res); 431 | } else if(nd.left.type === 'MemberExpression') { 432 | var base_tmp = genTmp(), index_tmp = genTmp(); 433 | var base = normalizeExpression(getBase(nd.left), base_tmp), 434 | index = normalizeExpression(getIndex(nd.left), index_tmp); 435 | var lhs = new ast.MemberExpression(new ast.Identifier(base_tmp), new ast.Identifier(index_tmp), true); 436 | 437 | if(nd.left.computed) 438 | ast.setAttribute(lhs, 'isComputed', true); 439 | 440 | var rhs_comp = normalizeExpression(nd.right, getTarget()); 441 | res = base.concat(index, rhs_comp, new ast.ExpressionStatement(new ast.AssignmentExpression('=', lhs, new ast.Identifier(getTarget())))); 442 | } else { 443 | throw new Error("unexpected lhs of type " + nd.left.type); 444 | } 445 | } else { 446 | // compound assignments are desugared into normal assignments and then rewritten recursively 447 | var op = nd.operator.substring(0, nd.operator.length-1); 448 | var lhs = nd.left, rhs = nd.right; 449 | 450 | if(nd.left.type === 'Identifier') { 451 | var tmp = genTmp(); 452 | res = normalizeExpression(rhs, tmp) 453 | .concat(normalizeExpression(new ast.AssignmentExpression('=', new ast.Identifier(nd.left.name), new ast.BinaryExpression(op, new ast.Identifier(nd.left.name), new ast.Identifier(tmp))))); 454 | } else if(nd.left.type === 'MemberExpression') { 455 | var tmp = genTmp(), trg = getTarget(); 456 | var base_tmp = genTmp(), index_tmp = genTmp(), extra = genTmp(); 457 | 458 | res = normalizeExpression(getBase(lhs), base_tmp) 459 | .concat(normalizeExpression(getIndex(lhs), index_tmp), 460 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(extra), 461 | new ast.MemberExpression(new ast.Identifier(base_tmp), new ast.Identifier(index_tmp), true))), 462 | normalizeExpression(rhs, tmp), 463 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(trg), 464 | new ast.BinaryExpression(op, new ast.Identifier(extra), new ast.Identifier(tmp)))), 465 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.MemberExpression(new ast.Identifier(base_tmp), new ast.Identifier(index_tmp), true), new ast.Identifier(target)))); 466 | } else { 467 | throw new Error("unexpected lhs"); 468 | } 469 | } 470 | break; 471 | 472 | case 'CallExpression': 473 | if(nd.callee.type === 'MemberExpression') { 474 | var base_tmp = genTmp(), index_tmp = genTmp(); 475 | var base = normalizeExpression(getBase(nd.callee), base_tmp); 476 | var index = normalizeExpression(getIndex(nd.callee), index_tmp); 477 | var arg_tmps = genTmps(nd.arguments.length); 478 | var args = nd.arguments.flatmap(function(arg, i) { return normalizeExpression(arg, arg_tmps[i]); }); 479 | var callee = new ast.MemberExpression(new ast.Identifier(base_tmp), new ast.Identifier(index_tmp), true); 480 | 481 | if(nd.callee.computed) 482 | ast.setAttribute(callee, 'isComputed', true); 483 | 484 | res = base.concat(index, args, 485 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), 486 | new ast.CallExpression(callee, arg_tmps.map(function(tmp) { return new ast.Identifier(tmp); }))))); 487 | break; 488 | } else if(nd.callee.type === 'Identifier') { 489 | if(nd.callee.name === 'eval') { 490 | // TODO: handle 'eval' inside 'with' 491 | var arg_tmps = genTmps(nd.arguments.length); 492 | var args = nd.arguments.flatmap(function(arg, i) { return normalizeExpression(arg, arg_tmps[i]); }); 493 | res = args.concat(new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), 494 | new ast.CallExpression(nd.callee, arg_tmps.map(function(tmp) { return new ast.Identifier(tmp); }))))); 495 | } else { 496 | var tmp = genTmp(); 497 | var fn = normalizeExpression(nd.callee, tmp); 498 | var arg_tmps = genTmps(nd.arguments.length); 499 | var args = nd.arguments.flatmap(function(arg, i) { return normalizeExpression(arg, arg_tmps[i]); }); 500 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), 501 | new ast[nd.type](new ast.Identifier(tmp), arg_tmps.map(function(tmp) { return new ast.Identifier(tmp); }))))]; 502 | 503 | var with_bindings = scope.possibleWithBindings(nd.callee.name); 504 | if(with_bindings.length) { 505 | var name_tmp = genTmp(); 506 | var prelude = new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(name_tmp), new ast.Literal(nd.callee.name))); 507 | 508 | with_bindings.forEach(function(with_var) { 509 | var tmp2 = genTmp(); 510 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp2), new ast.BinaryExpression('in', new ast.Identifier(name_tmp), new ast.Identifier(with_var)))), 511 | new ast.IfStatement(new ast.Identifier(tmp2), 512 | mkBlock([new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), 513 | new ast.CallExpression(new ast.MemberExpression(new ast.Identifier(with_var), new ast.Identifier(name_tmp), true), 514 | arg_tmps.map(function(tmp) { return new ast.Identifier(tmp); }))))]), 515 | mkBlock(res))]; 516 | }); 517 | 518 | res.unshift(prelude); 519 | } 520 | 521 | res = fn.concat(args, res); 522 | } 523 | break; 524 | } 525 | 526 | case 'NewExpression': 527 | var tmp = genTmp(); 528 | var fn = normalizeExpression(nd.callee, tmp); 529 | var arg_tmps = genTmps(nd.arguments.length); 530 | var args = nd.arguments.flatmap(function(arg, i) { return normalizeExpression(arg, arg_tmps[i]); }); 531 | res = fn.concat(args, new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), 532 | new ast[nd.type](new ast.Identifier(tmp), arg_tmps.map(function(tmp) { return new ast.Identifier(tmp); }))))); 533 | break; 534 | 535 | case 'SequenceExpression': 536 | var n = nd.expressions.length; 537 | res = nd.expressions.flatmap(function(expr, i) { 538 | if(i < n - 1) 539 | return normalizeExpression(expr); 540 | return normalizeExpression(expr, target); 541 | }); 542 | break; 543 | 544 | case 'LogicalExpression': 545 | var ltmp, l, r; 546 | if(nd.operator === '&&') { 547 | ltmp = genTmp(); 548 | l = normalizeExpression(nd.left, ltmp); 549 | res = mkIf(ltmp, normalizeExpression(nd.right, getTarget()), 550 | [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), new ast.Identifier(ltmp)))]); 551 | res = l.concat(res); 552 | } else if(nd.operator === '||') { 553 | ltmp = genTmp(); 554 | l = normalizeExpression(nd.left, ltmp); 555 | res = mkIf(ltmp, [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), new ast.Identifier(ltmp)))], 556 | normalizeExpression(nd.right, getTarget())); 557 | res = l.concat(res); 558 | } else { 559 | throw new Error("unknown logical expression"); 560 | } 561 | break; 562 | 563 | case 'BinaryExpression': 564 | var ltmp = genTmp(), rtmp = genTmp(); 565 | l = normalizeExpression(nd.left, ltmp); 566 | r = normalizeExpression(nd.right, rtmp); 567 | res = new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), 568 | new ast.BinaryExpression(nd.operator, new ast.Identifier(ltmp), new ast.Identifier(rtmp)))); 569 | res = l.concat(r, res); 570 | break; 571 | 572 | case 'ConditionalExpression': 573 | var tmp = genTmp(); 574 | res = normalizeExpression(nd.test, tmp) 575 | .concat(mkIf(tmp, normalizeExpression(nd.consequent, target), 576 | normalizeExpression(nd.alternate, target))); 577 | break; 578 | 579 | case 'UpdateExpression': 580 | var op = nd.operator === '++' ? '+' : '-'; 581 | // postfix expressions in void context are handled like prefix expressions 582 | if(!nd.prefix && target) { 583 | if(nd.argument.type === 'Identifier') { 584 | res = normalizeExpression(nd.argument, target) 585 | .concat(normalizeExpression(new ast.AssignmentExpression('=', new ast.Identifier(nd.argument.name), 586 | new ast.BinaryExpression(op, new ast.Identifier(nd.argument.name), new ast.Literal(1))))); 587 | 588 | } else if(nd.argument.type === 'MemberExpression') { 589 | var trg = getTarget(); 590 | var base_tmp = genTmp(), index_tmp = genTmp(), extra = genTmp(), extra_extra = genTmp(); 591 | res = normalizeExpression(getBase(nd.argument), base_tmp) 592 | .concat(normalizeExpression(getIndex(nd.argument), index_tmp), 593 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(trg), 594 | new ast.MemberExpression(new ast.Identifier(base_tmp), new ast.Identifier(index_tmp), true))), 595 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(extra_extra), new ast.Literal(1))), 596 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(extra), new ast.BinaryExpression(op, new ast.Identifier(target), new ast.Identifier(extra_extra)))), 597 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.MemberExpression(new ast.Identifier(base_tmp), new ast.Identifier(index_tmp), true), new ast.Identifier(extra)))); 598 | } else { 599 | throw new Error("unexpected operand to postfix expression"); 600 | } 601 | } else { 602 | if(nd.argument.type === 'Identifier') { 603 | res = normalizeExpression(new ast.AssignmentExpression('=', nd.argument, new ast.BinaryExpression(op, new ast.Identifier(nd.argument.name), new ast.Literal(1))), 604 | target); 605 | } else if(nd.argument.type === 'MemberExpression') { 606 | var trg = getTarget(); 607 | var base_tmp = genTmp(), index_tmp = genTmp(), extra = genTmp(), extra_extra = genTmp(); 608 | res = normalizeExpression(getBase(nd.argument), base_tmp) 609 | .concat(normalizeExpression(getIndex(nd.argument), index_tmp), 610 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(extra), 611 | new ast.MemberExpression(new ast.Identifier(base_tmp), new ast.Identifier(index_tmp), true))), 612 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(extra_extra), new ast.Literal(1))), 613 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(trg), 614 | new ast.BinaryExpression(op, new ast.Identifier(extra), new ast.Identifier(extra_extra)))), 615 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.MemberExpression(new ast.Identifier(base_tmp), new ast.Identifier(index_tmp), true), 616 | new ast.Identifier(target)))); 617 | } else { 618 | throw new Error("unexpected operand to prefix expression"); 619 | } 620 | } 621 | break; 622 | 623 | case 'UnaryExpression': 624 | var op = nd.operator; 625 | if(op === 'delete') { 626 | if(nd.argument.type === 'Identifier') { 627 | if(!isTmp(nd.argument.name) && scope.isGlobal(nd.argument.name)) { 628 | var trg = getTarget(); 629 | var tmp = genTmp(); 630 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp), new ast.Literal(nd.argument.name))), 631 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(trg), 632 | new ast.UnaryExpression('delete', 633 | new ast.MemberExpression(new ast.Identifier('__global'), 634 | new ast.Identifier(tmp), true))))]; 635 | } else { 636 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), nd))]; 637 | } 638 | } else if(nd.argument.type === 'MemberExpression') { 639 | var trg = getTarget(); 640 | var base_tmp = genTmp(), index_tmp = genTmp(); 641 | res = normalizeExpression(getBase(nd.argument), base_tmp) 642 | .concat(normalizeExpression(getIndex(nd.argument), index_tmp), 643 | new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(trg), 644 | new ast.UnaryExpression('delete', 645 | new ast.MemberExpression(new ast.Identifier(base_tmp), 646 | new ast.Identifier(index_tmp), true))))); 647 | } else { 648 | throw new Error(); 649 | } 650 | } else { 651 | var tmp = genTmp(); 652 | res = normalizeExpression(nd.argument, tmp) 653 | .concat(new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(getTarget()), 654 | new ast.UnaryExpression(op, new ast.Identifier(tmp))))); 655 | } 656 | break; 657 | 658 | default: 659 | throw new Error("unknown expression type: " + nd.type); 660 | } 661 | return inheritPosition(res, nd); 662 | } 663 | 664 | function normalizeStatement(nd, brk_label, cont_label) { 665 | var res; 666 | 667 | function rec(stmt) { 668 | return normalizeStatement(stmt, brk_label, cont_label); 669 | } 670 | 671 | switch(nd.type) { 672 | case 'EmptyStatement': 673 | res = []; 674 | attachComment(res, nd); 675 | break; 676 | 677 | case 'ExpressionStatement': 678 | res = normalizeExpression(nd.expression); 679 | attachComment(res, nd); 680 | break; 681 | 682 | // variable declarations are collected by normalizeEntity(); all we need to do here is to extract initializers into assignments 683 | case 'VariableDeclaration': 684 | res = nd.declarations.flatmap(rec); 685 | attachComment(res, nd); 686 | break; 687 | 688 | case 'VariableDeclarator': 689 | if(nd.init) 690 | res = normalizeExpression(new ast.AssignmentExpression('=', new ast.Identifier(nd.id.name), nd.init)); 691 | else 692 | res = []; 693 | break; 694 | 695 | // function declarations are collected by normalizeEntity() 696 | case 'FunctionDeclaration': 697 | res = []; 698 | break; 699 | 700 | case 'BlockStatement': 701 | res = nd.body.flatmap(rec); 702 | break; 703 | 704 | case 'ReturnStatement': 705 | if(options.unify_ret) { 706 | res = nd.argument ? normalizeExpression(nd.argument, ret_var) : []; 707 | res.push(new ast.BreakStatement(new ast.Identifier(ret_label))); 708 | } else { 709 | if(nd.argument) { 710 | var tmp = genTmp(); 711 | res = normalizeExpression(nd.argument, tmp).concat(new ast.ReturnStatement(new ast.Identifier(tmp))); 712 | } else { 713 | res = [new ast.ReturnStatement(null)]; 714 | } 715 | } 716 | attachComment(res, nd); 717 | break; 718 | 719 | case 'DebuggerStatement': 720 | res = [new ast.DebuggerStatement()]; 721 | attachComment(res, nd); 722 | break; 723 | 724 | case 'IfStatement': 725 | var tmp = genTmp(); 726 | var res = normalizeExpression(nd.test, tmp); 727 | var thenBranch = rec(nd.consequent); 728 | var elseBranch = nd.alternate ? rec(nd.alternate) : []; 729 | res = res.concat(mkIf(tmp, thenBranch, elseBranch)); 730 | attachComment(res, "if(", nd.test, ")"); 731 | break; 732 | 733 | case 'ThrowStatement': 734 | var tmp = genTmp(); 735 | res = normalizeExpression(nd.argument, tmp).concat(new ast.ThrowStatement(new ast.Identifier(tmp))); 736 | attachComment(res, nd); 737 | break; 738 | 739 | case 'TryStatement': 740 | // handle incompatibility between Esprima and Acorn ASTs 741 | if(!nd.handlers) 742 | nd.handlers = nd.handler ? [nd.handler] : []; 743 | 744 | if(nd.handlers.length > 0 && nd.finalizer) { 745 | res = rec(new ast.TryStatement(new ast.BlockStatement([new ast.TryStatement(nd.block, nd.guardedHandlers, nd.handlers, null)]), 746 | [], [], nd.finalizer)); 747 | } else if(nd.handlers.length > 0) { 748 | if(nd.guardedHandlers && nd.guardedHandlers.length > 0 || nd.handlers.length > 1) 749 | throw new Error("fancy catch clauses not supported"); 750 | 751 | var tryblock = rec(nd.block); 752 | scope = new scopes.CatchScope(scope, nd.handlers[0]); 753 | var catchblock = rec(nd.handlers[0].body); 754 | scope = scope.outer; 755 | 756 | res = [new ast.TryStatement(mkBlock(tryblock), [], [new ast.CatchClause(nd.handlers[0].param, mkBlock(catchblock))], null)]; 757 | } else if(nd.finalizer) { 758 | var tryblock = rec(nd.block); 759 | if(nd.finalizer.body.length === 0) { 760 | res = tryblock; 761 | } else { 762 | var finallyblock = rec(nd.finalizer); 763 | res = [new ast.TryStatement(mkBlock(tryblock), [], [], mkBlock(finallyblock))]; 764 | } 765 | } 766 | attachComment(res, "try"); 767 | break; 768 | 769 | case 'LabeledStatement': 770 | var stmts = normalizeStatement(nd.body, nd.label.name, isLoop(nd.body) ? nd.label.name : cont_label); 771 | res = [new ast.LabeledStatement(nd.label, mkBlock(stmts))]; 772 | attachComment(res, nd.label.name + ": { ... }"); 773 | break; 774 | 775 | case 'BreakStatement': 776 | if(nd.label) 777 | res = [nd]; 778 | else 779 | res = [new ast.BreakStatement(new ast.Identifier(brk_label))]; 780 | attachComment(res, nd); 781 | break; 782 | 783 | case 'ContinueStatement': 784 | if(nd.label) 785 | res = [new ast.BreakStatement(new ast.Identifier(nd.label.name))]; 786 | else 787 | res = [new ast.BreakStatement(new ast.Identifier(cont_label))]; 788 | attachComment(res, nd); 789 | break; 790 | 791 | case 'WhileStatement': 792 | var condtmp = genTmp(); 793 | var brk_lbl = genTmp(true), cont_lbl = genTmp(true); 794 | 795 | // initial computation of condition 796 | var cond1 = normalizeExpression(nd.test, condtmp); 797 | // while body 798 | var body = [new ast.LabeledStatement(new ast.Identifier(cont_lbl), mkBlock(normalizeStatement(nd.body, brk_lbl, cont_lbl)))]; 799 | // computation of updated condition 800 | var cond2 = normalizeExpression(nd.test, condtmp); 801 | res = cond1.concat(new ast.LabeledStatement(new ast.Identifier(brk_lbl), 802 | new ast.BlockStatement([new ast.WhileStatement(new ast.Identifier(condtmp), 803 | mkBlock(body.concat(cond2)))]))); 804 | attachComment(res, "while(", nd.test, ")"); 805 | break; 806 | 807 | case 'DoWhileStatement': 808 | var tmp = genTmp(); 809 | var brk_lbl = genTmp(true), cont_lbl = genTmp(true); 810 | 811 | var body = [new ast.LabeledStatement(new ast.Identifier(cont_lbl), mkBlock(normalizeStatement(nd.body, brk_lbl, cont_lbl)))]; 812 | var cond = normalizeExpression(nd.test, tmp); 813 | res = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(tmp), new ast.Literal(true))), 814 | new ast.LabeledStatement(new ast.Identifier(brk_lbl), new ast.BlockStatement([new ast.WhileStatement(new ast.Identifier(tmp), 815 | mkBlock(body.concat(cond)))]))]; 816 | attachComment(res, "do { ... } while(", nd.test, ");"); 817 | break; 818 | 819 | case 'ForInStatement': 820 | if(nd.left.type === 'VariableDeclaration') { 821 | res = rec(nd.left).concat(rec(new ast.ForInStatement(new ast.Identifier(nd.left.declarations[0].id.name), nd.right, nd.body))); 822 | } else if(nd.left.type === 'Identifier') { 823 | var tmp = genTmp(), brk_lbl = genTmp(true), cont_lbl = genTmp(true); 824 | 825 | var init = normalizeExpression(nd.right, tmp); 826 | 827 | var body = [new ast.LabeledStatement(new ast.Identifier(cont_lbl), mkBlock(normalizeStatement(nd.body, brk_lbl, cont_lbl)))]; 828 | 829 | var loopVar; 830 | if(scope.isLocal(nd.left.name)) { 831 | loopVar = nd.left.name; 832 | } else { 833 | loopVar = genTmp(); 834 | body = normalizeExpression(new ast.AssignmentExpression('=', new ast.Identifier(nd.left.name), new ast.Identifier(loopVar))) 835 | .concat(body); 836 | } 837 | 838 | res = init.concat(new ast.LabeledStatement(new ast.Identifier(brk_lbl), 839 | new ast.BlockStatement([new ast.ForInStatement(new ast.Identifier(loopVar), new ast.Identifier(tmp), 840 | mkBlock(body))]))); 841 | } else { 842 | // TODO: support member expressions as nd.left 843 | throw new Error("cannot handle for-in loop"); 844 | } 845 | attachComment(res, "for(", nd.left, " in ", nd.right, ") { ... }"); 846 | break; 847 | 848 | case 'ForStatement': 849 | var init = nd.init ? (nd.init.type === 'VariableDeclaration' ? rec(nd.init) : normalizeExpression(nd.init)) : []; 850 | var condVar = genTmp(); 851 | var cond1, cond2; 852 | if(!nd.test) { 853 | cond1 = [new ast.ExpressionStatement(new ast.AssignmentExpression('=', new ast.Identifier(condVar), new ast.Literal(true)))]; 854 | cond2 = []; 855 | } else { 856 | cond1 = normalizeExpression(nd.test, condVar); 857 | cond2 = normalizeExpression(nd.test, condVar); 858 | } 859 | var update = nd.update ? normalizeExpression(nd.update) : []; 860 | var brk_lbl = genTmp(true), cont_lbl = genTmp(true); 861 | var body = [new ast.LabeledStatement(new ast.Identifier(cont_lbl), mkBlock(normalizeStatement(nd.body, brk_lbl, cont_lbl)))]; 862 | res = init.concat(cond1, 863 | new ast.LabeledStatement(new ast.Identifier(brk_lbl), 864 | new ast.BlockStatement([new ast.WhileStatement(new ast.Identifier(condVar), 865 | mkBlock(body.concat(update, cond2)))]))); 866 | attachComment(res, "for(", nd.init || "", ";", nd.test || "", ";", nd.update || "", ") { ... }"); 867 | break; 868 | 869 | case 'SwitchStatement': 870 | var tmp = genTmp(), lbl = genTmp(true); 871 | 872 | var cond = normalizeExpression(nd.discriminant, tmp); 873 | 874 | // initialise default to single no-op statement 875 | var default_stmts = [new ast.EmptyStatement()]; 876 | var body = default_stmts; 877 | 878 | if(nd.cases) 879 | for(var i=nd.cases.length-1;i>=0;--i) { 880 | if(!nd.cases[i].test) { 881 | // overwrite default statements 882 | default_stmts.length = 0; 883 | Array.prototype.push.apply(default_stmts, nd.cases[i].consequent.flatmap(function(stmt) { return normalizeStatement(stmt, lbl, cont_label); })); 884 | } else { 885 | var all_stmts = nd.cases[i].consequent; 886 | for(var j=i+1;j 0) 1005 | body.unshift(new ast.VariableDeclaration(localDecls, 'var')); 1006 | 1007 | var fn_expr = new ast.FunctionExpression(root.id, root.params, new ast.BlockStatement(body)); 1008 | if(ret_var) 1009 | ast.setAttribute(fn_expr, 'ret_var', ret_var); 1010 | return inheritPosition(fn_expr, root); 1011 | } else if(root.type === 'Program') { 1012 | var body = root.body.flatmap(function(stmt) { return normalizeStatement(stmt); }); 1013 | 1014 | // declarations for locally declared functions become assignments to be inserted at the beginning of the program 1015 | var fundecls = scope.decls.flatmap(function(decl) { 1016 | if(decl.type === 'FunctionDeclaration') 1017 | return normalizeExpression(inheritPosition(new ast.AssignmentExpression('=', new ast.Identifier(decl.id.name), 1018 | new ast.FunctionExpression(options.backwards_compatible ? decl.id : null, 1019 | decl.params, decl.body)), decl)); 1020 | return []; 1021 | }); 1022 | 1023 | // variable declaration for temporaries, if necessary 1024 | var tmpdecls = tmps.length > 0 ? [new ast.VariableDeclaration(tmps, 'var')] : []; 1025 | 1026 | if(options.backwards_compatible) 1027 | insertNoOpAfterFinalIf(body); 1028 | 1029 | // whole program is wrapped into (function(__global) { ... })(typeof global === 'undefined' ? this : global); 1030 | return new ast.Program([new ast.ExpressionStatement(new ast.CallExpression(new ast.FunctionExpression(null, [new ast.Identifier("__global")], 1031 | new ast.BlockStatement(tmpdecls.concat(fundecls, body))), 1032 | [new ast.ConditionalExpression(new ast.BinaryExpression('===', new ast.UnaryExpression('typeof', new ast.Identifier('global')), new ast.Literal('undefined')), 1033 | new ast.ThisExpression(), new ast.Identifier("global"))]))]); 1034 | } 1035 | } 1036 | 1037 | return normalizeEntity(nd, new scopes.GlobalScope(nd)); 1038 | } 1039 | 1040 | exports.normalize = normalize; 1041 | }); 1042 | -------------------------------------------------------------------------------- /normalizer/lib/scope.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | /** 13 | * Scope objects keep track of name binding. Each scope object represents 14 | * either the global scope, a function scope, a catch clause scope, or 15 | * a 'with' scope. 16 | */ 17 | 18 | if(typeof define !== 'function') { 19 | var define = require('amdefine')(module); 20 | } 21 | 22 | define(function(require, exports) { 23 | var decls = require('./decls'); 24 | 25 | // abstract base class of all scopes 26 | function Scope(outer, decls) { 27 | this.outer = outer; 28 | this.decls = decls; 29 | } 30 | 31 | // is x a global variable in this scope? 32 | Scope.prototype.isGlobal = function(x) { 33 | return !this.isLocal(x) && this.outer.isGlobal(x); 34 | }; 35 | 36 | // does x have a declaration at the global level? 37 | Scope.prototype.isDeclaredGlobal = function(x) { 38 | return this.outer.isDeclaredGlobal(); 39 | }; 40 | 41 | // look up x among the local declarations in this scope 42 | Scope.prototype.localLookup = function(x) { 43 | for(var i=0;i> tmp2; 10 | tmp4[tmp5] = tmp3; 11 | tmp1 = 42; 12 | return tmp1; 13 | }; 14 | tmp7 = tmp0(); 15 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test56.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp0 = function() { 4 | var i, j; 5 | i = 42; 6 | j = i; 7 | return; 8 | }; 9 | tmp1 = tmp0(); 10 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test57.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp6; 3 | tmp0 = function(o) { 4 | var tmp1, tmp2, tmp3, tmp4, tmp5; 5 | tmp1 = o; 6 | tmp2 = "i"; 7 | tmp4 = o; 8 | tmp5 = "j"; 9 | tmp3 = 42; 10 | tmp4[tmp5] = tmp3; 11 | tmp1[tmp2] = tmp3; 12 | return; 13 | }; 14 | tmp6 = tmp0(); 15 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test58.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp3; 3 | tmp0 = function() { 4 | var f, tmp1; 5 | tmp1 = function() { 6 | var g, x; 7 | g = function() { 8 | var tmp2; 9 | tmp2 = x; 10 | return; 11 | }; 12 | return; 13 | }; 14 | f = tmp1(); 15 | return; 16 | }; 17 | tmp3 = tmp0(); 18 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test59.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function(x) { 4 | var tmp2, tmp3, tmp4, tmp5, tmp6; 5 | tmp3 = x; 6 | tmp4 = "f"; 7 | tmp2 = tmp3[tmp4]; 8 | if (tmp2) { 9 | x = tmp2; 10 | } else { 11 | tmp5 = x; 12 | tmp6 = "g"; 13 | x = tmp5[tmp6]; 14 | } 15 | return; 16 | }; 17 | tmp0 = "f"; 18 | __global[tmp0] = tmp1; 19 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test6.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; 3 | tmp2 = "x"; 4 | tmp0 = __global[tmp2]; 5 | tmp4 = "f"; 6 | tmp3 = __global[tmp4]; 7 | tmp1 = tmp3(); 8 | tmp5 = tmp0[tmp1]; 9 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test60.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0; 3 | try { 4 | tmp0 = ""; 5 | throw tmp0; 6 | } catch (e) { 7 | ; 8 | } 9 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test61.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0; 3 | tmp0 = ""; 4 | throw tmp0; 5 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test62.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function(o) { 4 | var tmp2, tmp3, tmp4; 5 | tmp3 = o; 6 | tmp4 = "x"; 7 | tmp2 = delete tmp3[tmp4]; 8 | return; 9 | }; 10 | tmp0 = "f"; 11 | __global[tmp0] = tmp1; 12 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test63.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function(o) { 4 | var tmp2; 5 | tmp2 = delete o; 6 | return; 7 | }; 8 | tmp0 = "f"; 9 | __global[tmp0] = tmp1; 10 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test64.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp1 = "Object"; 4 | tmp0 = delete __global[tmp1]; 5 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test65.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; 3 | tmp5 = "jQuery"; 4 | tmp1 = __global[tmp5]; 5 | tmp2 = "wait"; 6 | tmp3 = tmp1[tmp2]; 7 | tmp4 = 1; 8 | tmp0 = tmp3 - tmp4; 9 | tmp1[tmp2] = tmp0; 10 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test66.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | debugger; 3 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test67.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp3, tmp4, tmp5, tmp6; 3 | tmp0 = true; 4 | tmp1: { 5 | while (tmp0) { 6 | tmp2: { 7 | tmp4 = "alert"; 8 | tmp3 = __global[tmp4]; 9 | tmp5 = "stuck!"; 10 | tmp6 = tmp3(tmp5); 11 | } 12 | } 13 | } 14 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test68.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp2; 3 | tmp0 = function(target) { 4 | var tmp1; 5 | tmp1 = target; 6 | if (tmp1) { 7 | return; 8 | } else { 9 | ; 10 | } 11 | return; 12 | }; 13 | tmp2 = tmp0(); 14 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test69.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function() { 4 | var bar, tmp2; 5 | bar = function() { 6 | return; 7 | }; 8 | tmp2 = bar; 9 | return tmp2; 10 | }; 11 | tmp0 = "foo"; 12 | __global[tmp0] = tmp1; 13 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test7.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp0 = "x"; 4 | tmp1 = eval(tmp0); 5 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test70.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function() { 4 | var tmp2, tmp3, tmp4, tmp5; 5 | tmp5 = 'f'; 6 | tmp3 = __global[tmp5]; 7 | tmp4 = "prototype"; 8 | tmp2 = tmp3[tmp4]; 9 | return tmp2; 10 | }; 11 | tmp0 = "f"; 12 | __global[tmp0] = tmp1; 13 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test71.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp4; 3 | tmp0 = function f() { 4 | var tmp1, tmp2, tmp3; 5 | tmp2 = f; 6 | tmp3 = "prototype"; 7 | tmp1 = tmp2[tmp3]; 8 | return tmp1; 9 | }; 10 | tmp4 = tmp0(); 11 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test72.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13; 3 | tmp2 = 42; 4 | tmp1 = { 5 | x: tmp2 6 | }; 7 | tmp0 = "o"; 8 | __global[tmp0] = tmp1; 9 | tmp4 = "o"; 10 | tmp3 = __global[tmp4]; 11 | tmp6 = "alert"; 12 | tmp7 = tmp6 in tmp3; 13 | if (tmp7) { 14 | tmp5 = tmp3[tmp6]; 15 | } else { 16 | tmp5 = __global[tmp6]; 17 | } 18 | tmp9 = "x"; 19 | tmp10 = tmp9 in tmp3; 20 | if (tmp10) { 21 | tmp8 = tmp3[tmp9]; 22 | } else { 23 | tmp8 = __global[tmp9]; 24 | } 25 | tmp12 = 'alert'; 26 | tmp13 = tmp12 in tmp3; 27 | if(tmp13) { 28 | tmp11 = tmp3[tmp12](tmp8); 29 | } else { 30 | tmp11 = tmp5(tmp8); 31 | } 32 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test73.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function() { 4 | var o, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12; 5 | tmp2 = 42; 6 | o = { 7 | x: tmp2 8 | }; 9 | tmp3 = o; 10 | tmp5 = "alert"; 11 | tmp6 = tmp5 in tmp3; 12 | if (tmp6) { 13 | tmp4 = tmp3[tmp5]; 14 | } else { 15 | tmp4 = __global[tmp5]; 16 | } 17 | tmp8 = "x"; 18 | tmp9 = tmp8 in tmp3; 19 | if (tmp9) { 20 | tmp7 = tmp3[tmp8]; 21 | } else { 22 | tmp7 = __global[tmp8]; 23 | } 24 | tmp11 = 'alert'; 25 | tmp12 = tmp11 in tmp3; 26 | if(tmp12) { 27 | tmp10 = tmp3[tmp11](tmp7); 28 | } else { 29 | tmp10 = tmp4(tmp7); 30 | } 31 | return; 32 | }; 33 | tmp0 = "f"; 34 | __global[tmp0] = tmp1; 35 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test74.js: -------------------------------------------------------------------------------- 1 | (function (__global) { 2 | var tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; 3 | tmp1 = 0; 4 | tmp0 = 't'; 5 | __global[tmp0] = tmp1; 6 | tmp2 = 2; 7 | tmp7 = 't'; 8 | tmp5 = __global[tmp7]; 9 | tmp6 = tmp2; 10 | tmp4 = tmp5 + tmp6; 11 | tmp3 = 't'; 12 | __global[tmp3] = tmp4; 13 | }(typeof global === 'undefined' ? this : global)); 14 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test75.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function() { 4 | var i; 5 | i = 0; 6 | i = 1; 7 | return; 8 | }; 9 | tmp0 = "f"; 10 | __global[tmp0] = tmp1; 11 | })(typeof global === 'undefined' ? this : global); 12 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test76.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1, tmp2; 3 | tmp0 = 1; 4 | tmp1 = 2; 5 | tmp2 = 3; 6 | })(typeof global === 'undefined' ? this : global); 7 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test77.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function(o) { 4 | var x, tmp2, tmp3, tmp4, tmp5; 5 | x = 23; 6 | tmp2 = o; 7 | tmp4 = "x"; 8 | tmp5 = tmp4 in tmp2; 9 | if (tmp5) { 10 | tmp3 = tmp2[tmp4]; 11 | } else { 12 | tmp3 = x; 13 | } 14 | return tmp3; 15 | }; 16 | tmp0 = "f"; 17 | __global[tmp0] = tmp1; 18 | })(typeof global === 'undefined' ? this : global); 19 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test78.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function(o) { 4 | var x, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8; 5 | tmp2 = o; 6 | tmp3 = 23; 7 | tmp4 = "x"; 8 | tmp5 = tmp4 in tmp2; 9 | if (tmp5) { 10 | tmp2[tmp4] = tmp3; 11 | } else { 12 | x = tmp3; 13 | } 14 | tmp7 = 42; 15 | tmp6 = "y"; 16 | tmp8 = tmp6 in tmp2; 17 | if (tmp8) { 18 | tmp2[tmp6] = tmp7; 19 | } else { 20 | __global[tmp6] = tmp7; 21 | } 22 | return; 23 | }; 24 | tmp0 = "f"; 25 | __global[tmp0] = tmp1; 26 | })(typeof global === 'undefined' ? this : global); 27 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test79.js: -------------------------------------------------------------------------------- 1 | (function (__global) { 2 | var tmp0, tmp1, tmp15, tmp16; 3 | tmp1 = function(o) { 4 | var g, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14; 5 | tmp2 = o; 6 | tmp4 = 'g'; 7 | tmp5 = tmp4 in tmp2; 8 | if (tmp5) { 9 | tmp3 = tmp2[tmp4]; 10 | } else { 11 | tmp3 = g; 12 | } 13 | tmp7 = 'g'; 14 | tmp8 = tmp7 in tmp2; 15 | if (tmp8) { 16 | tmp6 = tmp2[tmp7](); 17 | } else { 18 | tmp6 = tmp3(); 19 | } 20 | tmp10 = 'h'; 21 | tmp11 = tmp10 in tmp2; 22 | if (tmp11) { 23 | tmp9 = tmp2[tmp10]; 24 | } else { 25 | tmp9 = __global[tmp10]; 26 | } 27 | tmp13 = 'h'; 28 | tmp14 = tmp13 in tmp2; 29 | if (tmp14) { 30 | tmp12 = tmp2[tmp13](); 31 | } else { 32 | tmp12 = tmp9(); 33 | } 34 | return; 35 | }; 36 | tmp0 = 'f'; 37 | __global[tmp0] = tmp1; 38 | tmp16 = function() { 39 | return; 40 | }; 41 | tmp15 = 'h'; 42 | __global[tmp15] = tmp16; 43 | }(typeof global === 'undefined' ? this : global)); 44 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test8.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1, tmp2, tmp3; 3 | tmp1 = "alert"; 4 | tmp0 = __global[tmp1]; 5 | tmp2 = "x"; 6 | tmp3 = tmp0(tmp2); 7 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test80.js: -------------------------------------------------------------------------------- 1 | (function (__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function (o) { 4 | var p, tmp2, tmp5, tmp6, tmp7, tmp8; 5 | tmp2 = o; 6 | tmp3: { 7 | for (p in tmp2) { 8 | tmp4: { 9 | tmp6 = p; 10 | tmp7 = 'SKIP'; 11 | tmp5 = tmp6 === tmp7; 12 | if (tmp5) { 13 | break tmp4; 14 | } else { 15 | ; 16 | } 17 | } 18 | } 19 | } 20 | tmp8 = p; 21 | return tmp8; 22 | }; 23 | tmp0 = 'f'; 24 | __global[tmp0] = tmp1; 25 | }(typeof global === 'undefined' ? this : global)); 26 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test81.js: -------------------------------------------------------------------------------- 1 | (function (__global) { 2 | var tmp0, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10; 3 | tmp3 = 'o'; 4 | tmp0 = __global[tmp3]; 5 | tmp1: { 6 | for (tmp8 in tmp0) { 7 | tmp10 = tmp8; 8 | tmp9 = 'p'; 9 | __global[tmp9] = tmp10; 10 | tmp2: { 11 | tmp7 = 'p'; 12 | tmp5 = __global[tmp7]; 13 | tmp6 = 'SKIP'; 14 | tmp4 = tmp5 === tmp6; 15 | if (tmp4) { 16 | break tmp2; 17 | } else { 18 | ; 19 | } 20 | } 21 | } 22 | } 23 | }(typeof global === 'undefined' ? this : global)); 24 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.test9.js: -------------------------------------------------------------------------------- 1 | (function(__global) { 2 | var tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; 3 | tmp2 = "Math"; 4 | tmp0 = __global[tmp2]; 5 | tmp1 = "min"; 6 | tmp3 = 23; 7 | tmp5 = 23; 8 | tmp6 = 19; 9 | tmp4 = tmp5 + tmp6; 10 | tmp7 = tmp0[tmp1](tmp3, tmp4); 11 | })(typeof global === 'undefined' ? this : global); -------------------------------------------------------------------------------- /normalizer/test/data/normalized.unify_ret1.js: -------------------------------------------------------------------------------- 1 | (function (__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function () { 4 | var tmp3; 5 | tmp2: { 6 | tmp3 = 23; 7 | break tmp2; 8 | } 9 | return tmp3; 10 | }; 11 | tmp0 = 'foo'; 12 | __global[tmp0] = tmp1; 13 | }(typeof global === 'undefined' ? this : global)); 14 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.unify_ret2.js: -------------------------------------------------------------------------------- 1 | (function (__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function () { 4 | var x, tmp3; 5 | tmp2: { 6 | x = 23; 7 | tmp3 = x; 8 | break tmp2; 9 | } 10 | return tmp3; 11 | }; 12 | tmp0 = 'foo'; 13 | __global[tmp0] = tmp1; 14 | }(typeof global === 'undefined' ? this : global)); 15 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.unify_ret3.js: -------------------------------------------------------------------------------- 1 | (function (__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function () { 4 | var x, tmp3; 5 | tmp2: { 6 | x = 23; 7 | tmp3 = x; 8 | break tmp2; 9 | x = 42; 10 | } 11 | return tmp3; 12 | }; 13 | tmp0 = 'foo'; 14 | __global[tmp0] = tmp1; 15 | }(typeof global === 'undefined' ? this : global)); 16 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.unify_ret4.js: -------------------------------------------------------------------------------- 1 | (function (__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function () { 4 | var tmp3, tmp4, tmp5, tmp6, tmp7; 5 | tmp2: { 6 | tmp7 = 'window'; 7 | tmp5 = __global[tmp7]; 8 | tmp6 = 'WeakMap'; 9 | tmp4 = tmp5[tmp6]; 10 | if (tmp4) { 11 | tmp3 = true; 12 | break tmp2; 13 | } else { 14 | ; 15 | } 16 | tmp3 = false; 17 | break tmp2; 18 | } 19 | return tmp3; 20 | }; 21 | tmp0 = 'tst'; 22 | __global[tmp0] = tmp1; 23 | }(typeof global === 'undefined' ? this : global)); 24 | -------------------------------------------------------------------------------- /normalizer/test/data/normalized.unify_ret5.js: -------------------------------------------------------------------------------- 1 | (function (__global) { 2 | var tmp0, tmp1; 3 | tmp1 = function (f) { 4 | var y, tmp3, tmp4, tmp5; 5 | tmp2: { 6 | y = 42; 7 | try { 8 | tmp4 = f; 9 | tmp5 = y; 10 | tmp3 = tmp4(tmp5); 11 | break tmp2; 12 | } finally { 13 | y = 23; 14 | } 15 | } 16 | return tmp3; 17 | }; 18 | tmp0 = 'tst'; 19 | __global[tmp0] = tmp1; 20 | }(typeof global === 'undefined' ? this : global)); 21 | -------------------------------------------------------------------------------- /normalizer/test/data/test1.js: -------------------------------------------------------------------------------- 1 | 23; -------------------------------------------------------------------------------- /normalizer/test/data/test10.js: -------------------------------------------------------------------------------- 1 | f()(); -------------------------------------------------------------------------------- /normalizer/test/data/test11.js: -------------------------------------------------------------------------------- 1 | new String("x"); -------------------------------------------------------------------------------- /normalizer/test/data/test12.js: -------------------------------------------------------------------------------- 1 | var x; -------------------------------------------------------------------------------- /normalizer/test/data/test13.js: -------------------------------------------------------------------------------- 1 | var x = 23; -------------------------------------------------------------------------------- /normalizer/test/data/test14.js: -------------------------------------------------------------------------------- 1 | var x, y = 42; -------------------------------------------------------------------------------- /normalizer/test/data/test15.js: -------------------------------------------------------------------------------- 1 | var x = 23, y; -------------------------------------------------------------------------------- /normalizer/test/data/test16.js: -------------------------------------------------------------------------------- 1 | var x, y = x + 1; -------------------------------------------------------------------------------- /normalizer/test/data/test17.js: -------------------------------------------------------------------------------- 1 | []; -------------------------------------------------------------------------------- /normalizer/test/data/test18.js: -------------------------------------------------------------------------------- 1 | [ [] ]; -------------------------------------------------------------------------------- /normalizer/test/data/test19.js: -------------------------------------------------------------------------------- 1 | [ 23, 452 ]; -------------------------------------------------------------------------------- /normalizer/test/data/test2.js: -------------------------------------------------------------------------------- 1 | "hello"; -------------------------------------------------------------------------------- /normalizer/test/data/test20.js: -------------------------------------------------------------------------------- 1 | [23,,452]; -------------------------------------------------------------------------------- /normalizer/test/data/test21.js: -------------------------------------------------------------------------------- 1 | ({}); -------------------------------------------------------------------------------- /normalizer/test/data/test22.js: -------------------------------------------------------------------------------- 1 | ({ 2 | x: 23 3 | }); -------------------------------------------------------------------------------- /normalizer/test/data/test23.js: -------------------------------------------------------------------------------- 1 | ({ 2 | x: 23, 3 | get y() { 4 | return this.x; 5 | }, 6 | set y(v) {} 7 | }); -------------------------------------------------------------------------------- /normalizer/test/data/test24.js: -------------------------------------------------------------------------------- 1 | ({ 2 | x: 23 3 | }); -------------------------------------------------------------------------------- /normalizer/test/data/test25.js: -------------------------------------------------------------------------------- 1 | ({ 2 | x: 23 + 19 3 | }); -------------------------------------------------------------------------------- /normalizer/test/data/test26.js: -------------------------------------------------------------------------------- 1 | lbl: { 2 | break lbl; 3 | } -------------------------------------------------------------------------------- /normalizer/test/data/test27.js: -------------------------------------------------------------------------------- 1 | lbl1: lbl2: { 2 | break lbl1; 3 | } -------------------------------------------------------------------------------- /normalizer/test/data/test28.js: -------------------------------------------------------------------------------- 1 | if (x === y) 2 | alert("They are the same!"); 3 | -------------------------------------------------------------------------------- /normalizer/test/data/test29.js: -------------------------------------------------------------------------------- 1 | x + y; -------------------------------------------------------------------------------- /normalizer/test/data/test3.js: -------------------------------------------------------------------------------- 1 | /world/; -------------------------------------------------------------------------------- /normalizer/test/data/test30.js: -------------------------------------------------------------------------------- 1 | x.f && x.g; -------------------------------------------------------------------------------- /normalizer/test/data/test31.js: -------------------------------------------------------------------------------- 1 | function count() { 2 | var i = 0; 3 | while (i < 10) 4 | alert(i); 5 | } -------------------------------------------------------------------------------- /normalizer/test/data/test32.js: -------------------------------------------------------------------------------- 1 | i++; -------------------------------------------------------------------------------- /normalizer/test/data/test33.js: -------------------------------------------------------------------------------- 1 | function f(i) { 2 | return i++; 3 | } -------------------------------------------------------------------------------- /normalizer/test/data/test34.js: -------------------------------------------------------------------------------- 1 | var i = 0; 2 | do { 3 | alert(i++); 4 | } while (i < 10); -------------------------------------------------------------------------------- /normalizer/test/data/test35.js: -------------------------------------------------------------------------------- 1 | for (var i = 0; i < 10; ++i) 2 | alert(i); -------------------------------------------------------------------------------- /normalizer/test/data/test36.js: -------------------------------------------------------------------------------- 1 | for (var p in src) 2 | dest[p] = src[p]; -------------------------------------------------------------------------------- /normalizer/test/data/test37.js: -------------------------------------------------------------------------------- 1 | for (p in src) 2 | dest[p] = src[p]; -------------------------------------------------------------------------------- /normalizer/test/data/test38.js: -------------------------------------------------------------------------------- 1 | function extend(dest, src) { 2 | for (var p in src) 3 | dest[p] = src[p]; 4 | } -------------------------------------------------------------------------------- /normalizer/test/data/test39.js: -------------------------------------------------------------------------------- 1 | switch (23 + 19) { 2 | case 42: 3 | var x; 4 | alert("yes!"); 5 | break; 6 | default: 7 | alert("huh?"); 8 | } -------------------------------------------------------------------------------- /normalizer/test/data/test4.js: -------------------------------------------------------------------------------- 1 | foo; -------------------------------------------------------------------------------- /normalizer/test/data/test40.js: -------------------------------------------------------------------------------- 1 | switch (23 + 19) { 2 | case 42: 3 | var x; 4 | alert("yes!"); 5 | default: 6 | alert("huh?"); 7 | } -------------------------------------------------------------------------------- /normalizer/test/data/test41.js: -------------------------------------------------------------------------------- 1 | switch (23 + 19) { 2 | default: 3 | alert("huh?"); 4 | break; 5 | case 42: 6 | var x; 7 | alert("yes!"); 8 | break; 9 | } -------------------------------------------------------------------------------- /normalizer/test/data/test42.js: -------------------------------------------------------------------------------- 1 | switch (42) { 2 | } -------------------------------------------------------------------------------- /normalizer/test/data/test43.js: -------------------------------------------------------------------------------- 1 | switch (42) { 2 | case 23: 3 | } -------------------------------------------------------------------------------- /normalizer/test/data/test44.js: -------------------------------------------------------------------------------- 1 | switch (23 + 19) { 2 | case 42: 3 | case 19 + 23: 4 | var x; 5 | alert("yes!"); 6 | break; 7 | default: 8 | alert("huh?"); 9 | } -------------------------------------------------------------------------------- /normalizer/test/data/test45.js: -------------------------------------------------------------------------------- 1 | try { 2 | f(); 3 | } catch (e) { 4 | alert(e); 5 | } finally { 6 | alert("done"); 7 | } -------------------------------------------------------------------------------- /normalizer/test/data/test46.js: -------------------------------------------------------------------------------- 1 | throw new Error; -------------------------------------------------------------------------------- /normalizer/test/data/test47.js: -------------------------------------------------------------------------------- 1 | this.alert("Hi!"); -------------------------------------------------------------------------------- /normalizer/test/data/test48.js: -------------------------------------------------------------------------------- 1 | function f() { 2 | return this.g(); 3 | } -------------------------------------------------------------------------------- /normalizer/test/data/test49.js: -------------------------------------------------------------------------------- 1 | getPlatform(); 2 | function getPlatform() { 3 | return exports ? "node" : "browser"; 4 | } -------------------------------------------------------------------------------- /normalizer/test/data/test5.js: -------------------------------------------------------------------------------- 1 | x.f; -------------------------------------------------------------------------------- /normalizer/test/data/test50.js: -------------------------------------------------------------------------------- 1 | ++window.tick; -------------------------------------------------------------------------------- /normalizer/test/data/test51.js: -------------------------------------------------------------------------------- 1 | oldTick = window.tick++; -------------------------------------------------------------------------------- /normalizer/test/data/test52.js: -------------------------------------------------------------------------------- 1 | (function(j) { 2 | for (var i = 0; i < j; ++i) { 3 | if (i % 2) 4 | break; 5 | 6 | } 7 | })(10); -------------------------------------------------------------------------------- /normalizer/test/data/test53.js: -------------------------------------------------------------------------------- 1 | (function(i) { 2 | return i++, 42; 3 | })(); -------------------------------------------------------------------------------- /normalizer/test/data/test54.js: -------------------------------------------------------------------------------- 1 | (function(i) { 2 | return i += 1, 42; 3 | })(); -------------------------------------------------------------------------------- /normalizer/test/data/test55.js: -------------------------------------------------------------------------------- 1 | (function(o) { 2 | return o.x >>= 2, 42; 3 | })(); -------------------------------------------------------------------------------- /normalizer/test/data/test56.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var i, j; 3 | i = j = 42; 4 | })(); -------------------------------------------------------------------------------- /normalizer/test/data/test57.js: -------------------------------------------------------------------------------- 1 | (function(o) { 2 | o.i = o.j = 42; 3 | })(); -------------------------------------------------------------------------------- /normalizer/test/data/test58.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var f = function() { 3 | var g = function() { 4 | x; 5 | }, x; 6 | }(); 7 | })(); -------------------------------------------------------------------------------- /normalizer/test/data/test59.js: -------------------------------------------------------------------------------- 1 | function f(x) { 2 | x = x.f || x.g; 3 | } -------------------------------------------------------------------------------- /normalizer/test/data/test6.js: -------------------------------------------------------------------------------- 1 | x[f()]; -------------------------------------------------------------------------------- /normalizer/test/data/test60.js: -------------------------------------------------------------------------------- 1 | try { 2 | throw ""; 3 | } catch (e) {} -------------------------------------------------------------------------------- /normalizer/test/data/test61.js: -------------------------------------------------------------------------------- 1 | try { 2 | throw ""; 3 | } finally {} -------------------------------------------------------------------------------- /normalizer/test/data/test62.js: -------------------------------------------------------------------------------- 1 | function f(o) { 2 | delete o.x; 3 | } -------------------------------------------------------------------------------- /normalizer/test/data/test63.js: -------------------------------------------------------------------------------- 1 | function f(o) { 2 | delete o; 3 | } -------------------------------------------------------------------------------- /normalizer/test/data/test64.js: -------------------------------------------------------------------------------- 1 | delete Object; -------------------------------------------------------------------------------- /normalizer/test/data/test65.js: -------------------------------------------------------------------------------- 1 | --jQuery.wait; -------------------------------------------------------------------------------- /normalizer/test/data/test66.js: -------------------------------------------------------------------------------- 1 | debugger; -------------------------------------------------------------------------------- /normalizer/test/data/test67.js: -------------------------------------------------------------------------------- 1 | for (; ; ) 2 | alert("stuck!"); -------------------------------------------------------------------------------- /normalizer/test/data/test68.js: -------------------------------------------------------------------------------- 1 | (function(target) { 2 | if (target) 3 | return; 4 | 5 | })(); -------------------------------------------------------------------------------- /normalizer/test/data/test69.js: -------------------------------------------------------------------------------- 1 | function foo() { 2 | function bar() {} 3 | return bar; 4 | } -------------------------------------------------------------------------------- /normalizer/test/data/test7.js: -------------------------------------------------------------------------------- 1 | eval("x"); -------------------------------------------------------------------------------- /normalizer/test/data/test70.js: -------------------------------------------------------------------------------- 1 | function f() { 2 | return f.prototype; 3 | } -------------------------------------------------------------------------------- /normalizer/test/data/test71.js: -------------------------------------------------------------------------------- 1 | (function f() { 2 | return f.prototype; 3 | })(); -------------------------------------------------------------------------------- /normalizer/test/data/test72.js: -------------------------------------------------------------------------------- 1 | var o = { 2 | x: 42 3 | }; 4 | with (o) { 5 | alert(x); 6 | } -------------------------------------------------------------------------------- /normalizer/test/data/test73.js: -------------------------------------------------------------------------------- 1 | function f() { 2 | var o = { 3 | x: 42 4 | }; 5 | with (o) { 6 | alert(x); 7 | } 8 | } -------------------------------------------------------------------------------- /normalizer/test/data/test74.js: -------------------------------------------------------------------------------- 1 | var t = 0; 2 | t += 2; -------------------------------------------------------------------------------- /normalizer/test/data/test75.js: -------------------------------------------------------------------------------- 1 | function f() { 2 | var i = 0; 3 | var i = 1; 4 | } -------------------------------------------------------------------------------- /normalizer/test/data/test76.js: -------------------------------------------------------------------------------- 1 | 1, 2, 3; -------------------------------------------------------------------------------- /normalizer/test/data/test77.js: -------------------------------------------------------------------------------- 1 | function f(o) { 2 | var x = 23; 3 | with(o) { 4 | return x; 5 | } 6 | } -------------------------------------------------------------------------------- /normalizer/test/data/test78.js: -------------------------------------------------------------------------------- 1 | function f(o) { 2 | var x; 3 | with(o) { 4 | x = 23; 5 | y = 42; 6 | } 7 | } -------------------------------------------------------------------------------- /normalizer/test/data/test79.js: -------------------------------------------------------------------------------- 1 | function f(o) { 2 | var g; 3 | with(o) { 4 | g(); 5 | h(); 6 | } 7 | } 8 | 9 | function h() {} -------------------------------------------------------------------------------- /normalizer/test/data/test8.js: -------------------------------------------------------------------------------- 1 | alert("x"); -------------------------------------------------------------------------------- /normalizer/test/data/test80.js: -------------------------------------------------------------------------------- 1 | function f(o) { 2 | for(var p in o) 3 | if(p === 'SKIP') 4 | continue; 5 | return p; 6 | } 7 | -------------------------------------------------------------------------------- /normalizer/test/data/test81.js: -------------------------------------------------------------------------------- 1 | for(var p in o) 2 | if(p === 'SKIP') 3 | continue; 4 | -------------------------------------------------------------------------------- /normalizer/test/data/test9.js: -------------------------------------------------------------------------------- 1 | Math.min(23, 23 + 19); -------------------------------------------------------------------------------- /normalizer/test/data/unify_ret1.js: -------------------------------------------------------------------------------- 1 | // { "unify_ret": true } 2 | function foo() { 3 | return 23; 4 | } 5 | -------------------------------------------------------------------------------- /normalizer/test/data/unify_ret2.js: -------------------------------------------------------------------------------- 1 | // { "unify_ret": true } 2 | function foo() { 3 | var x = 23; 4 | return x; 5 | } -------------------------------------------------------------------------------- /normalizer/test/data/unify_ret3.js: -------------------------------------------------------------------------------- 1 | // { "unify_ret": true } 2 | function foo() { 3 | var x = 23; 4 | return x; 5 | x = 42; 6 | } -------------------------------------------------------------------------------- /normalizer/test/data/unify_ret4.js: -------------------------------------------------------------------------------- 1 | // { "unify_ret": true } 2 | function tst() { 3 | if(window.WeakMap) 4 | return true; 5 | return false; 6 | } 7 | -------------------------------------------------------------------------------- /normalizer/test/data/unify_ret5.js: -------------------------------------------------------------------------------- 1 | // { "unify_ret": true } 2 | function tst(f) { 3 | var y = 42; 4 | try { 5 | return f(y); 6 | } finally { 7 | y = 23; 8 | } 9 | } -------------------------------------------------------------------------------- /normalizer/test/normalize.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | /** 12 | * Simple command line interface to the normalizer: normalizes and pretty-prints 13 | * code in file given as first argument. 14 | */ 15 | var normalizer = require("../lib/normalizer"), 16 | esprima = require("esprima"), 17 | escodegen = require("escodegen"), 18 | fs = require("fs"); 19 | 20 | var src = fs.readFileSync(process.argv[2], 'utf-8'); 21 | var ast = esprima.parse(src); 22 | var normalized = normalizer.normalize(ast, { pp: escodegen.generate }); 23 | console.log(escodegen.generate(normalized, { comment: true })); 24 | -------------------------------------------------------------------------------- /normalizer/test/test-normalizer.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2012 IBM Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * IBM Corporation - initial API and implementation 10 | *******************************************************************************/ 11 | 12 | /** 13 | * Unit tests for the normalizer. The actual test data is in directory 'data/'. 14 | * For every test 'foo', 'data/foo.js' is the original file, while 15 | * 'data/normalized.foo.js' is the expected result of normalization. 16 | * 17 | * The original file can optionally start with a '//' comment line containing 18 | * a JSON encoding of the option object to pass to the normalizer (see the 19 | * unify_ret*.js files for examples). 20 | */ 21 | var normalizer = require("../lib/normalizer"), 22 | esprima = require("esprima"), 23 | escodegen = require("escodegen"), 24 | fs = require("fs"); 25 | 26 | function runtest(test, input_file, expected_file) { 27 | var input_src = fs.readFileSync(input_file, 'utf-8'); 28 | var input = esprima.parse(input_src), 29 | expected = esprima.parse(fs.readFileSync(expected_file, 'utf-8')); 30 | 31 | var options = null; 32 | if(input_src.substring(0, 2) === '//') 33 | options = JSON.parse(input_src.substring(2, input_src.indexOf('\n'))); 34 | 35 | var normalized = normalizer.normalize(input, options); 36 | var expected_str = escodegen.generate(expected), 37 | actual_str = escodegen.generate(normalized); 38 | if(expected_str !== actual_str) { 39 | console.log("Expected:\n" + expected_str); 40 | console.log("Actual:\n" + actual_str); 41 | } 42 | test.equal(expected_str, actual_str); 43 | test.done(); 44 | } 45 | 46 | var DATA_DIR = "data/"; 47 | fs.readdirSync(DATA_DIR).forEach(function(file) { 48 | if(/\.js$/.test(file) && !/^normalized\./.test(file)) 49 | exports[file] = function(test) { 50 | runtest(test, DATA_DIR + file, DATA_DIR + "normalized." + file); 51 | }; 52 | }); 53 | 54 | var reporter = require('nodeunit').reporters['default']; 55 | reporter.run({"test-normalise" : module.exports}); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JS_WALA", 3 | "version": "0.1.1", 4 | "author": "IBM", 5 | "contributors": [ 6 | { 7 | "name": "Max Schaefer", 8 | "email": "schaefer@ntu.edu.sg" 9 | }, 10 | { 11 | "name": "Manu Sridharan", 12 | "email": "msridhar@us.ibm.com" 13 | } 14 | ], 15 | "description": "JavaScript Tools for WALA", 16 | "dependencies" : { 17 | "esprima": "*", 18 | "escodegen": "*", 19 | "nodeunit" : "*", 20 | "amdefine" : "*" 21 | }, 22 | "license": "Eclipse" 23 | } 24 | --------------------------------------------------------------------------------