Syncing with master

This commit is contained in:
sanex3339
2018-02-17 20:19:07 +03:00
20 changed files with 744 additions and 10113 deletions
+1
View File
@@ -4,3 +4,4 @@
coverage
images
test/fixtures/compile-performance.js
test-tmp
+12
View File
@@ -1,5 +1,17 @@
Change Log
===
v0.14.3
---
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/195
v0.14.2
---
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/181
v0.14.1
---
* Temporary fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/181
v0.14.0
---
* **New option:** `identifiersPrefix` sets prefix for all generated identifiers.
+1 -10051
View File
File diff suppressed because one or more lines are too long
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "javascript-obfuscator",
"version": "0.14.0",
"version": "0.14.3",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
@@ -19,7 +19,7 @@
"javascript-obfuscator": "./bin/javascript-obfuscator"
},
"dependencies": {
"chalk": "2.3.0",
"chalk": "2.3.1",
"chance": "1.0.13",
"class-validator": "0.8.1",
"commander": "2.14.1",
@@ -47,7 +47,7 @@
"@types/md5": "2.1.32",
"@types/mkdirp": "0.5.2",
"@types/mocha": "2.2.48",
"@types/node": "9.4.5",
"@types/node": "9.4.6",
"@types/rimraf": "2.0.2",
"@types/sinon": "4.1.3",
"@types/string-template": "1.0.2",
@@ -60,17 +60,17 @@
"chai": "4.1.2",
"coveralls": "3.0.0",
"istanbul": "1.1.0-alpha.1",
"mocha": "5.0.0",
"mocha": "5.0.1",
"pre-commit": "1.2.2",
"rimraf": "2.6.2",
"sinon": "4.3.0",
"threads": "0.10.1",
"ts-node": "4.1.0",
"tslint": "5.9.1",
"tslint-eslint-rules": "4.1.1",
"tslint-eslint-rules": "5.0.0",
"tslint-language-service": "0.9.8",
"tslint-webpack-plugin": "1.1.1",
"typescript": "2.7.1",
"typescript": "2.7.2",
"webpack": "3.11.0",
"webpack-node-externals": "1.6.0"
},
@@ -1,8 +1,12 @@
import { inject, injectable, } from 'inversify';
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
import * as estraverse from 'estraverse';
import * as ESTree from 'estree';
import { TNodeWithScope } from '../../types/node/TNodeWithScope';
import { TStatement } from '../../types/node/TStatement';
import { IOptions } from '../../interfaces/options/IOptions';
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
@@ -12,6 +16,7 @@ import { TransformationStage } from '../../enums/node-transformers/Transformatio
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
import { NodeGuards } from '../../node/NodeGuards';
import { Nodes } from '../../node/Nodes';
import { NodeUtils } from '../../node/NodeUtils';
/**
* Transform ES2015 template literals to ES5
@@ -47,6 +52,11 @@ export class TemplateLiteralTransformer extends AbstractNodeTransformer {
case TransformationStage.Converting:
return {
enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
if (parentNode && NodeGuards.isReturnStatementNode(node) && node.argument === null) {
return this.fixEsprimaReturnStatementTemplateLiteralNode(node);
}
},
leave: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
if (parentNode && NodeGuards.isTemplateLiteralNode(node)) {
return this.transformNode(node, parentNode);
}
@@ -66,7 +76,7 @@ export class TemplateLiteralTransformer extends AbstractNodeTransformer {
public transformNode (templateLiteralNode: ESTree.TemplateLiteral, parentNode: ESTree.Node): ESTree.Node {
const templateLiteralExpressions: ESTree.Expression[] = templateLiteralNode.expressions;
let nodes: (ESTree.Literal | ESTree.Expression)[] = [];
let nodes: ESTree.Expression[] = [];
templateLiteralNode.quasis.forEach((templateElement: ESTree.TemplateElement) => {
nodes.push(Nodes.getLiteralNode(templateElement.value.cooked));
@@ -109,4 +119,56 @@ export class TemplateLiteralTransformer extends AbstractNodeTransformer {
return nodes[0];
}
/**
* @param {ReturnStatement} returnStatementNode
* @returns {Node | VisitorOption}
*/
private fixEsprimaReturnStatementTemplateLiteralNode (returnStatementNode: ESTree.ReturnStatement): ESTree.Node | void {
const scopeNode: TNodeWithScope = NodeUtils.getScopeOfNode(returnStatementNode);
const scopeBody: TStatement[] = !NodeGuards.isSwitchCaseNode(scopeNode)
? scopeNode.body
: scopeNode.consequent;
const indexInScope: number = scopeBody.indexOf(returnStatementNode);
// in incorrect AST-tree return statement node should be penultimate
if (indexInScope !== scopeBody.length - 2) {
return;
}
const nextSiblingStatementNode: TStatement | null = scopeBody[indexInScope + 1];
if (!nextSiblingStatementNode || !NodeGuards.isExpressionStatementNode(nextSiblingStatementNode)) {
return;
}
let isSiblingStatementHasTemplateLiteralNode: boolean = false;
estraverse.traverse(nextSiblingStatementNode, {
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): void | estraverse.VisitorOption => {
if (!NodeGuards.isTemplateLiteralNode(node)) {
return;
}
isSiblingStatementHasTemplateLiteralNode = true;
return estraverse.VisitorOption.Break;
}
});
if (!isSiblingStatementHasTemplateLiteralNode) {
return;
}
returnStatementNode.argument = nextSiblingStatementNode.expression;
scopeBody.pop();
if (!NodeGuards.isSwitchCaseNode(scopeNode)) {
scopeNode.body = [...scopeBody];
} else {
scopeNode.consequent = <ESTree.Statement[]>[...scopeBody];
}
return returnStatementNode;
}
}
@@ -6,6 +6,7 @@ import * as ESTree from 'estree';
import { TDeadNodeInjectionCustomNodeFactory } from '../../types/container/custom-nodes/TDeadNodeInjectionCustomNodeFactory';
import { TNodeWithBlockScope } from '../../types/node/TNodeWithBlockScope';
import { TNodeWithScope } from '../../types/node/TNodeWithScope';
import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
import { IOptions } from '../../interfaces/options/IOptions';
@@ -97,15 +98,62 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
}
/**
* @param {Node} blockStatementNode
* @param {Node} targetNode
* @returns {boolean}
*/
private static isValidBlockStatementNode (blockStatementNode: ESTree.Node): boolean {
const isProhibitedNode: (node: ESTree.Node) => boolean =
(node: ESTree.Node): boolean => NodeGuards.isBreakStatementNode(node) ||
NodeGuards.isContinueStatementNode(node) ||
NodeGuards.isAwaitExpressionNode(node) ||
NodeGuards.isSuperNode(node);
private static isProhibitedNodeInsideCollectedBlockStatement (targetNode: ESTree.Node): boolean {
return NodeGuards.isBreakStatementNode(targetNode)
|| NodeGuards.isContinueStatementNode(targetNode)
|| NodeGuards.isAwaitExpressionNode(targetNode)
|| NodeGuards.isSuperNode(targetNode);
}
/**
* @param {Node} targetNode
* @returns {boolean}
*/
private static isScopeHoistingFunctionDeclaration (targetNode: ESTree.Node): boolean {
if (!NodeGuards.isFunctionDeclarationNode(targetNode)) {
return false;
}
const scopeNode: TNodeWithScope = NodeUtils.getScopeOfNode(targetNode);
const scopeBody: ESTree.Statement[] = !NodeGuards.isSwitchCaseNode(scopeNode)
? <ESTree.Statement[]>scopeNode.body
: scopeNode.consequent;
const indexInScope: number = scopeBody.indexOf(targetNode);
if (indexInScope === 0) {
return false;
}
const slicedBody: ESTree.Statement[] = scopeBody.slice(0, indexInScope);
const hostBlockStatementNode: ESTree.BlockStatement = Nodes.getBlockStatementNode(slicedBody);
const functionDeclarationName: string = targetNode.id.name;
let isScopeHoistedFunctionDeclaration: boolean = false;
estraverse.traverse(hostBlockStatementNode, {
enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
if (NodeGuards.isIdentifierNode(node) && node.name === functionDeclarationName) {
isScopeHoistedFunctionDeclaration = true;
return estraverse.VisitorOption.Break;
}
}
});
return isScopeHoistedFunctionDeclaration;
}
/**
* @param {BlockStatement} blockStatementNode
* @returns {boolean}
*/
private static isValidCollectedBlockStatementNode (blockStatementNode: ESTree.BlockStatement): boolean {
if (!blockStatementNode.body.length) {
return false;
}
let nestedBlockStatementsCount: number = 0;
let isValidBlockStatementNode: boolean = true;
@@ -117,8 +165,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
}
if (
nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount ||
isProhibitedNode(node)
nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount
|| DeadCodeInjectionTransformer.isProhibitedNodeInsideCollectedBlockStatement(node)
|| DeadCodeInjectionTransformer.isScopeHoistingFunctionDeclaration(node)
) {
isValidBlockStatementNode = false;
@@ -130,6 +179,37 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
return isValidBlockStatementNode;
}
/**
* @param {BlockStatement} blockStatementNode
* @returns {boolean}
*/
private static isValidWrappedBlockStatementNode (blockStatementNode: ESTree.BlockStatement): boolean {
if (!blockStatementNode.body.length) {
return false;
}
let isValidBlockStatementNode: boolean = true;
estraverse.traverse(blockStatementNode, {
enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
if (DeadCodeInjectionTransformer.isScopeHoistingFunctionDeclaration(node)) {
isValidBlockStatementNode = false;
return estraverse.VisitorOption.Break;
}
}
});
if (!isValidBlockStatementNode) {
return false;
}
const blockScopeOfBlockStatementNode: TNodeWithBlockScope = NodeUtils
.getBlockScopesOfNode(blockStatementNode)[0];
return blockScopeOfBlockStatementNode.type !== NodeType.Program;
}
/**
* @param {TransformationStage} transformationStage
* @returns {IVisitor | null}
@@ -183,7 +263,7 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
let clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(node);
if (!DeadCodeInjectionTransformer.isValidBlockStatementNode(clonedBlockStatementNode)) {
if (!DeadCodeInjectionTransformer.isValidCollectedBlockStatementNode(clonedBlockStatementNode)) {
return;
}
@@ -209,23 +289,21 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
* @param {NodeGuards} parentNode
* @returns {NodeGuards | VisitorOption}
*/
public transformNode (blockStatementNode: ESTree.BlockStatement, parentNode: ESTree.Node): ESTree.Node | estraverse.VisitorOption {
if (this.collectedBlockStatementsTotalLength < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount) {
public transformNode (
blockStatementNode: ESTree.BlockStatement,
parentNode: ESTree.Node
): ESTree.Node | estraverse.VisitorOption {
const canBreakTraverse: boolean = !this.collectedBlockStatements.length
|| this.collectedBlockStatementsTotalLength < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount;
if (canBreakTraverse) {
return estraverse.VisitorOption.Break;
}
if (!this.collectedBlockStatements.length) {
return estraverse.VisitorOption.Break;
}
if (this.randomGenerator.getMathRandom() > this.options.deadCodeInjectionThreshold) {
return blockStatementNode;
}
const blockScopeOfBlockStatementNode: TNodeWithBlockScope = NodeUtils
.getBlockScopesOfNode(blockStatementNode)[0];
if (blockScopeOfBlockStatementNode.type === NodeType.Program) {
if (
this.randomGenerator.getMathRandom() > this.options.deadCodeInjectionThreshold
|| !DeadCodeInjectionTransformer.isValidWrappedBlockStatementNode(blockStatementNode)
) {
return blockStatementNode;
}
@@ -233,8 +311,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
const maxInteger: number = this.collectedBlockStatements.length - 1;
const randomIndex: number = this.randomGenerator.getRandomInteger(minInteger, maxInteger);
const randomBlockStatementNode: ESTree.BlockStatement = this.collectedBlockStatements.splice(randomIndex, 1)[0];
const isDuplicateBlockStatementNodes: boolean = randomBlockStatementNode === blockStatementNode;
if (randomBlockStatementNode === blockStatementNode) {
if (isDuplicateBlockStatementNodes) {
return blockStatementNode;
}
+31
View File
@@ -71,6 +71,22 @@ export class NodeUtils {
return NodeUtils.getBlockScopesOfNodeRecursive(targetNode);
}
/**
* @param {Statement} node
* @returns {TStatement | null}
*/
public static getNextSiblingStatementNode (node: ESTree.Statement): TStatement | null {
return NodeUtils.getSiblingStatementNodeByOffset(node, 1);
}
/**
* @param {Statement} node
* @returns {TStatement | null}
*/
public static getPreviousSiblingStatementNode (node: ESTree.Statement): TStatement | null {
return NodeUtils.getSiblingStatementNodeByOffset(node, -1);
}
/**
* @param {NodeGuards} node
* @returns {TNodeWithScope}
@@ -208,4 +224,19 @@ export class NodeUtils {
return blockScopes;
}
/**
* @param {Statement} node
* @param {number} offset
* @returns {TStatement | null}
*/
private static getSiblingStatementNodeByOffset (node: ESTree.Statement, offset: number): TStatement | null {
const scopeNode: TNodeWithScope = NodeUtils.getScopeOfNode(node);
const scopeBody: TStatement[] = !NodeGuards.isSwitchCaseNode(scopeNode)
? scopeNode.body
: scopeNode.consequent;
const indexInScope: number = scopeBody.indexOf(node);
return scopeBody[indexInScope + offset] || null;
}
}
+27 -5
View File
@@ -7,18 +7,40 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`
(function(){
var foo = 'foo'; var bar = 'bar'; var bark = foo + bar,; var baz = 'baz';
function foo () {
var a = 1;
inner1();
var b = 2;
function inner1 () {}
var c = 3;
}
function bar () {
var a = 1;
}
function baz () {
var a = 1;
}
function bark () {
var a = 1;
}
function hawk () {
var a = 1;
}
function eagle () {
var a = 1;
}
})();
`,
{
...NO_ADDITIONAL_NODES_PRESET,
compact: false,
identifiersPrefix: 'foo',
identifierNamesGenerator: 'mangled',
renameGlobals: true
stringArray: true,
stringArrayThreshold: 1,
deadCodeInjection: true,
deadCodeInjectionThreshold: 1
}
).getObfuscatedCode();
console.log(obfuscatedCode);
console.log(eval(obfuscatedCode));
})();
})();
@@ -24,7 +24,74 @@ describe('TemplateLiteralTransformer', () => {
});
});
describe('variant #1: simple template literal with expression only', () => {
describe('variant #2: multiline template literals', () => {
it('variant #1: should transform es6 multiline template literal to es5', () => {
const code: string = readFileAsString(__dirname + '/fixtures/multiline-template-literal.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
unicodeEscapeSequence: false
}
);
assert.match(obfuscationResult.getObfuscatedCode(), /^var *test *= *'foo\\x0abar';$/);
});
it('variant #2: should transform es6 multiline template literal inside return statement', () => {
const code: string = readFileAsString(__dirname + '/fixtures/multiline-template-literal-return-statement-1.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
unicodeEscapeSequence: false
}
);
assert.match(obfuscationResult.getObfuscatedCode(), /{ *return *'foo\\x0abar'; *}$/);
});
it('variant #3: should transform es6 multiline template literal inside return statement', () => {
const code: string = readFileAsString(__dirname + '/fixtures/multiline-template-literal-return-statement-2.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
unicodeEscapeSequence: false
}
);
assert.match(obfuscationResult.getObfuscatedCode(), /case *!!\[] *: *return *'foo\\x0abar'; *} *}$/);
});
it('variant #4: should transform es6 multiline template literal inside binary expression inside return statement', () => {
const code: string = readFileAsString(__dirname + '/fixtures/multiline-template-literal-binary-expression-return-statement-1.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
unicodeEscapeSequence: false
}
);
assert.match(obfuscationResult.getObfuscatedCode(), /{ *return *'foo\\x0abar' *\+ *0x1; *}$/);
});
it('variant #5: should transform es6 multiline template literal inside binary expression inside return statement', () => {
const code: string = readFileAsString(__dirname + '/fixtures/multiline-template-literal-binary-expression-return-statement-2.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
unicodeEscapeSequence: false
}
);
assert.match(obfuscationResult.getObfuscatedCode(), /case *!!\[] *: *return *'foo\\x0abar' *\+ *0x1; *} *}$/);
});
});
describe('variant #3: simple template literal with expression only', () => {
it('should transform es6 template literal to es5 and add empty literal node before expression node', () => {
const code: string = readFileAsString(__dirname + '/fixtures/expression-only.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
@@ -39,7 +106,7 @@ describe('TemplateLiteralTransformer', () => {
});
});
describe('variant #3: literal node inside expression', () => {
describe('variant #4: literal node inside expression', () => {
it('should transform es6 template literal to es5', () => {
const code: string = readFileAsString(__dirname + '/fixtures/literal-inside-expression.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
@@ -54,7 +121,7 @@ describe('TemplateLiteralTransformer', () => {
});
});
describe('variant #4: multiple expressions', () => {
describe('variant #5: multiple expressions', () => {
it('should transform es6 template literal to es5', () => {
const code: string = readFileAsString(__dirname + '/fixtures/multiple-expressions.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
@@ -0,0 +1,7 @@
function hi() {
switch (true) {
case true:
return `foo
bar`;
}
}
@@ -481,5 +481,115 @@ describe('DeadCodeInjectionTransformer', () => {
assert.notEqual(returnIdentifierName, variableDeclarationIdentifierName);
});
});
describe('variant #11 - block statements with empty body', () => {
const regExp: RegExp = new RegExp(
`function *${variableMatch} *\\(\\) *{ *} *` +
`${variableMatch} *\\(\\); *`,
'g'
);
const expectedMatchesLength: number = 5;
let matchesLength: number = 0;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/block-statement-empty-body.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayThreshold: 1,
deadCodeInjection: true,
deadCodeInjectionThreshold: 1
}
);
const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(regExp);
if (functionMatches) {
matchesLength = functionMatches.length;
}
});
it('shouldn\'t add dead code conditions to the block empty block statements', () => {
assert.isAtLeast(matchesLength, expectedMatchesLength);
});
});
describe('variant #12 - block statement with scope-hoisting', () => {
describe('variant #1: collecting of block statements', () => {
const regExp: RegExp = new RegExp(
`${variableMatch} *\\(\\); *` +
`var *${variableMatch} *= *0x2; *` +
`function *${variableMatch} *\\(\\) *{ *} *`,
'g'
);
const expectedMatchesLength: number = 5;
let matchesLength: number = 0;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/block-statement-with-scope-hoisting-1.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayThreshold: 1,
deadCodeInjection: true,
deadCodeInjectionThreshold: 1
}
);
const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(regExp);
if (functionMatches) {
matchesLength = functionMatches.length;
}
});
it('shouldn\'t collect block statements with scope-hoisting', () => {
assert.equal(matchesLength, expectedMatchesLength);
});
});
describe('variant #2: wrapping of block statements in dead code conditions', () => {
const regExp: RegExp = new RegExp(
`function *${variableMatch} *\\(\\) *{ *` +
`var *${variableMatch} *= *0x1; *` +
`${variableMatch} *\\(\\); *` +
`var *${variableMatch} *= *0x2; *` +
`function *${variableMatch} *\\(\\) *{ *} *` +
`var *${variableMatch} *= *0x3; *` +
`}`,
'g'
);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/block-statement-with-scope-hoisting-2.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayThreshold: 1,
deadCodeInjection: true,
deadCodeInjectionThreshold: 1
}
);
obfuscatedCode = obfuscationResult.getObfuscatedCode();
});
it('shouldn\'t wrap block statements in dead code conditions', () => {
assert.match(obfuscatedCode, regExp);
});
});
});
});
});
@@ -0,0 +1,22 @@
(function(){
function foo () {
function inner1 () {}
inner1();
}
function bar () {
function inner2 () {}
inner2();
}
function baz () {
function inner3 () {}
inner3();
}
function bark () {
function inner4 () {}
inner4();
}
function hawk () {
function inner5 () {}
inner5();
}
})();
@@ -0,0 +1,37 @@
(function(){
function foo () {
var a = 1;
inner1();
var b = 2;
function inner1 () {}
var c = 3;
}
function bar () {
var a = 1;
inner2();
var b = 2;
function inner2 () {}
var c = 3;
}
function baz () {
var a = 1;
inner3();
var b = 2;
function inner3 () {}
var c = 3;
}
function bark () {
var a = 1;
inner4();
var b = 2;
function inner4 () {}
var c = 3;
}
function hawk () {
var a = 1;
inner5();
var b = 2;
function inner5 () {}
var c = 3;
}
})();
@@ -0,0 +1,24 @@
(function(){
function foo () {
var a = 1;
inner1();
var b = 2;
function inner1 () {}
var c = 3;
}
function bar () {
var a = 1;
}
function baz () {
var a = 1;
}
function bark () {
var a = 1;
}
function hawk () {
var a = 1;
}
function eagle () {
var a = 1;
}
})();
@@ -294,6 +294,176 @@ describe('NodeUtils', () => {
});
});
describe('getNextSiblingStatementNode (node: ESTree.Statement): TStatement | null', () => {
describe('variant #1: block statement node as scope node', () => {
let statementNode1: ESTree.Statement,
statementNode2: ESTree.Statement,
statementNode3: ESTree.Statement;
before(() => {
statementNode1 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('a')
);
statementNode2 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('b')
);
statementNode3 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('c')
);
const blockStatementNode: ESTree.BlockStatement = Nodes.getBlockStatementNode([
statementNode1,
statementNode2,
statementNode3
]);
statementNode1.parentNode = blockStatementNode;
statementNode2.parentNode = blockStatementNode;
statementNode3.parentNode = blockStatementNode;
});
it('should return next sibling statement node', () => {
assert.deepEqual(NodeUtils.getNextSiblingStatementNode(statementNode1), statementNode2);
});
it('should return next sibling statement node', () => {
assert.deepEqual(NodeUtils.getNextSiblingStatementNode(statementNode2), statementNode3);
});
it('should return `null` if given statement node is last node in the scope', () => {
assert.deepEqual(NodeUtils.getNextSiblingStatementNode(statementNode3), null);
});
});
describe('variant #2: switch case node as scope node', () => {
let statementNode1: ESTree.Statement,
statementNode2: ESTree.Statement,
statementNode3: ESTree.Statement;
before(() => {
statementNode1 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('a')
);
statementNode2 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('b')
);
statementNode3 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('c')
);
const switchCaseNode: ESTree.SwitchCase = Nodes.getSwitchCaseNode(
Nodes.getLiteralNode(true),
[
statementNode1,
statementNode2,
statementNode3
]
);
statementNode1.parentNode = switchCaseNode;
statementNode2.parentNode = switchCaseNode;
statementNode3.parentNode = switchCaseNode;
});
it('should return next sibling statement node', () => {
assert.deepEqual(NodeUtils.getNextSiblingStatementNode(statementNode1), statementNode2);
});
it('should return next sibling statement node', () => {
assert.deepEqual(NodeUtils.getNextSiblingStatementNode(statementNode2), statementNode3);
});
it('should return `null` if given statement node is last node in the scope', () => {
assert.deepEqual(NodeUtils.getNextSiblingStatementNode(statementNode3), null);
});
});
});
describe('getPreviousSiblingStatementNode (node: ESTree.Statement): TStatement | null', () => {
describe('variant #1: block statement node as scope node', () => {
let statementNode1: ESTree.Statement,
statementNode2: ESTree.Statement,
statementNode3: ESTree.Statement;
before(() => {
statementNode1 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('a')
);
statementNode2 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('b')
);
statementNode3 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('c')
);
const blockStatementNode: ESTree.BlockStatement = Nodes.getBlockStatementNode([
statementNode1,
statementNode2,
statementNode3
]);
statementNode1.parentNode = blockStatementNode;
statementNode2.parentNode = blockStatementNode;
statementNode3.parentNode = blockStatementNode;
});
it('should return next sibling statement node', () => {
assert.deepEqual(NodeUtils.getPreviousSiblingStatementNode(statementNode1), null);
});
it('should return next sibling statement node', () => {
assert.deepEqual(NodeUtils.getPreviousSiblingStatementNode(statementNode2), statementNode1);
});
it('should return `null` if given statement node is last node in the scope', () => {
assert.deepEqual(NodeUtils.getPreviousSiblingStatementNode(statementNode3), statementNode2);
});
});
describe('variant #2: switch case node as scope node', () => {
let statementNode1: ESTree.Statement,
statementNode2: ESTree.Statement,
statementNode3: ESTree.Statement;
before(() => {
statementNode1 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('a')
);
statementNode2 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('b')
);
statementNode3 = Nodes.getExpressionStatementNode(
Nodes.getIdentifierNode('c')
);
const switchCaseNode: ESTree.SwitchCase = Nodes.getSwitchCaseNode(
Nodes.getLiteralNode(true),
[
statementNode1,
statementNode2,
statementNode3
]
);
statementNode1.parentNode = switchCaseNode;
statementNode2.parentNode = switchCaseNode;
statementNode3.parentNode = switchCaseNode;
});
it('should return next sibling statement node', () => {
assert.deepEqual(NodeUtils.getPreviousSiblingStatementNode(statementNode1), null);
});
it('should return next sibling statement node', () => {
assert.deepEqual(NodeUtils.getPreviousSiblingStatementNode(statementNode2), statementNode1);
});
it('should return `null` if given statement node is last node in the scope', () => {
assert.deepEqual(NodeUtils.getPreviousSiblingStatementNode(statementNode3), statementNode2);
});
});
});
describe('getScopeOfNode (node: ESTree.Node): TNodeWithScope | null', () => {
let functionDeclarationBlockStatementNode: ESTree.BlockStatement,
ifStatementBlockStatementNode1: ESTree.BlockStatement,
+42 -22
View File
@@ -72,9 +72,9 @@
version "8.0.53"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.53.tgz#396b35af826fa66aad472c8cb7b8d5e277f4e6d8"
"@types/node@9.4.5":
version "9.4.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.5.tgz#d2a90c634208173d1b1a0a6ba9f1df3de62edcf5"
"@types/node@9.4.6":
version "9.4.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.6.tgz#d8176d864ee48753d053783e4e463aec86b8d82e"
"@types/rimraf@2.0.2":
version "2.0.2"
@@ -168,7 +168,7 @@ ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
ansi-styles@^3.1.0:
ansi-styles@^3.1.0, ansi-styles@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88"
dependencies:
@@ -969,7 +969,15 @@ chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chalk@2.3.0, chalk@^2.1.0, chalk@^2.3.0:
chalk@2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796"
dependencies:
ansi-styles "^3.2.0"
escape-string-regexp "^1.0.5"
supports-color "^5.2.0"
chalk@^2.1.0, chalk@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba"
dependencies:
@@ -1330,7 +1338,7 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0"
randombytes "^2.0.0"
doctrine@^0.7.2:
doctrine@0.7.2:
version "0.7.2"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.7.2.tgz#7cb860359ba3be90e040b26b729ce4bfa654c523"
dependencies:
@@ -1839,6 +1847,10 @@ has-flag@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@@ -2575,9 +2587,9 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.0:
dependencies:
minimist "0.0.8"
mocha@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.0.0.tgz#cccac988b0bc5477119cba0e43de7af6d6ad8f4e"
mocha@5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.0.1.tgz#759b62c836b0732382a62b6b1fb245ec1bc943ac"
dependencies:
browser-stdout "1.3.0"
commander "2.11.0"
@@ -3603,6 +3615,12 @@ supports-color@^5.1.0:
dependencies:
has-flag "^2.0.0"
supports-color@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.2.0.tgz#b0d5333b1184dd3666cbe5aa0b45c5ac7ac17a4a"
dependencies:
has-flag "^3.0.0"
tapable@^0.2.5, tapable@^0.2.7:
version "0.2.8"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22"
@@ -3722,17 +3740,17 @@ tslib@1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
tslib@^1.0.0, tslib@^1.7.1, tslib@^1.8.0:
tslib@^1.7.1, tslib@^1.8.0:
version "1.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.8.1.tgz#6946af2d1d651a7b1863b531d6e5afa41aa44eac"
tslint-eslint-rules@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/tslint-eslint-rules/-/tslint-eslint-rules-4.1.1.tgz#7c30e7882f26bc276bff91d2384975c69daf88ba"
tslint-eslint-rules@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/tslint-eslint-rules/-/tslint-eslint-rules-5.0.0.tgz#ba46e32168137b7b6f73c2121a29d57abea032a6"
dependencies:
doctrine "^0.7.2"
tslib "^1.0.0"
tsutils "^1.4.0"
doctrine "0.7.2"
tslib "1.9.0"
tsutils "2.8.0"
tslint-language-service@0.9.8:
version "0.9.8"
@@ -3761,9 +3779,11 @@ tslint@5.9.1:
tslib "^1.8.0"
tsutils "^2.12.1"
tsutils@^1.4.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-1.9.1.tgz#b9f9ab44e55af9681831d5f28d0aeeaf5c750cb0"
tsutils@2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.0.tgz#0160173729b3bf138628dd14a1537e00851d814a"
dependencies:
tslib "^1.7.1"
tsutils@^2.12.1:
version "2.12.2"
@@ -3803,9 +3823,9 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
typescript@2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.1.tgz#bb3682c2c791ac90e7c6210b26478a8da085c359"
typescript@2.7.2:
version "2.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836"
uglify-js@^2.6, uglify-js@^2.8.29:
version "2.8.29"