1 | /*
|
2 | Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
|
3 |
|
4 | Redistribution and use in source and binary forms, with or without
|
5 | modification, are permitted provided that the following conditions are met:
|
6 |
|
7 | * Redistributions of source code must retain the above copyright
|
8 | notice, this list of conditions and the following disclaimer.
|
9 | * Redistributions in binary form must reproduce the above copyright
|
10 | notice, this list of conditions and the following disclaimer in the
|
11 | documentation and/or other materials provided with the distribution.
|
12 |
|
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
16 | ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23 | */
|
24 |
|
25 | /*jslint bitwise:true */
|
26 | /*global exports:true*/
|
27 |
|
28 | (function () {
|
29 | ;
|
30 |
|
31 | var esshorten,
|
32 | common,
|
33 | Options,
|
34 | Syntax,
|
35 | Pass,
|
36 | annotateDirective;
|
37 |
|
38 | esshorten = require('esshorten');
|
39 | common = require('./common');
|
40 | Options = require('./options');
|
41 | Pass = require('./pass');
|
42 | annotateDirective = require('./annotate-directive');
|
43 | Syntax = common.Syntax;
|
44 |
|
45 | // recover some broken AST
|
46 |
|
47 | function recover(tree, useDirectiveStatement) {
|
48 | function trailingIf(node) {
|
49 | while (true) {
|
50 | switch (node.type) {
|
51 | case Syntax.IfStatement:
|
52 | if (!node.alternate) {
|
53 | return true;
|
54 | }
|
55 | node = node.alternate;
|
56 | continue;
|
57 |
|
58 | case Syntax.LabeledStatement:
|
59 | case Syntax.ForStatement:
|
60 | case Syntax.ForInStatement:
|
61 | case Syntax.WhileStatement:
|
62 | case Syntax.WithStatement:
|
63 | node = node.body;
|
64 | continue;
|
65 | }
|
66 | return false;
|
67 | }
|
68 | }
|
69 |
|
70 | common.traverse(tree, {
|
71 | leave: function leave(node) {
|
72 | if (node.type === Syntax.IfStatement && node.alternate) {
|
73 | // force wrap up or not
|
74 | if (node.consequent.type !== Syntax.BlockStatement) {
|
75 | if (trailingIf(node.consequent)) {
|
76 | node.consequent = {
|
77 | type: Syntax.BlockStatement,
|
78 | body: [ node.consequent ]
|
79 | };
|
80 | }
|
81 | }
|
82 | }
|
83 | if (!useDirectiveStatement && node.type === Syntax.DirectiveStatement) {
|
84 | node.type = Syntax.ExpressionStatement;
|
85 | node.expression = common.moveLocation(node, {
|
86 | type: Syntax.Literal,
|
87 | value: node.value,
|
88 | raw: node.raw
|
89 | });
|
90 | delete node.directive;
|
91 | delete node.value;
|
92 | delete node.raw;
|
93 | }
|
94 | }
|
95 | });
|
96 |
|
97 | return tree;
|
98 | }
|
99 |
|
100 | function iteration(tree, p, options) {
|
101 | var i, iz, pass, res, changed, statuses, passes, result;
|
102 |
|
103 | function addPass(pass) {
|
104 | var name;
|
105 | if (typeof pass !== 'function') {
|
106 | // automatic lookup pass (esmangle pass format)
|
107 | name = Object.keys(pass)[0];
|
108 | pass = pass[name];
|
109 | }
|
110 | if (pass.hasOwnProperty('passName')) {
|
111 | name = pass.passName;
|
112 | } else {
|
113 | name = pass.name;
|
114 | }
|
115 | passes.push(pass);
|
116 | statuses.push(true);
|
117 | }
|
118 |
|
119 | function fillStatuses(bool) {
|
120 | var i, iz;
|
121 | for (i = 0, iz = statuses.length; i < iz; ++i) {
|
122 | statuses[i] = bool;
|
123 | }
|
124 | }
|
125 |
|
126 | result = (options.get('destructive')) ? tree : common.deepCopy(tree);
|
127 |
|
128 | statuses = [];
|
129 | passes = [];
|
130 |
|
131 |
|
132 | for (i = 0, iz = p.length; i < iz; ++i) {
|
133 | addPass(p[i]);
|
134 | }
|
135 |
|
136 | do {
|
137 | changed = false;
|
138 | for (i = 0, iz = passes.length; i < iz; ++i) {
|
139 | pass = passes[i];
|
140 | if (statuses[i]) {
|
141 | res = pass(result, options);
|
142 | if (res.modified) {
|
143 | changed = true;
|
144 | fillStatuses(true);
|
145 | } else {
|
146 | statuses[i] = false;
|
147 | }
|
148 | result = res.result;
|
149 | }
|
150 | }
|
151 | } while (changed);
|
152 |
|
153 | return result;
|
154 | }
|
155 |
|
156 | function optimize(tree, pipeline, options) {
|
157 | var i, iz, j, jz, section, pass;
|
158 |
|
159 | tree = annotateDirective(tree, new Options({ destructive: false }));
|
160 |
|
161 | if (null == pipeline) {
|
162 | pipeline = Pass.__defaultPipeline;
|
163 | }
|
164 |
|
165 | options = new Options(options);
|
166 |
|
167 | for (i = 0, iz = pipeline.length; i < iz; ++i) {
|
168 | section = pipeline[i];
|
169 | // simple iterative pass
|
170 | if (common.Array.isArray(section)) {
|
171 | tree = iteration(tree, section, options);
|
172 | } else if (section.once) {
|
173 | pass = section.pass;
|
174 | for (j = 0, jz = pass.length; j < jz; ++j) {
|
175 | tree = pass[j](tree, options).result;
|
176 | }
|
177 | }
|
178 | }
|
179 |
|
180 | return recover(tree, options.get('directive'));
|
181 | }
|
182 |
|
183 | exports.version = require('../package.json').version;
|
184 | exports.mangle = esshorten.mangle;
|
185 | exports.optimize = optimize;
|
186 | exports.pass = Pass;
|
187 | }());
|
188 | /* vim: set sw=4 ts=4 et tw=80 : */
|