Path: blob/master/node_modules/@javascript-obfuscator/estraverse/estraverse.js
1126 views
/*1Copyright (C) 2012-2013 Yusuke Suzuki <[email protected]>2Copyright (C) 2012 Ariya Hidayat <[email protected]>34Redistribution and use in source and binary forms, with or without5modification, are permitted provided that the following conditions are met:67* Redistributions of source code must retain the above copyright8notice, this list of conditions and the following disclaimer.9* Redistributions in binary form must reproduce the above copyright10notice, this list of conditions and the following disclaimer in the11documentation and/or other materials provided with the distribution.1213THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"14AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY17DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES18(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;19LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND20ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT21(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF22THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.23*/24/*jslint vars:false, bitwise:true*/25/*jshint indent:4*/26/*global exports:true*/27(function clone(exports) {28'use strict';2930var Syntax,31VisitorOption,32VisitorKeys,33BREAK,34SKIP,35REMOVE;3637function deepCopy(obj) {38var ret = {}, key, val;39for (key in obj) {40if (obj.hasOwnProperty(key)) {41val = obj[key];42if (typeof val === 'object' && val !== null) {43ret[key] = deepCopy(val);44} else {45ret[key] = val;46}47}48}49return ret;50}5152// based on LLVM libc++ upper_bound / lower_bound53// MIT License5455function upperBound(array, func) {56var diff, len, i, current;5758len = array.length;59i = 0;6061while (len) {62diff = len >>> 1;63current = i + diff;64if (func(array[current])) {65len = diff;66} else {67i = current + 1;68len -= diff + 1;69}70}71return i;72}7374Syntax = {75AssignmentExpression: 'AssignmentExpression',76AssignmentPattern: 'AssignmentPattern',77ArrayExpression: 'ArrayExpression',78ArrayPattern: 'ArrayPattern',79ArrowFunctionExpression: 'ArrowFunctionExpression',80AwaitExpression: 'AwaitExpression', // CAUTION: It's deferred to ES7.81BlockStatement: 'BlockStatement',82BinaryExpression: 'BinaryExpression',83BreakStatement: 'BreakStatement',84CallExpression: 'CallExpression',85CatchClause: 'CatchClause',86ChainExpression: 'ChainExpression',87ClassBody: 'ClassBody',88ClassDeclaration: 'ClassDeclaration',89ClassExpression: 'ClassExpression',90ComprehensionBlock: 'ComprehensionBlock', // CAUTION: It's deferred to ES7.91ComprehensionExpression: 'ComprehensionExpression', // CAUTION: It's deferred to ES7.92ConditionalExpression: 'ConditionalExpression',93ContinueStatement: 'ContinueStatement',94DebuggerStatement: 'DebuggerStatement',95DirectiveStatement: 'DirectiveStatement',96DoWhileStatement: 'DoWhileStatement',97EmptyStatement: 'EmptyStatement',98ExportAllDeclaration: 'ExportAllDeclaration',99ExportDefaultDeclaration: 'ExportDefaultDeclaration',100ExportNamedDeclaration: 'ExportNamedDeclaration',101ExportSpecifier: 'ExportSpecifier',102ExpressionStatement: 'ExpressionStatement',103ForStatement: 'ForStatement',104ForInStatement: 'ForInStatement',105ForOfStatement: 'ForOfStatement',106FunctionDeclaration: 'FunctionDeclaration',107FunctionExpression: 'FunctionExpression',108GeneratorExpression: 'GeneratorExpression', // CAUTION: It's deferred to ES7.109Identifier: 'Identifier',110IfStatement: 'IfStatement',111ImportExpression: 'ImportExpression',112ImportDeclaration: 'ImportDeclaration',113ImportDefaultSpecifier: 'ImportDefaultSpecifier',114ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',115ImportSpecifier: 'ImportSpecifier',116Literal: 'Literal',117LabeledStatement: 'LabeledStatement',118LogicalExpression: 'LogicalExpression',119MemberExpression: 'MemberExpression',120MetaProperty: 'MetaProperty',121MethodDefinition: 'MethodDefinition',122ModuleSpecifier: 'ModuleSpecifier',123NewExpression: 'NewExpression',124ObjectExpression: 'ObjectExpression',125ObjectPattern: 'ObjectPattern',126PrivateIdentifier: 'PrivateIdentifier',127Program: 'Program',128Property: 'Property',129PropertyDefinition: 'PropertyDefinition',130RestElement: 'RestElement',131ReturnStatement: 'ReturnStatement',132SequenceExpression: 'SequenceExpression',133SpreadElement: 'SpreadElement',134StaticBlock: 'StaticBlock',135Super: 'Super',136SwitchStatement: 'SwitchStatement',137SwitchCase: 'SwitchCase',138TaggedTemplateExpression: 'TaggedTemplateExpression',139TemplateElement: 'TemplateElement',140TemplateLiteral: 'TemplateLiteral',141ThisExpression: 'ThisExpression',142ThrowStatement: 'ThrowStatement',143TryStatement: 'TryStatement',144UnaryExpression: 'UnaryExpression',145UpdateExpression: 'UpdateExpression',146VariableDeclaration: 'VariableDeclaration',147VariableDeclarator: 'VariableDeclarator',148WhileStatement: 'WhileStatement',149WithStatement: 'WithStatement',150YieldExpression: 'YieldExpression'151};152153VisitorKeys = {154AssignmentExpression: ['left', 'right'],155AssignmentPattern: ['left', 'right'],156ArrayExpression: ['elements'],157ArrayPattern: ['elements'],158ArrowFunctionExpression: ['params', 'body'],159AwaitExpression: ['argument'], // CAUTION: It's deferred to ES7.160BlockStatement: ['body'],161BinaryExpression: ['left', 'right'],162BreakStatement: ['label'],163CallExpression: ['callee', 'arguments'],164CatchClause: ['param', 'body'],165ChainExpression: ['expression'],166ClassBody: ['body'],167ClassDeclaration: ['id', 'superClass', 'body'],168ClassExpression: ['id', 'superClass', 'body'],169ComprehensionBlock: ['left', 'right'], // CAUTION: It's deferred to ES7.170ComprehensionExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7.171ConditionalExpression: ['test', 'consequent', 'alternate'],172ContinueStatement: ['label'],173DebuggerStatement: [],174DirectiveStatement: [],175DoWhileStatement: ['body', 'test'],176EmptyStatement: [],177ExportAllDeclaration: ['source'],178ExportDefaultDeclaration: ['declaration'],179ExportNamedDeclaration: ['declaration', 'specifiers', 'source'],180ExportSpecifier: ['exported', 'local'],181ExpressionStatement: ['expression'],182ForStatement: ['init', 'test', 'update', 'body'],183ForInStatement: ['left', 'right', 'body'],184ForOfStatement: ['left', 'right', 'body'],185FunctionDeclaration: ['id', 'params', 'body'],186FunctionExpression: ['id', 'params', 'body'],187GeneratorExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7.188Identifier: [],189IfStatement: ['test', 'consequent', 'alternate'],190ImportExpression: ['source'],191ImportDeclaration: ['specifiers', 'source'],192ImportDefaultSpecifier: ['local'],193ImportNamespaceSpecifier: ['local'],194ImportSpecifier: ['imported', 'local'],195Literal: [],196LabeledStatement: ['label', 'body'],197LogicalExpression: ['left', 'right'],198MemberExpression: ['object', 'property'],199MetaProperty: ['meta', 'property'],200MethodDefinition: ['key', 'value'],201ModuleSpecifier: [],202NewExpression: ['callee', 'arguments'],203ObjectExpression: ['properties'],204ObjectPattern: ['properties'],205PrivateIdentifier: [],206Program: ['body'],207Property: ['key', 'value'],208PropertyDefinition: ['key', 'value'],209RestElement: [ 'argument' ],210ReturnStatement: ['argument'],211SequenceExpression: ['expressions'],212SpreadElement: ['argument'],213StaticBlock: ['body'],214Super: [],215SwitchStatement: ['discriminant', 'cases'],216SwitchCase: ['test', 'consequent'],217TaggedTemplateExpression: ['tag', 'quasi'],218TemplateElement: [],219TemplateLiteral: ['quasis', 'expressions'],220ThisExpression: [],221ThrowStatement: ['argument'],222TryStatement: ['block', 'handler', 'finalizer'],223UnaryExpression: ['argument'],224UpdateExpression: ['argument'],225VariableDeclaration: ['declarations'],226VariableDeclarator: ['id', 'init'],227WhileStatement: ['test', 'body'],228WithStatement: ['object', 'body'],229YieldExpression: ['argument']230};231232// unique id233BREAK = {};234SKIP = {};235REMOVE = {};236237VisitorOption = {238Break: BREAK,239Skip: SKIP,240Remove: REMOVE241};242243function Reference(parent, key) {244this.parent = parent;245this.key = key;246}247248Reference.prototype.replace = function replace(node) {249this.parent[this.key] = node;250};251252Reference.prototype.remove = function remove() {253if (Array.isArray(this.parent)) {254this.parent.splice(this.key, 1);255return true;256} else {257this.replace(null);258return false;259}260};261262function Element(node, path, wrap, ref) {263this.node = node;264this.path = path;265this.wrap = wrap;266this.ref = ref;267}268269function Controller() { }270271// API:272// return property path array from root to current node273Controller.prototype.path = function path() {274var i, iz, j, jz, result, element;275276function addToPath(result, path) {277if (Array.isArray(path)) {278for (j = 0, jz = path.length; j < jz; ++j) {279result.push(path[j]);280}281} else {282result.push(path);283}284}285286// root node287if (!this.__current.path) {288return null;289}290291// first node is sentinel, second node is root element292result = [];293for (i = 2, iz = this.__leavelist.length; i < iz; ++i) {294element = this.__leavelist[i];295addToPath(result, element.path);296}297addToPath(result, this.__current.path);298return result;299};300301// API:302// return type of current node303Controller.prototype.type = function () {304var node = this.current();305return node.type || this.__current.wrap;306};307308// API:309// return array of parent elements310Controller.prototype.parents = function parents() {311var i, iz, result;312313// first node is sentinel314result = [];315for (i = 1, iz = this.__leavelist.length; i < iz; ++i) {316result.push(this.__leavelist[i].node);317}318319return result;320};321322// API:323// return current node324Controller.prototype.current = function current() {325return this.__current.node;326};327328Controller.prototype.__execute = function __execute(callback, element) {329var previous, result;330331result = undefined;332333previous = this.__current;334this.__current = element;335this.__state = null;336if (callback) {337result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node);338}339this.__current = previous;340341return result;342};343344// API:345// notify control skip / break346Controller.prototype.notify = function notify(flag) {347this.__state = flag;348};349350// API:351// skip child nodes of current node352Controller.prototype.skip = function () {353this.notify(SKIP);354};355356// API:357// break traversals358Controller.prototype['break'] = function () {359this.notify(BREAK);360};361362// API:363// remove node364Controller.prototype.remove = function () {365this.notify(REMOVE);366};367368Controller.prototype.__initialize = function(root, visitor) {369this.visitor = visitor;370this.root = root;371this.__worklist = [];372this.__leavelist = [];373this.__current = null;374this.__state = null;375this.__fallback = null;376if (visitor.fallback === 'iteration') {377this.__fallback = Object.keys;378} else if (typeof visitor.fallback === 'function') {379this.__fallback = visitor.fallback;380}381382this.__keys = VisitorKeys;383if (visitor.keys) {384this.__keys = Object.assign(Object.create(this.__keys), visitor.keys);385}386};387388function isNode(node) {389if (node == null) {390return false;391}392return typeof node === 'object' && typeof node.type === 'string';393}394395function isProperty(nodeType, key) {396return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key;397}398399function candidateExistsInLeaveList(leavelist, candidate) {400for (var i = leavelist.length - 1; i >= 0; --i) {401if (leavelist[i].node === candidate) {402return true;403}404}405return false;406}407408Controller.prototype.traverse = function traverse(root, visitor) {409var worklist,410leavelist,411element,412node,413nodeType,414ret,415key,416current,417current2,418candidates,419candidate,420sentinel;421422this.__initialize(root, visitor);423424sentinel = {};425426// reference427worklist = this.__worklist;428leavelist = this.__leavelist;429430// initialize431worklist.push(new Element(root, null, null, null));432leavelist.push(new Element(null, null, null, null));433434while (worklist.length) {435element = worklist.pop();436437if (element === sentinel) {438element = leavelist.pop();439440ret = this.__execute(visitor.leave, element);441442if (this.__state === BREAK || ret === BREAK) {443return;444}445continue;446}447448if (element.node) {449450ret = this.__execute(visitor.enter, element);451452if (this.__state === BREAK || ret === BREAK) {453return;454}455456worklist.push(sentinel);457leavelist.push(element);458459if (this.__state === SKIP || ret === SKIP) {460continue;461}462463node = element.node;464nodeType = node.type || element.wrap;465candidates = this.__keys[nodeType];466if (!candidates) {467if (this.__fallback) {468candidates = this.__fallback(node);469} else {470throw new Error('Unknown node type ' + nodeType + '.');471}472}473474current = candidates.length;475while ((current -= 1) >= 0) {476key = candidates[current];477candidate = node[key];478if (!candidate) {479continue;480}481482if (Array.isArray(candidate)) {483current2 = candidate.length;484while ((current2 -= 1) >= 0) {485if (!candidate[current2]) {486continue;487}488489if (candidateExistsInLeaveList(leavelist, candidate[current2])) {490continue;491}492493if (isProperty(nodeType, candidates[current])) {494element = new Element(candidate[current2], [key, current2], 'Property', null);495} else if (isNode(candidate[current2])) {496element = new Element(candidate[current2], [key, current2], null, null);497} else {498continue;499}500worklist.push(element);501}502} else if (isNode(candidate)) {503if (candidateExistsInLeaveList(leavelist, candidate)) {504continue;505}506507worklist.push(new Element(candidate, key, null, null));508}509}510}511}512};513514Controller.prototype.replace = function replace(root, visitor) {515var worklist,516leavelist,517node,518nodeType,519target,520element,521current,522current2,523candidates,524candidate,525sentinel,526outer,527key;528529function removeElem(element) {530var i,531key,532nextElem,533parent;534535if (element.ref.remove()) {536// When the reference is an element of an array.537key = element.ref.key;538parent = element.ref.parent;539540// If removed from array, then decrease following items' keys.541i = worklist.length;542while (i--) {543nextElem = worklist[i];544if (nextElem.ref && nextElem.ref.parent === parent) {545if (nextElem.ref.key < key) {546break;547}548--nextElem.ref.key;549}550}551}552}553554this.__initialize(root, visitor);555556sentinel = {};557558// reference559worklist = this.__worklist;560leavelist = this.__leavelist;561562// initialize563outer = {564root: root565};566element = new Element(root, null, null, new Reference(outer, 'root'));567worklist.push(element);568leavelist.push(element);569570while (worklist.length) {571element = worklist.pop();572573if (element === sentinel) {574element = leavelist.pop();575576target = this.__execute(visitor.leave, element);577578// node may be replaced with null,579// so distinguish between undefined and null in this place580if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {581// replace582element.ref.replace(target);583}584585if (this.__state === REMOVE || target === REMOVE) {586removeElem(element);587}588589if (this.__state === BREAK || target === BREAK) {590return outer.root;591}592continue;593}594595target = this.__execute(visitor.enter, element);596597// node may be replaced with null,598// so distinguish between undefined and null in this place599if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {600// replace601element.ref.replace(target);602element.node = target;603}604605if (this.__state === REMOVE || target === REMOVE) {606removeElem(element);607element.node = null;608}609610if (this.__state === BREAK || target === BREAK) {611return outer.root;612}613614// node may be null615node = element.node;616if (!node) {617continue;618}619620worklist.push(sentinel);621leavelist.push(element);622623if (this.__state === SKIP || target === SKIP) {624continue;625}626627nodeType = node.type || element.wrap;628candidates = this.__keys[nodeType];629if (!candidates) {630if (this.__fallback) {631candidates = this.__fallback(node);632} else {633throw new Error('Unknown node type ' + nodeType + '.');634}635}636637current = candidates.length;638while ((current -= 1) >= 0) {639key = candidates[current];640candidate = node[key];641if (!candidate) {642continue;643}644645if (Array.isArray(candidate)) {646current2 = candidate.length;647while ((current2 -= 1) >= 0) {648if (!candidate[current2]) {649continue;650}651if (isProperty(nodeType, candidates[current])) {652element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2));653} else if (isNode(candidate[current2])) {654element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2));655} else {656continue;657}658worklist.push(element);659}660} else if (isNode(candidate)) {661worklist.push(new Element(candidate, key, null, new Reference(node, key)));662}663}664}665666return outer.root;667};668669function traverse(root, visitor) {670var controller = new Controller();671return controller.traverse(root, visitor);672}673674function replace(root, visitor) {675var controller = new Controller();676return controller.replace(root, visitor);677}678679function extendCommentRange(comment, tokens) {680var target;681682target = upperBound(tokens, function search(token) {683return token.range[0] > comment.range[0];684});685686comment.extendedRange = [comment.range[0], comment.range[1]];687688if (target !== tokens.length) {689comment.extendedRange[1] = tokens[target].range[0];690}691692target -= 1;693if (target >= 0) {694comment.extendedRange[0] = tokens[target].range[1];695}696697return comment;698}699700function attachComments(tree, providedComments, tokens) {701// At first, we should calculate extended comment ranges.702var comments = [], comment, len, i, cursor;703704if (!tree.range) {705throw new Error('attachComments needs range information');706}707708// tokens array is empty, we attach comments to tree as 'leadingComments'709if (!tokens.length) {710if (providedComments.length) {711for (i = 0, len = providedComments.length; i < len; i += 1) {712comment = deepCopy(providedComments[i]);713comment.extendedRange = [0, tree.range[0]];714comments.push(comment);715}716tree.leadingComments = comments;717}718return tree;719}720721for (i = 0, len = providedComments.length; i < len; i += 1) {722comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens));723}724725// This is based on John Freeman's implementation.726cursor = 0;727traverse(tree, {728enter: function (node) {729var comment;730731while (cursor < comments.length) {732comment = comments[cursor];733if (comment.extendedRange[1] > node.range[0]) {734break;735}736737if (comment.extendedRange[1] === node.range[0]) {738if (!node.leadingComments) {739node.leadingComments = [];740}741node.leadingComments.push(comment);742comments.splice(cursor, 1);743} else {744cursor += 1;745}746}747748// already out of owned node749if (cursor === comments.length) {750return VisitorOption.Break;751}752753if (comments[cursor].extendedRange[0] > node.range[1]) {754return VisitorOption.Skip;755}756}757});758759cursor = 0;760traverse(tree, {761leave: function (node) {762var comment;763764while (cursor < comments.length) {765comment = comments[cursor];766if (node.range[1] < comment.extendedRange[0]) {767break;768}769770if (node.range[1] === comment.extendedRange[0]) {771if (!node.trailingComments) {772node.trailingComments = [];773}774node.trailingComments.push(comment);775comments.splice(cursor, 1);776} else {777cursor += 1;778}779}780781// already out of owned node782if (cursor === comments.length) {783return VisitorOption.Break;784}785786if (comments[cursor].extendedRange[0] > node.range[1]) {787return VisitorOption.Skip;788}789}790});791792return tree;793}794795exports.Syntax = Syntax;796exports.traverse = traverse;797exports.replace = replace;798exports.attachComments = attachComments;799exports.VisitorKeys = VisitorKeys;800exports.VisitorOption = VisitorOption;801exports.Controller = Controller;802exports.cloneEnvironment = function () { return clone({}); };803804return exports;805}(exports));806/* vim: set sw=4 ts=4 et tw=80 : */807808809