UNPKG

11.1 kBJavaScriptView Raw
1/*
2 Copyright (C) 2012 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/*jshint eqeqeq:false*/
26(function () {
27 'use strict';
28
29 var Syntax, common;
30
31 common = require('./common');
32 Syntax = common.Syntax;
33
34 // constant
35
36 function isConstant(node, allowRegExp) {
37 if (node.type === Syntax.Literal) {
38 if (typeof node.value === 'object' && node.value !== null) {
39 // This is RegExp
40 return allowRegExp;
41 }
42 return true;
43 }
44 if (node.type === Syntax.UnaryExpression) {
45 if (node.operator === 'void' || node.operator === 'delete' || node.operator === '!') {
46 return isConstant(node.argument, true);
47 }
48 return isConstant(node.argument, false);
49 }
50 if (node.type === Syntax.BinaryExpression) {
51 if (node.operator === 'in' || node.operator === 'instanceof') {
52 return false;
53 }
54 return isConstant(node.left, false) && isConstant(node.right, false);
55 }
56 if (node.type === Syntax.LogicalExpression) {
57 return isConstant(node.left, true) && isConstant(node.right, true);
58 }
59 return false;
60 }
61
62 function getConstant(node) {
63 if (node.type === Syntax.Literal) {
64 return node.value;
65 }
66 if (node.type === Syntax.UnaryExpression) {
67 return doUnary(node.operator, getConstant(node.argument));
68 }
69 if (node.type === Syntax.BinaryExpression) {
70 return doBinary(node.operator, getConstant(node.left), getConstant(node.right));
71 }
72 if (node.type === Syntax.LogicalExpression) {
73 return doLogical(node.operator, getConstant(node.left), getConstant(node.right));
74 }
75 common.unreachable();
76 }
77
78 function doLogical(operator, left, right) {
79 if (operator === '||') {
80 return left || right;
81 }
82 if (operator === '&&') {
83 return left && right;
84 }
85 common.unreachable();
86 }
87
88 function doUnary(operator, argument) {
89 switch (operator) {
90 case '+':
91 return +argument;
92 case '-':
93 return -argument;
94 case '~':
95 return ~argument;
96 case '!':
97 return !argument;
98 case 'delete':
99 // do delete on constant value (not considering identifier in this tree based constant folding)
100 return true;
101 case 'void':
102 return undefined;
103 case 'typeof':
104 return typeof argument;
105 }
106 common.unreachable();
107 }
108
109 function doBinary(operator, left, right) {
110 switch (operator) {
111 case '|':
112 return left | right;
113 case '^':
114 return left ^ right;
115 case '&':
116 return left & right;
117 case '==':
118 return left == right;
119 case '!=':
120 return left != right;
121 case '===':
122 return left === right;
123 case '!==':
124 return left !== right;
125 case '<':
126 return left < right;
127 case '>':
128 return left > right;
129 case '<=':
130 return left <= right;
131 case '>=':
132 return left >= right;
133 // case 'in':
134 // return left in right;
135 // case 'instanceof':
136 // return left instanceof right;
137 case '<<':
138 return left << right;
139 case '>>':
140 return left >> right;
141 case '>>>':
142 return left >>> right;
143 case '+':
144 return left + right;
145 case '-':
146 return left - right;
147 case '*':
148 return left * right;
149 case '/':
150 return left / right;
151 case '%':
152 return left % right;
153 }
154 common.unreachable();
155 }
156
157 exports.constant = {
158 doBinary: doBinary,
159 doUnary: doUnary,
160 doLogical: doLogical,
161 evaluate: getConstant,
162 isConstant: isConstant
163 };
164
165 // has side effect
166 function hasSideEffect(expr, scope) {
167 function visit(expr) {
168 var i, iz, ref;
169 switch (expr.type) {
170 case Syntax.AssignmentExpression:
171 return true;
172
173 case Syntax.ArrayExpression:
174 for (i = 0, iz = expr.elements.length; i < iz; ++i) {
175 if (visit(expr.elements[i])) {
176 return true;
177 }
178 }
179 return false;
180
181 case Syntax.BinaryExpression:
182 return !isConstant(expr);
183
184 case Syntax.CallExpression:
185 return true;
186
187 case Syntax.ConditionalExpression:
188 return visit(expr.test) || visit(expr.consequent) || visit(expr.alternate);
189
190 case Syntax.FunctionExpression:
191 return false;
192
193 case Syntax.Identifier:
194 ref = scope.resolve(expr);
195 if (ref && ref.isStatic()) {
196 return false;
197 }
198 return true;
199
200 case Syntax.Literal:
201 return false;
202
203 case Syntax.LogicalExpression:
204 return visit(expr.left) || visit(expr.right);
205
206 case Syntax.MemberExpression:
207 return true;
208
209 case Syntax.NewExpression:
210 return true;
211
212 case Syntax.ObjectExpression:
213 for (i = 0, iz = expr.properties.length; i < iz; ++i) {
214 if (visit(expr.properties[i])) {
215 return true;
216 }
217 }
218 return false;
219
220 case Syntax.Property:
221 return visit(expr.value);
222
223 case Syntax.SequenceExpression:
224 for (i = 0, iz = expr.expressions.length; i < iz; ++i) {
225 if (visit(expr.expressions[i])) {
226 return true;
227 }
228 }
229 return false;
230
231 case Syntax.ThisExpression:
232 return false;
233
234 case Syntax.UnaryExpression:
235 if (expr.operator === 'void' || expr.operator === 'delete' || expr.operator === 'typeof' || expr.operator === '!') {
236 return visit(expr.argument);
237 }
238 return !isConstant(expr);
239
240 case Syntax.UpdateExpression:
241 return true;
242 }
243 return true;
244 }
245
246 return visit(expr);
247 }
248
249 exports.hasSideEffect = hasSideEffect;
250
251 // boolean decision
252 // @return {boolean|null} when indeterminate value comes, returns null
253 function booleanCondition(expr) {
254 var ret;
255 switch (expr.type) {
256 case Syntax.AssignmentExpression:
257 return booleanCondition(expr.right);
258
259 case Syntax.ArrayExpression:
260 return true;
261
262 case Syntax.BinaryExpression:
263 if (isConstant(expr)) {
264 return !!getConstant(expr);
265 }
266 return null;
267
268 case Syntax.CallExpression:
269 return null;
270
271 case Syntax.ConditionalExpression:
272 ret = booleanCondition(expr.test);
273 if (ret === true) {
274 return booleanCondition(expr.consequent);
275 }
276 if (ret === false) {
277 return booleanCondition(expr.alternate);
278 }
279 ret = booleanCondition(expr.consequent);
280 if (ret === booleanCondition(expr.alternate)) {
281 return ret;
282 }
283 return null;
284
285 case Syntax.FunctionExpression:
286 return true;
287
288 case Syntax.Identifier:
289 return null;
290
291 case Syntax.Literal:
292 return !!getConstant(expr);
293
294 case Syntax.LogicalExpression:
295 if (expr.operator === '&&') {
296 ret = booleanCondition(expr.left);
297 if (ret === null) {
298 return null;
299 }
300 if (!ret) {
301 return false;
302 }
303 return booleanCondition(expr.right);
304 } else {
305 ret = booleanCondition(expr.left);
306 if (ret === null) {
307 return null;
308 }
309 if (ret) {
310 return true;
311 }
312 return booleanCondition(expr.right);
313 }
314 return null;
315
316 case Syntax.MemberExpression:
317 return null;
318
319 case Syntax.NewExpression:
320 // always return object
321 return true;
322
323 case Syntax.ObjectExpression:
324 return true;
325
326 case Syntax.Property:
327 common.unreachable();
328 return null;
329
330 case Syntax.SequenceExpression:
331 return booleanCondition(common.Array.last(expr.expressions));
332
333 case Syntax.ThisExpression:
334 // in strict mode, this may be null / undefined
335 return null;
336
337 case Syntax.UnaryExpression:
338 if (expr.operator === 'void') {
339 return false;
340 }
341 if (expr.operator === 'typeof') {
342 return true;
343 }
344 if (expr.operator === '!') {
345 ret = booleanCondition(expr.argument);
346 if (ret === null) {
347 return null;
348 }
349 return !ret;
350 }
351 if (isConstant(expr)) {
352 return !!getConstant(expr);
353 }
354 return null;
355
356 case Syntax.UpdateExpression:
357 return null;
358 }
359
360 return null;
361 }
362
363 exports.booleanCondition = booleanCondition;
364}());