123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- /*
- Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- /*jshint eqeqeq:false*/
- (function () {
- 'use strict';
-
- var Syntax, common;
-
- common = require('./common');
- Syntax = common.Syntax;
-
- // constant
-
- function isConstant(node, allowRegExp) {
- if (node.type === Syntax.Literal) {
- if (typeof node.value === 'object' && node.value !== null) {
- // This is RegExp
- return allowRegExp;
- }
- return true;
- }
- if (node.type === Syntax.UnaryExpression) {
- if (node.operator === 'void' || node.operator === 'delete' || node.operator === '!') {
- return isConstant(node.argument, true);
- }
- return isConstant(node.argument, false);
- }
- if (node.type === Syntax.BinaryExpression) {
- if (node.operator === 'in' || node.operator === 'instanceof') {
- return false;
- }
- return isConstant(node.left, false) && isConstant(node.right, false);
- }
- if (node.type === Syntax.LogicalExpression) {
- return isConstant(node.left, true) && isConstant(node.right, true);
- }
- return false;
- }
-
- function getConstant(node) {
- if (node.type === Syntax.Literal) {
- return node.value;
- }
- if (node.type === Syntax.UnaryExpression) {
- return doUnary(node.operator, getConstant(node.argument));
- }
- if (node.type === Syntax.BinaryExpression) {
- return doBinary(node.operator, getConstant(node.left), getConstant(node.right));
- }
- if (node.type === Syntax.LogicalExpression) {
- return doLogical(node.operator, getConstant(node.left), getConstant(node.right));
- }
- common.unreachable();
- }
-
- function doLogical(operator, left, right) {
- if (operator === '||') {
- return left || right;
- }
- if (operator === '&&') {
- return left && right;
- }
- common.unreachable();
- }
-
- function doUnary(operator, argument) {
- switch (operator) {
- case '+':
- return +argument;
- case '-':
- return -argument;
- case '~':
- return ~argument;
- case '!':
- return !argument;
- case 'delete':
- // do delete on constant value (not considering identifier in this tree based constant folding)
- return true;
- case 'void':
- return undefined;
- case 'typeof':
- return typeof argument;
- }
- common.unreachable();
- }
-
- function doBinary(operator, left, right) {
- switch (operator) {
- case '|':
- return left | right;
- case '^':
- return left ^ right;
- case '&':
- return left & right;
- case '==':
- return left == right;
- case '!=':
- return left != right;
- case '===':
- return left === right;
- case '!==':
- return left !== right;
- case '<':
- return left < right;
- case '>':
- return left > right;
- case '<=':
- return left <= right;
- case '>=':
- return left >= right;
- // case 'in':
- // return left in right;
- // case 'instanceof':
- // return left instanceof right;
- case '<<':
- return left << right;
- case '>>':
- return left >> right;
- case '>>>':
- return left >>> right;
- case '+':
- return left + right;
- case '-':
- return left - right;
- case '*':
- return left * right;
- case '/':
- return left / right;
- case '%':
- return left % right;
- }
- common.unreachable();
- }
-
- exports.constant = {
- doBinary: doBinary,
- doUnary: doUnary,
- doLogical: doLogical,
- evaluate: getConstant,
- isConstant: isConstant
- };
-
- // has side effect
- function hasSideEffect(expr, scope) {
- function visit(expr) {
- var i, iz, ref;
- switch (expr.type) {
- case Syntax.AssignmentExpression:
- return true;
-
- case Syntax.ArrayExpression:
- for (i = 0, iz = expr.elements.length; i < iz; ++i) {
- if (expr.elements[i] !== null && visit(expr.elements[i])) {
- return true;
- }
- }
- return false;
-
- case Syntax.BinaryExpression:
- return !isConstant(expr);
-
- case Syntax.CallExpression:
- return true;
-
- case Syntax.ConditionalExpression:
- return visit(expr.test) || visit(expr.consequent) || visit(expr.alternate);
-
- case Syntax.FunctionExpression:
- return false;
-
- case Syntax.Identifier:
- ref = scope.resolve(expr);
- if (ref && ref.isStatic()) {
- return false;
- }
- return true;
-
- case Syntax.Literal:
- return false;
-
- case Syntax.LogicalExpression:
- return visit(expr.left) || visit(expr.right);
-
- case Syntax.MemberExpression:
- return true;
-
- case Syntax.NewExpression:
- return true;
-
- case Syntax.ObjectExpression:
- for (i = 0, iz = expr.properties.length; i < iz; ++i) {
- if (visit(expr.properties[i])) {
- return true;
- }
- }
- return false;
-
- case Syntax.Property:
- return visit(expr.value);
-
- case Syntax.SequenceExpression:
- for (i = 0, iz = expr.expressions.length; i < iz; ++i) {
- if (visit(expr.expressions[i])) {
- return true;
- }
- }
- return false;
-
- case Syntax.ThisExpression:
- return false;
-
- case Syntax.UnaryExpression:
- if (expr.operator === 'void' || expr.operator === 'delete' || expr.operator === 'typeof' || expr.operator === '!') {
- return visit(expr.argument);
- }
- return !isConstant(expr);
-
- case Syntax.UpdateExpression:
- return true;
- }
- return true;
- }
-
- return visit(expr);
- }
-
- exports.hasSideEffect = hasSideEffect;
-
- // boolean decision
- // @return {boolean|null} when indeterminate value comes, returns null
- function booleanCondition(expr) {
- var ret;
- switch (expr.type) {
- case Syntax.AssignmentExpression:
- return booleanCondition(expr.right);
-
- case Syntax.ArrayExpression:
- return true;
-
- case Syntax.BinaryExpression:
- if (isConstant(expr)) {
- return !!getConstant(expr);
- }
- return null;
-
- case Syntax.CallExpression:
- return null;
-
- case Syntax.ConditionalExpression:
- ret = booleanCondition(expr.test);
- if (ret === true) {
- return booleanCondition(expr.consequent);
- }
- if (ret === false) {
- return booleanCondition(expr.alternate);
- }
- ret = booleanCondition(expr.consequent);
- if (ret === booleanCondition(expr.alternate)) {
- return ret;
- }
- return null;
-
- case Syntax.FunctionExpression:
- return true;
-
- case Syntax.Identifier:
- return null;
-
- case Syntax.Literal:
- return !!getConstant(expr);
-
- case Syntax.LogicalExpression:
- if (expr.operator === '&&') {
- ret = booleanCondition(expr.left);
- if (ret === null) {
- return null;
- }
- if (!ret) {
- return false;
- }
- return booleanCondition(expr.right);
- } else {
- ret = booleanCondition(expr.left);
- if (ret === null) {
- return null;
- }
- if (ret) {
- return true;
- }
- return booleanCondition(expr.right);
- }
- return null;
-
- case Syntax.MemberExpression:
- return null;
-
- case Syntax.NewExpression:
- // always return object
- return true;
-
- case Syntax.ObjectExpression:
- return true;
-
- case Syntax.Property:
- common.unreachable();
- return null;
-
- case Syntax.SequenceExpression:
- return booleanCondition(common.Array.last(expr.expressions));
-
- case Syntax.ThisExpression:
- // in strict mode, this may be null / undefined
- return null;
-
- case Syntax.UnaryExpression:
- if (expr.operator === 'void') {
- return false;
- }
- if (expr.operator === 'typeof') {
- return true;
- }
- if (expr.operator === '!') {
- ret = booleanCondition(expr.argument);
- if (ret === null) {
- return null;
- }
- return !ret;
- }
- if (isConstant(expr)) {
- return !!getConstant(expr);
- }
- return null;
-
- case Syntax.UpdateExpression:
- return null;
- }
-
- return null;
- }
-
- exports.booleanCondition = booleanCondition;
- }());
|