Fixed behaviour of simplify options when node with a single-statement body is inside simplified IfStatement node

Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/860
This commit is contained in:
sanex
2021-01-17 19:53:16 +03:00
parent ee41f06b3c
commit 5289dcc834
18 changed files with 955 additions and 8 deletions
+4
View File
@@ -1,5 +1,9 @@
Change Log
v2.10.2
---
* Fixed behaviour of `simplify` options when node with a single-statement `body` is inside simplified `IfStatement` node. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/860
v2.10.1
---
* Removed padding characters from all base64 encoded strings. Removed RegExp that trims padding characters from `base64` encoded strings from `atob` code helper to prevent mutation of `RegExp.$1` value during calls to the `stringArray`. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/829
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "javascript-obfuscator",
"version": "2.10.1",
"version": "2.10.2",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
+2
View File
@@ -14,6 +14,7 @@ export enum NodeType {
ClassDeclaration = 'ClassDeclaration',
ConditionalExpression = 'ConditionalExpression',
ContinueStatement = 'ContinueStatement',
DoWhileStatement = 'DoWhileStatement',
ExportAllDeclaration = 'ExportAllDeclaration',
ExportNamedDeclaration = 'ExportNamedDeclaration',
ExportSpecifier = 'ExportSpecifier',
@@ -54,6 +55,7 @@ export enum NodeType {
UpdateExpression = 'UpdateExpression',
VariableDeclaration = 'VariableDeclaration',
VariableDeclarator = 'VariableDeclarator',
WithStatement = 'WithStatement',
WhileStatement = 'WhileStatement',
YieldExpression = 'YieldExpression'
}
@@ -286,6 +286,7 @@ export class IfStatementSimplifyTransformer extends AbstractStatementSimplifyTra
*/
return NodeGuards.isFunctionDeclarationNode(statement)
/**
* Ignore any nodes with a single statement as a `body`
* Without ignore it can break following code:
* Input:
* if (condition1) {
@@ -302,8 +303,10 @@ export class IfStatementSimplifyTransformer extends AbstractStatementSimplifyTra
* var foo = bar();
* else
* var baz = bark();
*
* See issue: https://github.com/javascript-obfuscator/javascript-obfuscator/issues/860
*/
|| NodeGuards.isIfStatementNode(statement)
|| NodeGuards.isNodeWithSingleStatementBody(statement)
/**
* `let` and `const` variable declarations are not allowed outside of `IfStatement` block statement
+97
View File
@@ -167,6 +167,20 @@ export class NodeFactory {
};
}
/**
* @param {Statement} body
* @param {Expression} test
* @returns {DoWhileStatement}
*/
public static doWhileStatementNode (body: ESTree.Statement, test: ESTree.Expression): ESTree.DoWhileStatement {
return {
type: NodeType.DoWhileStatement,
body,
test,
metadata: { ignoredNode: false }
};
}
/**
* @param {Literal} source
* @returns {ExportAllDeclaration}
@@ -210,6 +224,72 @@ export class NodeFactory {
};
}
/**
* @param {VariableDeclaration | Expression | null} init
* @param {Expression | null} test
* @param {Expression | null} update
* @param {Statement} body
* @returns {ForStatement}
*/
public static forStatementNode (
init: ESTree.VariableDeclaration | ESTree.Expression | null,
test: ESTree.Expression | null,
update: ESTree.Expression | null,
body: ESTree.Statement
): ESTree.ForStatement {
return {
type: NodeType.ForStatement,
init,
test,
update,
body,
metadata: { ignoredNode: false }
};
}
/**
* @param {VariableDeclaration | Pattern} left
* @param {Expression} right
* @param {Statement} body
* @returns {ForInStatement}
*/
public static forInStatementNode (
left: ESTree.VariableDeclaration | ESTree.Pattern,
right: ESTree.Expression,
body: ESTree.Statement
): ESTree.ForInStatement {
return {
type: NodeType.ForInStatement,
left,
right,
body,
metadata: { ignoredNode: false }
};
}
/**
* @param {boolean} await
* @param {VariableDeclaration | Pattern} left
* @param {Expression} right
* @param {Statement} body
* @returns {ForOfStatement}
*/
public static forOfStatementNode (
await: boolean,
left: ESTree.VariableDeclaration | ESTree.Pattern,
right: ESTree.Expression,
body: ESTree.Statement
): ESTree.ForOfStatement {
return {
type: NodeType.ForOfStatement,
await,
left,
right,
body,
metadata: { ignoredNode: false }
};
}
/**
* @param {string} functionName
* @param {Identifier[]} params
@@ -298,6 +378,23 @@ export class NodeFactory {
};
}
/**
* @param {Identifier} label
* @param {Statement} body
* @returns {LabeledStatement}
*/
public static labeledStatementNode (
label: ESTree.Identifier,
body: ESTree.Statement
): ESTree.LabeledStatement {
return {
type: NodeType.LabeledStatement,
label,
body,
metadata: { ignoredNode: false }
};
}
/**
* @param {boolean | number | string} value
* @param {string} raw
+76
View File
@@ -1,3 +1,4 @@
/* eslint-disable max-lines */
import * as ESTree from 'estree';
import { TNodeWithLexicalScope } from '../types/node/TNodeWithLexicalScope';
@@ -5,6 +6,7 @@ import { TNodeWithLexicalScopeStatements } from '../types/node/TNodeWithLexicalS
import { TNodeWithStatements } from '../types/node/TNodeWithStatements';
import { NodeType } from '../enums/node/NodeType';
import { TNodeWithSingleStatementBody } from '../types/node/TNodeWithSingleStatementBody';
export class NodeGuards {
/**
@@ -116,6 +118,14 @@ export class NodeGuards {
&& 'directive' in node;
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isDoWhileStatementNode (node: ESTree.Node): node is ESTree.DoWhileStatement {
return node.type === NodeType.DoWhileStatement;
}
/**
* @param {Node} node
* @returns {boolean}
@@ -149,6 +159,22 @@ export class NodeGuards {
&& !('directive' in node);
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isForStatementNode (node: ESTree.Node): node is ESTree.ForStatement {
return node.type === NodeType.ForStatement;
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isForInStatementNode (node: ESTree.Node): node is ESTree.ForInStatement {
return node.type === NodeType.ForInStatement;
}
/**
* @param {Node} node
* @returns {boolean}
@@ -201,6 +227,19 @@ export class NodeGuards {
return node.type === NodeType.IfStatement;
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isIfStatementNodeWithSingleStatementBody (node: ESTree.Node): node is ESTree.IfStatement {
if (!NodeGuards.isIfStatementNode(node)) {
return false;
}
return !NodeGuards.isBlockStatementNode(node.consequent)
|| (!!node.alternate && !NodeGuards.isBlockStatementNode(node.alternate));
}
/**
* @param {Node} node
* @returns {boolean}
@@ -287,6 +326,35 @@ export class NodeGuards {
return NodeGuards.isNodeWithLexicalScope(node) || NodeGuards.isBlockStatementNode(node);
}
/**
* Checks if a node is the node with single statement body, like:
* while (true)
* console.log(1);
*
* or:
*
*
* @param {Node} node
* @returns {boolean}
*/
public static isNodeWithSingleStatementBody (node: ESTree.Node): node is TNodeWithSingleStatementBody {
// Different approach for `IfStatement` node because this node hasn't `body` property
if (NodeGuards.isIfStatementNode(node)) {
return NodeGuards.isIfStatementNodeWithSingleStatementBody(node);
}
// All other nodes with `Statement` node as `body` property
return (
NodeGuards.isForStatementNode(node)
|| NodeGuards.isForOfStatementNode(node)
|| NodeGuards.isForInStatementNode(node)
|| NodeGuards.isWhileStatementNode(node)
|| NodeGuards.isDoWhileStatementNode(node)
|| NodeGuards.isWithStatementNode(node)
|| NodeGuards.isLabeledStatementNode(node)
) && !NodeGuards.isBlockStatementNode(node.body);
}
/**
* @param {Node} node
* @param {Node} parentNode
@@ -438,6 +506,14 @@ export class NodeGuards {
return node.type === NodeType.VariableDeclarator;
}
/**
* @param {Node} node
* @returns {boolean}
*/
public static isWithStatementNode (node: ESTree.Node): node is ESTree.WithStatement {
return node.type === NodeType.WithStatement;
}
/**
* @param {Node} node
* @returns {boolean}
@@ -0,0 +1,21 @@
import * as ESTree from 'estree';
export type TNodeWithSingleStatementBody = (
ESTree.LabeledStatement
| ESTree.WithStatement
| ESTree.WhileStatement
| ESTree.DoWhileStatement
| ESTree.ForStatement
| ESTree.ForInStatement
| ESTree.ForOfStatement
& {
body: Exclude<ESTree.Statement, ESTree.BlockStatement>;
}
)
| (
ESTree.IfStatement
& {
consequent: Exclude<ESTree.Statement, ESTree.BlockStatement>;
alternate?: Exclude<ESTree.Statement, ESTree.BlockStatement> | null;
}
);
@@ -781,7 +781,182 @@ describe('IfStatementSimplifyTransformer', () => {
});
});
describe('Variant #2: `FunctionDeclaration` as prohibited single statement', () => {
describe('Variant #2: Nodes with single statement `body` property', () => {
describe('Variant #1: Single line `ForStatement` as prohibited single statement', () => {
const regExp: RegExp = new RegExp(
'if *\\(!!\\[]\\) *{ *' +
'for *\\( *' +
'let _0x([a-f0-9]){4,6} *= *0x0; *' +
'_0x([a-f0-9]){4,6} *< *0x1; *' +
'_0x([a-f0-9]){4,6}\\+\\+ *' +
'\\) *' +
'console\\[\'log\']\\(_0x([a-f0-9]){4,6}\\); *' +
'} *else *' +
'var _0x([a-f0-9]){4,6} *= *hawk\\(\\);'
);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/single-line-for-statement-as-prohibited-single-statement.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
simplify: true
}
).getObfuscatedCode();
});
it('should not simplify if statement', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #2: Single line `ForOfStatement` as prohibited single statement', () => {
const regExp: RegExp = new RegExp(
'if *\\(!!\\[]\\) *{ *' +
'for *\\(const _0x([a-f0-9]){4,6} of *\\[\\]\\) *' +
'console\\[\'log\']\\(_0x([a-f0-9]){4,6}\\); *' +
'} *else *' +
'var _0x([a-f0-9]){4,6} *= *hawk\\(\\);'
);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/single-line-for-of-statement-as-prohibited-single-statement.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
simplify: true
}
).getObfuscatedCode();
});
it('should not simplify if statement', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #3: Single line `ForInStatement` as prohibited single statement', () => {
const regExp: RegExp = new RegExp(
'if *\\(!!\\[]\\) *{ *' +
'for *\\(const _0x([a-f0-9]){4,6} in *\\{\\}\\) *' +
'console\\[\'log\']\\(_0x([a-f0-9]){4,6}\\); *' +
'} *else *' +
'var _0x([a-f0-9]){4,6} *= *hawk\\(\\);'
);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/single-line-for-in-statement-as-prohibited-single-statement.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
simplify: true
}
).getObfuscatedCode();
});
it('should not simplify if statement', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #4: Single line `WhileStatement` as prohibited single statement', () => {
const regExp: RegExp = new RegExp(
'if *\\(!!\\[]\\) *{ *' +
'while *\\(!!\\[]\\) *' +
'console\\[\'log\']\\(0x1\\); *' +
'} *else *' +
'var _0x([a-f0-9]){4,6} *= *hawk\\(\\);'
);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/single-line-while-statement-as-prohibited-single-statement.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
simplify: true
}
).getObfuscatedCode();
});
it('should not simplify if statement', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #5: Single line `DoWhileStatement` as prohibited single statement', () => {
const regExp: RegExp = new RegExp(
'if *\\(!!\\[]\\) *{ *' +
'do *' +
'console\\[\'log\']\\(0x1\\); *' +
'while *\\(!!\\[]\\); *' +
'} *else *' +
'var _0x([a-f0-9]){4,6} *= *hawk\\(\\);'
);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/single-line-do-while-statement-as-prohibited-single-statement.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
simplify: true
}
).getObfuscatedCode();
});
it('should not simplify if statement', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #6: Single line `LabeledStatement` as prohibited single statement', () => {
const regExp: RegExp = new RegExp(
'if *\\(!!\\[]\\) *{ *' +
'_0x([a-f0-9]){4,6}: *' +
'console\\[\'log\']\\(0x1\\); *' +
'} *else *' +
'var _0x([a-f0-9]){4,6} *= *hawk\\(\\);'
);
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/single-line-labeled-statement-as-prohibited-single-statement.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
simplify: true
}
).getObfuscatedCode();
});
it('should not simplify if statement', () => {
assert.match(obfuscatedCode, regExp);
});
});
});
describe('Variant #3: `FunctionDeclaration` as prohibited single statement', () => {
const regExp: RegExp = new RegExp(
'if *\\(!!\\[]\\) *{ *' +
'function _0x([a-f0-9]){4,6} *\\(\\) *{} *' +
@@ -810,7 +985,7 @@ describe('IfStatementSimplifyTransformer', () => {
});
});
describe('Variant #3: `let` `VariableDeclaration` as prohibited single statement', () => {
describe('Variant #4: `let` `VariableDeclaration` as prohibited single statement', () => {
const regExp: RegExp = new RegExp(
'if *\\(!!\\[]\\) *{ *' +
'let foo *= *0x1; *' +
@@ -837,7 +1012,7 @@ describe('IfStatementSimplifyTransformer', () => {
});
});
describe('Variant #4: `const` `VariableDeclaration` as prohibited single statement', () => {
describe('Variant #5: `const` `VariableDeclaration` as prohibited single statement', () => {
const regExp: RegExp = new RegExp(
'if *\\(!!\\[]\\) *{ *' +
'const foo *= *0x1; *' +
@@ -0,0 +1,9 @@
function foo() {
if (true) {
do
console.log(1);
while (true);
} else {
var bark = hawk();
}
}
@@ -0,0 +1,8 @@
function foo() {
if (true) {
for (const key in {})
console.log(key);
} else {
var bark = hawk();
}
}
@@ -0,0 +1,8 @@
function foo() {
if (true) {
for (const item of [])
console.log(item);
} else {
var bark = hawk();
}
}
@@ -0,0 +1,8 @@
function foo() {
if (true) {
for (let i= 0; i < 1; i++)
console.log(i);
} else {
var bark = hawk();
}
}
@@ -0,0 +1,8 @@
function foo() {
if (true) {
label:
console.log(1);
} else {
var bark = hawk();
}
}
@@ -0,0 +1,8 @@
function foo() {
if (true) {
while (true)
console.log(1);
} else {
var bark = hawk();
}
}
@@ -7,6 +7,107 @@ import { NodeFactory } from '../../../../src/node/NodeFactory';
import { NodeUtils } from '../../../../src/node/NodeUtils';
describe('NodeGuards', () => {
describe('isIfStatementNodeWithSingleStatementBody', () => {
describe('truthful checks', () => {
describe('Variant #1: single statement `consequent`', () => {
const expectedResult: boolean = true;
const node: ESTree.IfStatement = NodeFactory.ifStatementNode(
NodeFactory.literalNode(true),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isIfStatementNodeWithSingleStatementBody(node);
});
it('should check if `IfStatement` node has single statement `consequent`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #2: single statement `alternate`', () => {
const expectedResult: boolean = true;
const node: ESTree.IfStatement = NodeFactory.ifStatementNode(
NodeFactory.literalNode(true),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
]),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isIfStatementNodeWithSingleStatementBody(node);
});
it('should check if `IfStatement` node has single statement `alternate`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #3: single statement `consequent` and `alternate`', () => {
const expectedResult: boolean = true;
const node: ESTree.IfStatement = NodeFactory.ifStatementNode(
NodeFactory.literalNode(true),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isIfStatementNodeWithSingleStatementBody(node);
});
it('should check if `IfStatement` node has single statement `consequent` and `alternate`', () => {
assert.equal(result, expectedResult);
});
});
});
describe('false checks', () => {
describe('Variant #1: multiple statements `consequent` and `alternate`', () => {
const expectedResult: boolean = false;
const node: ESTree.IfStatement = NodeFactory.ifStatementNode(
NodeFactory.literalNode(true),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
]),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
])
);
let result: boolean;
before(() => {
result = NodeGuards.isIfStatementNodeWithSingleStatementBody(node);
});
it('should check if `IfStatement` node has multiple statements `consequent` and `alternate`', () => {
assert.equal(result, expectedResult);
});
});
});
});
describe('isNodeWithLexicalScopeStatements', () => {
describe('truthful checks', () => {
describe('Variant #1: block statement of function declaration', () => {
@@ -138,6 +239,425 @@ describe('NodeGuards', () => {
});
});
describe('isNodeWithSingleStatementBody', () => {
describe('truthful checks', () => {
describe('Variant #1: `IfStatement` node', () => {
describe('Variant #1: single statement `consequent`', () => {
const expectedResult: boolean = true;
const node: ESTree.IfStatement = NodeFactory.ifStatementNode(
NodeFactory.literalNode(true),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `IfStatement` node has single statement `consequent`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #2: single statement `alternate`', () => {
const expectedResult: boolean = true;
const node: ESTree.IfStatement = NodeFactory.ifStatementNode(
NodeFactory.literalNode(true),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
]),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `IfStatement` node has single statement `alternate`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #3: single statement `consequent` and `alternate`', () => {
const expectedResult: boolean = true;
const node: ESTree.IfStatement = NodeFactory.ifStatementNode(
NodeFactory.literalNode(true),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `IfStatement` node has single statement `consequent` and `alternate`', () => {
assert.equal(result, expectedResult);
});
});
});
describe('Variant #2: `ForStatement` node', () => {
const expectedResult: boolean = true;
const node: ESTree.ForStatement = NodeFactory.forStatementNode(
NodeFactory.variableDeclarationNode([
NodeFactory.variableDeclaratorNode(
NodeFactory.identifierNode('i'),
NodeFactory.literalNode(0)
)
]),
NodeFactory.binaryExpressionNode(
'<',
NodeFactory.identifierNode('i'),
NodeFactory.literalNode(10)
),
NodeFactory.updateExpressionNode(
'++',
NodeFactory.identifierNode('i')
),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `ForStatement` node has single statement `body`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #3: `ForInStatement` node', () => {
const expectedResult: boolean = true;
const node: ESTree.ForInStatement = NodeFactory.forInStatementNode(
NodeFactory.variableDeclarationNode([
NodeFactory.variableDeclaratorNode(
NodeFactory.identifierNode('key'),
null
)
]),
NodeFactory.objectExpressionNode(
[]
),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `ForInStatement` node has single statement `body`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #4: `ForOfStatement` node', () => {
const expectedResult: boolean = true;
const node: ESTree.ForOfStatement = NodeFactory.forOfStatementNode(
false,
NodeFactory.variableDeclarationNode([
NodeFactory.variableDeclaratorNode(
NodeFactory.identifierNode('key'),
null
)
]),
NodeFactory.objectExpressionNode(
[]
),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `ForOfStatement` node has single statement `body`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #5: `WhileStatement` node', () => {
const expectedResult: boolean = true;
const node: ESTree.WhileStatement = NodeFactory.whileStatementNode(
NodeFactory.literalNode(true),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `WhileStatement` node has single statement `body`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #6: `DoWhileStatement` node', () => {
const expectedResult: boolean = true;
const node: ESTree.DoWhileStatement = NodeFactory.doWhileStatementNode(
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
),
NodeFactory.literalNode(true)
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `DoWhileStatement` node has single statement `body`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #7: `LabeledStatement` node', () => {
const expectedResult: boolean = true;
const node: ESTree.LabeledStatement = NodeFactory.labeledStatementNode(
NodeFactory.identifierNode('label'),
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `LabeledStatement` node has single statement `body`', () => {
assert.equal(result, expectedResult);
});
});
});
describe('false checks', () => {
describe('Variant #1: `IfStatement` node', () => {
describe('Variant #1: multiple statements `consequent` and `alternate`', () => {
const expectedResult: boolean = false;
const node: ESTree.IfStatement = NodeFactory.ifStatementNode(
NodeFactory.literalNode(true),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
]),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
])
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `IfStatement` node has multiple statements `consequent` and `alternate`', () => {
assert.equal(result, expectedResult);
});
});
});
describe('Variant #2: `ForStatement` node', () => {
const expectedResult: boolean = false;
const node: ESTree.ForStatement = NodeFactory.forStatementNode(
NodeFactory.variableDeclarationNode([
NodeFactory.variableDeclaratorNode(
NodeFactory.identifierNode('i'),
NodeFactory.literalNode(0)
)
]),
NodeFactory.binaryExpressionNode(
'<',
NodeFactory.identifierNode('i'),
NodeFactory.literalNode(10)
),
NodeFactory.updateExpressionNode(
'++',
NodeFactory.identifierNode('i')
),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
])
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `ForStatement` node has multiple statements `body`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #3: `ForInStatement` node', () => {
const expectedResult: boolean = false;
const node: ESTree.ForInStatement = NodeFactory.forInStatementNode(
NodeFactory.variableDeclarationNode([
NodeFactory.variableDeclaratorNode(
NodeFactory.identifierNode('key'),
null
)
]),
NodeFactory.objectExpressionNode(
[]
),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
])
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `ForInStatement` node has multiple statements `body`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #4: `ForOfStatement` node', () => {
const expectedResult: boolean = false;
const node: ESTree.ForOfStatement = NodeFactory.forOfStatementNode(
false,
NodeFactory.variableDeclarationNode([
NodeFactory.variableDeclaratorNode(
NodeFactory.identifierNode('key'),
null
)
]),
NodeFactory.objectExpressionNode(
[]
),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
])
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `ForOfStatement` node has multiple statements `body`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #5: `WhileStatement` node', () => {
const expectedResult: boolean = false;
const node: ESTree.WhileStatement = NodeFactory.whileStatementNode(
NodeFactory.literalNode(true),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
])
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `WhileStatement` node has multiple statements `body`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #6: `DoWhileStatement` node', () => {
const expectedResult: boolean = false;
const node: ESTree.DoWhileStatement = NodeFactory.doWhileStatementNode(
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
]),
NodeFactory.literalNode(true)
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `DoWhileStatement` node has multiple statements `body`', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #7: `LabeledStatement` node', () => {
const expectedResult: boolean = false;
const node: ESTree.LabeledStatement = NodeFactory.labeledStatementNode(
NodeFactory.identifierNode('label'),
NodeFactory.blockStatementNode([
NodeFactory.expressionStatementNode(
NodeFactory.literalNode(true)
)
])
);
let result: boolean;
before(() => {
result = NodeGuards.isNodeWithSingleStatementBody(node);
});
it('should check if `LabeledStatement` node has multiple statements `body`', () => {
assert.equal(result, expectedResult);
});
});
});
});
describe('isNodeWithStatements', () => {
describe('truthful checks', () => {
describe('Variant #1: program node', () => {