Added numbersToExpressions option and NumberToNumericalExpressionTransformer

This commit is contained in:
sanex3339
2020-07-12 18:24:57 +03:00
parent f07452e139
commit dd8dcac223
23 changed files with 1048 additions and 31777 deletions
+4
View File
@@ -1,5 +1,9 @@
Change Log
v1.6.0
---
* **New option:** `numbersToExpressions` enables numbers conversion to expressions
v1.5.2
---
* Prevented mutation of the name sequences of `mangled` identifier name generators
+19
View File
@@ -329,6 +329,7 @@ Following options are available for the JS Obfuscator:
identifiersPrefix: '',
inputFileName: '',
log: false,
numbersToExpressions: false,
renameGlobals: false,
renameProperties: false,
reservedNames: [],
@@ -375,6 +376,7 @@ Following options are available for the JS Obfuscator:
--identifiers-dictionary '<list>' (comma separated)
--identifiers-prefix <string>
--log <boolean>
--numbers-to-expressions <boolean>
--rename-globals <boolean>
--rename-properties <boolean>
--reserved-names '<list>' (comma separated)
@@ -667,6 +669,20 @@ Type: `boolean` Default: `false`
Enables logging of the information to the console.
### `numbersToExpressions`
Type: `boolean` Default: `false`
Enables numbers conversion to expressions
Example:
```ts
// input
const foo = 1234;
// output
const foo=-0xd93+-0x10b4+0x41*0x67+0x84e*0x3+-0xff8;
```
### `renameGlobals`
Type: `boolean` Default: `false`
@@ -985,6 +1001,7 @@ Performance will 50-100% slower than without obfuscation
disableConsoleOutput: true,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: true,
renameGlobals: false,
rotateStringArray: true,
selfDefending: true,
@@ -1016,6 +1033,7 @@ Performance will 30-35% slower than without obfuscation
disableConsoleOutput: true,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: false,
renameGlobals: false,
rotateStringArray: true,
selfDefending: true,
@@ -1045,6 +1063,7 @@ Performance will slightly slower than without obfuscation
disableConsoleOutput: true,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: false,
renameGlobals: false,
rotateStringArray: true,
selfDefending: true,
+7 -7
View File
File diff suppressed because one or more lines are too long
+1 -16233
View File
File diff suppressed because one or more lines are too long
+1 -15460
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": "1.5.2",
"version": "1.6.0",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
+1
View File
@@ -75,6 +75,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
NodeTransformer.MemberExpressionTransformer,
NodeTransformer.MetadataTransformer,
NodeTransformer.MethodDefinitionTransformer,
NodeTransformer.NumberToNumericalExpressionTransformer,
NodeTransformer.ObfuscatingGuardsTransformer,
NodeTransformer.ObjectExpressionKeysTransformer,
NodeTransformer.ObjectExpressionTransformer,
@@ -7,15 +7,17 @@ import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
import { NumberUtils } from '../../utils/NumberUtils';
/**
* Rework of https://gist.github.com/da411d/0e59f79dcf4603cdabf0024a10eeb6fe
* Based on https://gist.github.com/da411d/0e59f79dcf4603cdabf0024a10eeb6fe
*/
@injectable()
export class NumberNumericalExpressionAnalyzer implements INumberNumericalExpressionAnalyzer {
/**
* @type {number}
*/
private static readonly additionalParts: number = 5;
private static readonly additionalParts: number = 3;
/**
* @type {IRandomGenerator}
@@ -40,6 +42,10 @@ export class NumberNumericalExpressionAnalyzer implements INumberNumericalExpres
throw new Error('Given value is NaN');
}
if (NumberUtils.isUnsafeNumber(number)) {
return [number];
}
const additionParts: number[] = this.generateAdditionParts(number);
return additionParts.map((addition: number) => this.mixWithMultiplyParts(addition));
@@ -52,8 +58,10 @@ export class NumberNumericalExpressionAnalyzer implements INumberNumericalExpres
private generateAdditionParts (number: number): number[] {
const additionParts = [];
const from: number = Math.min(-10000, -Math.abs(number * 2));
const to: number = Math.max(10000, Math.abs(number * 2));
const upperNumberLimit: number = Math.min(Math.abs(number * 2), Number.MAX_SAFE_INTEGER);
const from: number = Math.min(-10000, -upperNumberLimit);
const to: number = Math.max(10000, upperNumberLimit);
let temporarySum = 0;
@@ -61,14 +69,27 @@ export class NumberNumericalExpressionAnalyzer implements INumberNumericalExpres
if (i < NumberNumericalExpressionAnalyzer.additionalParts - 1) {
// trailing parts
const addition: number = this.randomGenerator.getRandomInteger(from, to);
let addition: number = this.randomGenerator.getRandomInteger(from, to);
const isUnsafeCombination: boolean = NumberUtils.isUnsafeNumber(temporarySum + addition);
// we have to flip sign if total expression sum overflows over safe integer limits
if (isUnsafeCombination) {
addition = -addition;
}
additionParts.push(addition);
temporarySum += addition;
} else {
// last part
const combination: number = number - temporarySum;
const isUnsafeCombination: boolean = NumberUtils.isUnsafeNumber(combination);
additionParts.push(number - temporarySum);
// last part
if (isUnsafeCombination) {
additionParts.push(0 - temporarySum);
additionParts.push(number);
} else {
additionParts.push(combination);
}
}
}
@@ -80,36 +101,20 @@ export class NumberNumericalExpressionAnalyzer implements INumberNumericalExpres
* @returns {number | number[]}
*/
private mixWithMultiplyParts (number: number): number | number[] {
const dividers: number[] = this.getDividers(number);
const shouldMixWithMultiplyParts: boolean = this.randomGenerator.getMathRandom() > 0.5;
if (!shouldMixWithMultiplyParts || !dividers.length) {
if (!shouldMixWithMultiplyParts) {
return number;
}
const divider = dividers[
this.randomGenerator.getRandomInteger(0, dividers.length - 1)
];
const factors: number[] = NumberUtils.getFactors(number);
return [divider, number / divider];
}
/**
* @param {number} number
* @returns {number[]}
*/
private getDividers (number: number): number[] {
const dividers: number[] = [];
number = Math.abs(number);
for (let i = 2; i < number; i++) {
if (number % i === 0){
dividers.push(i);
}
if (!factors.length) {
return number;
}
return dividers;
const factor: number = factors[this.randomGenerator.getRandomInteger(0, factors.length - 1)];
return [factor, number / factor];
}
}
+4
View File
@@ -249,6 +249,10 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
'--log <boolean>', 'Enables logging of the information to the console',
BooleanSanitizer
)
.option(
'--numbers-to-expressions <boolean>', 'Enables numbers conversion to expressions',
BooleanSanitizer
)
.option(
'--reserved-names <list> (comma separated, without whitespaces)',
'Disables obfuscation and generation of identifiers, which being matched by passed RegExp patterns (comma separated)',
@@ -8,14 +8,15 @@ import { IObjectExpressionExtractor } from '../../../interfaces/node-transformer
import { NodeTransformer } from '../../../enums/node-transformers/NodeTransformer';
import { ObjectExpressionExtractor } from '../../../enums/node-transformers/converting-transformers/properties-extractors/ObjectExpressionExtractor';
import { BasePropertiesExtractor } from '../../../node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor';
import { ObjectExpressionToVariableDeclarationExtractor } from '../../../node-transformers/converting-transformers/object-expression-extractors/ObjectExpressionToVariableDeclarationExtractor';
import { MemberExpressionTransformer } from '../../../node-transformers/converting-transformers/MemberExpressionTransformer';
import { MethodDefinitionTransformer } from '../../../node-transformers/converting-transformers/MethodDefinitionTransformer';
import { NumberToNumericalExpressionTransformer } from '../../../node-transformers/converting-transformers/NumberToNumericalExpressionTransformer';
import { ObjectExpressionKeysTransformer } from '../../../node-transformers/converting-transformers/ObjectExpressionKeysTransformer';
import { ObjectExpressionTransformer } from '../../../node-transformers/converting-transformers/ObjectExpressionTransformer';
import { SplitStringTransformer } from '../../../node-transformers/converting-transformers/SplitStringTransformer';
import { TemplateLiteralTransformer } from '../../../node-transformers/converting-transformers/TemplateLiteralTransformer';
import { BasePropertiesExtractor } from '../../../node-transformers/converting-transformers/object-expression-extractors/BasePropertiesExtractor';
export const convertingTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
// converting transformers
@@ -27,6 +28,11 @@ export const convertingTransformersModule: interfaces.ContainerModule = new Cont
.to(MethodDefinitionTransformer)
.whenTargetNamed(NodeTransformer.MethodDefinitionTransformer);
bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
.to(NumberToNumericalExpressionTransformer)
.whenTargetNamed(NodeTransformer.NumberToNumericalExpressionTransformer);
bind<INodeTransformer>(ServiceIdentifiers.INodeTransformer)
.to(ObjectExpressionKeysTransformer)
.whenTargetNamed(NodeTransformer.ObjectExpressionKeysTransformer);
@@ -11,6 +11,7 @@ export enum NodeTransformer {
MemberExpressionTransformer = 'MemberExpressionTransformer',
MetadataTransformer = 'MetadataTransformer',
MethodDefinitionTransformer = 'MethodDefinitionTransformer',
NumberToNumericalExpressionTransformer = 'NumberToNumericalExpressionTransformer',
ObfuscatingGuardsTransformer = 'ObfuscatingGuardsTransformer',
ObjectExpressionKeysTransformer = 'ObjectExpressionKeysTransformer',
ObjectExpressionTransformer = 'ObjectExpressionTransformer',
+1
View File
@@ -21,6 +21,7 @@ export interface IOptions {
readonly identifiersPrefix: string;
readonly inputFileName: string;
readonly log: boolean;
readonly numbersToExpressions: boolean;
readonly renameGlobals: boolean;
readonly renameProperties: boolean;
readonly reservedNames: string[];
@@ -0,0 +1,175 @@
import { inject, injectable, } from 'inversify';
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
import * as ESTree from 'estree';
import { TNumberNumericalExpressionData } from '../../types/analyzers/number-numerical-expression-analyzer/TNumberNumericalExpressionData';
import { INumberNumericalExpressionAnalyzer } from '../../interfaces/analyzers/number-numerical-expression-analyzer/INumberNumericalExpressionAnalyzer';
import { IOptions } from '../../interfaces/options/IOptions';
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
import { NodeGuards } from '../../node/NodeGuards';
import { NodeFactory } from '../../node/NodeFactory';
import { NumberUtils } from '../../utils/NumberUtils';
/**
* replaces:
* var number = 123;
*
* on:
* var number = 50 + (100 * 2) - 127;
*/
@injectable()
export class NumberToNumericalExpressionTransformer extends AbstractNodeTransformer {
/**
* @type {INumberNumericalExpressionAnalyzer}
*/
private readonly numberNumericalExpressionAnalyzer: INumberNumericalExpressionAnalyzer;
/**
* @param {INumberNumericalExpressionAnalyzer} numberNumericalExpressionAnalyzer
* @param {IRandomGenerator} randomGenerator
* @param {IOptions} options
*/
public constructor (
@inject(ServiceIdentifiers.INumberNumericalExpressionAnalyzer)
numberNumericalExpressionAnalyzer: INumberNumericalExpressionAnalyzer,
@inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
@inject(ServiceIdentifiers.IOptions) options: IOptions
) {
super(randomGenerator, options);
this.numberNumericalExpressionAnalyzer = numberNumericalExpressionAnalyzer;
}
/**
* @param {NodeTransformationStage} nodeTransformationStage
* @returns {IVisitor | null}
*/
public getVisitor (nodeTransformationStage: NodeTransformationStage): IVisitor | null {
if (!this.options.numbersToExpressions) {
return null;
}
switch (nodeTransformationStage) {
case NodeTransformationStage.Converting:
return {
leave: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
if (parentNode && NodeGuards.isLiteralNode(node)) {
return this.transformNode(node, parentNode);
}
}
};
default:
return null;
}
}
/**
* @param {Literal} literalNode
* @param {NodeGuards} parentNode
* @returns {NodeGuards}
*/
public transformNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): ESTree.Node {
if (typeof literalNode.value !== 'number') {
return literalNode;
}
if (NodeGuards.isPropertyNode(parentNode) && !parentNode.computed) {
return literalNode;
}
const numberNumericalExpressionData: TNumberNumericalExpressionData = this.numberNumericalExpressionAnalyzer.analyze(literalNode.value);
return this.convertNumericalExpressionDataToNode(numberNumericalExpressionData);
}
/**
* @param {TNumberNumericalExpressionData} numberNumericalExpressionData
* @param {ESTree.BinaryOperator} operator
* @returns {ESTree.BinaryExpression | ESTree.Literal | ESTree.UnaryExpression}
*/
private convertNumericalExpressionDataToNode (
numberNumericalExpressionData: TNumberNumericalExpressionData,
operator: ESTree.BinaryOperator = '+'
): ESTree.BinaryExpression | ESTree.Literal | ESTree.UnaryExpression {
const numberNumericalExpressionDataLength: number = numberNumericalExpressionData.length;
const leftParts: TNumberNumericalExpressionData = numberNumericalExpressionDataLength > 1
? numberNumericalExpressionData.slice(0, numberNumericalExpressionDataLength - 1)
: [numberNumericalExpressionData[0]];
const rightParts: TNumberNumericalExpressionData = numberNumericalExpressionDataLength > 1
? numberNumericalExpressionData.slice(-1)
: [];
// trailing iterations
if (rightParts.length) {
return this.convertPartsToBinaryExpression(operator, leftParts, rightParts);
}
const firstLeftPartOrNumber: number | number[] | null = leftParts[0] ?? null;
// last iteration when only single left part is left
return Array.isArray(firstLeftPartOrNumber)
? this.convertNumericalExpressionDataToNode(firstLeftPartOrNumber, '*')
: this.convertPartOrNumberToLiteralNode(firstLeftPartOrNumber);
}
/**
* @param {ESTree.BinaryOperator} operator
* @param {TNumberNumericalExpressionData} leftParts
* @param {TNumberNumericalExpressionData} rightParts
* @returns {ESTree.BinaryExpression}
*/
private convertPartsToBinaryExpression (
operator: ESTree.BinaryOperator,
leftParts: TNumberNumericalExpressionData,
rightParts: TNumberNumericalExpressionData
): ESTree.BinaryExpression {
const rightPartOrNumber: number | number[] = rightParts[0];
if (Array.isArray(rightPartOrNumber)) {
// right part is array with multiply numbers
return NodeFactory.binaryExpressionNode(
operator,
this.convertNumericalExpressionDataToNode(leftParts),
this.convertNumericalExpressionDataToNode(rightPartOrNumber, '*')
);
} else {
// right part is number
return NodeFactory.binaryExpressionNode(
operator,
this.convertNumericalExpressionDataToNode(leftParts),
this.convertPartOrNumberToLiteralNode(rightPartOrNumber)
);
}
}
/**
* @param {number | number[]} partOrNumber
* @returns {ESTree.Literal}
*/
private convertPartOrNumberToLiteralNode (
partOrNumber: number | number[]
): ESTree.Literal | ESTree.UnaryExpression {
const number: number = Array.isArray(partOrNumber)
? partOrNumber[0]
: partOrNumber;
const isPositiveNumber: boolean = NumberUtils.isPositive(number);
const literalNode: ESTree.Literal = NodeFactory.literalNode(Math.abs(number));
return isPositiveNumber
? literalNode
: NodeFactory.unaryExpressionNode(
'-',
literalNode
);
}
}
+6
View File
@@ -151,6 +151,12 @@ export class Options implements IOptions {
@IsBoolean()
public readonly log!: boolean;
/**
* @type {boolean}
*/
@IsBoolean()
public readonly numbersToExpressions!: boolean;
/**
* @type {boolean}
*/
+1
View File
@@ -21,6 +21,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
identifiersDictionary: [],
inputFileName: '',
log: false,
numbersToExpressions: false,
renameGlobals: false,
renameProperties: false,
reservedNames: [],
+1
View File
@@ -20,6 +20,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
identifiersDictionary: [],
inputFileName: '',
log: false,
numbersToExpressions: false,
renameGlobals: false,
renameProperties: false,
reservedNames: [],
+81
View File
@@ -24,4 +24,85 @@ export class NumberUtils {
? number % 1 === 0
: true;
}
/**
* @param {number} number
* @returns {boolean}
*/
public static isPositive (number: number): boolean {
if (isNaN(number)) {
throw new Error('Given value is NaN');
}
if (number > 0) {
return true;
}
if (number < 0) {
return false;
}
if (1 / number === Number.POSITIVE_INFINITY) {
return true;
}
return false;
}
/**
* @param {number} number
* @returns {boolean}
*/
public static isUnsafeNumber (number: number): boolean {
if (isNaN(number)) {
throw new Error('Given value is NaN');
}
return number < Number.MIN_SAFE_INTEGER || number > Number.MAX_SAFE_INTEGER;
}
/**
* Returns all factors of a number
* Based on https://stackoverflow.com/a/43204663
*
* @param {number} number
* @returns {number[]}
*/
public static getFactors (number: number): number[] {
if (number === 0) {
throw new Error('Invalid number. Allowed only non-zero number');
}
number = Math.abs(number);
// special case for 1
if (number === 1) {
return [-number, number];
}
const factors: number[] = [];
const isEven: boolean = number % 2 === 0;
const incrementValue: number = isEven ? 1 : 2;
for (
let currentFactor = 1;
Math.pow(currentFactor, 2) <= number;
currentFactor += incrementValue
) {
if (number % currentFactor !== 0) {
continue;
}
factors.push(...[-currentFactor, currentFactor]);
const compliment: number = number / currentFactor;
if (compliment !== currentFactor) {
factors.push(...[-compliment, compliment]);
}
}
return factors.sort((a: number, b: number) => a - b);
}
}
+3 -12
View File
@@ -1,6 +1,5 @@
'use strict';
import { IdentifierNamesGenerator } from '../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
(function () {
@@ -8,21 +7,13 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`
const example1 = 0;
const example2 = 1;
const example3 = 100;
const example4 = 125793;
const example5 = -15232103;
const foo = 100;
console.log(example1);
console.log(example2);
console.log(example3);
console.log(example4);
console.log(example5);
console.log(foo);
`,
{
...NO_ADDITIONAL_NODES_PRESET,
identifierNamesGenerator: IdentifierNamesGenerator.MangledShuffledIdentifierNamesGenerator
numbersToExpressions: true
}
).getObfuscatedCode();
@@ -0,0 +1,167 @@
import { assert } from 'chai';
import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
import { readFileAsString } from '../../../../helpers/readFileAsString';
describe('NumbersToNumericalExpressionsTransformer', function () {
this.timeout(60000);
describe('Variant #1: base', () => {
const initialNumber: number = -50;
const lastNumber: number = 50;
let areValidExpressions: boolean = true;
before(() => {
for (let i = initialNumber; i < lastNumber; i++) {
const number: number = i;
const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`${number};`,
{
...NO_ADDITIONAL_NODES_PRESET,
numbersToExpressions: true
}
).getObfuscatedCode();
const result: number = eval(obfuscatedCode);
if (result !== number) {
areValidExpressions = false;
break;
}
}
});
it('should correctly transform numbers to expressions', () => {
assert.isTrue(areValidExpressions);
});
});
describe('Variant #2: safe integers', () => {
describe('Variant #1: max safe integer', () => {
const number: number = Number.MAX_SAFE_INTEGER;
const samplesCount: number = 15;
let areValidExpressions: boolean = true;
before(() => {
for (let i = 0; i < samplesCount; i++) {
const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`${number};`,
{
...NO_ADDITIONAL_NODES_PRESET,
numbersToExpressions: true
}
).getObfuscatedCode();
const result: number = eval(obfuscatedCode);
if (result !== number) {
areValidExpressions = false;
break;
}
}
});
it('should correctly transform numbers to expressions', () => {
assert.isTrue(areValidExpressions);
});
});
describe('Variant #2: max unsafe integer', () => {
const unsafeIntegerRegExp: RegExp = /0x20000000000000;/;
const number: number = Number.MAX_SAFE_INTEGER + 1;
let obfuscatedCode: string;
before(() => {
obfuscatedCode = JavaScriptObfuscator.obfuscate(
`${number};`,
{
...NO_ADDITIONAL_NODES_PRESET,
numbersToExpressions: true
}
).getObfuscatedCode();
});
it('should not transform unsafe integer to expressions', () => {
assert.match(obfuscatedCode, unsafeIntegerRegExp);
});
});
describe('Variant #3: min safe integer', () => {
const number: number = Number.MIN_SAFE_INTEGER;
const samplesCount: number = 15;
let areValidExpressions: boolean = true;
before(() => {
for (let i = 0; i < samplesCount; i++) {
const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
`${number};`,
{
...NO_ADDITIONAL_NODES_PRESET,
numbersToExpressions: true
}
).getObfuscatedCode();
const result: number = eval(obfuscatedCode);
if (result !== number) {
areValidExpressions = false;
break;
}
}
});
it('should correctly transform numbers to expressions', () => {
assert.isTrue(areValidExpressions);
});
});
describe('Variant #4: min unsafe integer', () => {
const unsafeIntegerRegExp: RegExp = /-0x20000000000000;/;
const number: number = Number.MIN_SAFE_INTEGER - 1;
let obfuscatedCode: string;
before(() => {
obfuscatedCode = JavaScriptObfuscator.obfuscate(
`${number};`,
{
...NO_ADDITIONAL_NODES_PRESET,
numbersToExpressions: true
}
).getObfuscatedCode();
});
it('should not transform unsafe integer to expressions', () => {
assert.match(obfuscatedCode, unsafeIntegerRegExp);
});
});
});
describe('Variant #3: parent node is non-computed object property', () => {
const regExp: RegExp = /const foo *= *{1: *'bar'};/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/non-computed-object-key.js');
obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
numbersToExpressions: true
}
).getObfuscatedCode();
});
it('should not replace non-computed object property literal with expression', () => {
assert.match(obfuscatedCode, regExp);
});
});
});
+1
View File
@@ -88,6 +88,7 @@ import './functional-tests/node-transformers/control-flow-transformers/control-f
import './functional-tests/node-transformers/control-flow-transformers/control-flow-replacers/string-litertal-control-flow-replacer/StringLiteralControlFlowReplacer.spec';
import './functional-tests/node-transformers/converting-transformers/member-expression-transformer/MemberExpressionTransformer.spec';
import './functional-tests/node-transformers/converting-transformers/method-definition-transformer/MethodDefinitionTransformer.spec';
import './functional-tests/node-transformers/converting-transformers/numbers-to-numerical-expressions-transformer/NumbersToNumericalExpressionsTransformer.spec';
import './functional-tests/node-transformers/converting-transformers/object-expression-keys-transformer/ObjectExpressionKeysTransformer.spec';
import './functional-tests/node-transformers/converting-transformers/object-expression-transformer/ObjectExpressionTransformer.spec';
import './functional-tests/node-transformers/converting-transformers/split-string-transformer/SplitStringTransformer.spec';
@@ -21,9 +21,11 @@ const numberNumericalExpressionDataToString = (data: TNumberNumericalExpressionD
.join('+')
.replace(/\+-/g, '-');
describe('NumberNumericalExpressionAnalyzer', () => {
describe('NumberNumericalExpressionAnalyzer', function() {
let numberNumericalExpressionAnalyzer: INumberNumericalExpressionAnalyzer;
this.timeout(10000);
before(() => {
const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();
@@ -35,52 +37,131 @@ describe('NumberNumericalExpressionAnalyzer', () => {
describe('analyze', () => {
let evaluatedResult: number;
describe('Variant #1: positive number', () => {
const number: number = 1234;
describe('Positive numbers', () => {
describe('Variant #1: positive number', () => {
const number: number = 1234;
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
});
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
describe('Variant #2: positive zero number', () => {
const number: number = 0;
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
});
});
describe('Variant #3: positive big number', () => {
const number: number = Number.MAX_SAFE_INTEGER;
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
});
});
describe('Variant #4: positive unsafe big number', () => {
const number: number = Number.MAX_SAFE_INTEGER + 1;
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
});
});
});
describe('Variant #2: negative number', () => {
const number: number = -1234;
describe('Negative numbers', () => {
describe('Variant #1: negative number', () => {
const number: number = -1234;
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
});
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
describe('Variant #2: negative zero number', () => {
const number: number = -0;
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
});
});
describe('Variant #3: negative big number', () => {
const number: number = Number.MIN_SAFE_INTEGER;
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
});
});
describe('Variant #4: negative unsafe number', () => {
const number: number = Number.MIN_SAFE_INTEGER - 1;
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
});
});
});
describe('Variant #3: zero number', () => {
const number: number = 0;
before(() => {
const numberNumericalExpressionData: TNumberNumericalExpressionData =
numberNumericalExpressionAnalyzer.analyze(number);
evaluatedResult = eval(numberNumericalExpressionDataToString(numberNumericalExpressionData));
});
it('should return correct number numerical expression data', () => {
assert.equal(number, evaluatedResult);
});
});
describe('Variant #4: NaN', () => {
describe('NaN', () => {
const number: number = NaN;
let testFunc: () => void;
+414
View File
@@ -128,4 +128,418 @@ describe('NumberUtils', () => {
});
});
});
describe('isPositive', () => {
describe('Variant #1: positive integer', () => {
const number: number = 2;
const expectedResult: boolean = true;
let result: boolean;
before(() => {
result = NumberUtils.isPositive(number);
});
it('should return true', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #2: positive zero', () => {
const number: number = 0;
const expectedResult: boolean = true;
let result: boolean;
before(() => {
result = NumberUtils.isPositive(number);
});
it('should return true', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #3: negative integer', () => {
const number: number = -2;
const expectedResult: boolean = false;
let result: boolean;
before(() => {
result = NumberUtils.isPositive(number);
});
it('should return false', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #4: negative zero', () => {
const number: number = -0;
const expectedResult: boolean = false;
let result: boolean;
before(() => {
result = NumberUtils.isPositive(number);
});
it('should return false', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #5: NaN', () => {
const number: number = NaN;
let testFunc: () => void;
before(() => {
testFunc = () => NumberUtils.isPositive(number);
});
it('should throw an error', () => {
assert.throw(testFunc, 'Given value is NaN');
});
});
});
describe('isUnsafeNumber', () => {
describe('Positive number', () => {
describe('Variant #1: positive small safe integer', () => {
const number: number = 100;
const expectedResult: boolean = false
let result: boolean;
before(() => {
result = NumberUtils.isUnsafeNumber(number);
});
it('should return false', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #2: positive big safe integer', () => {
const number: number = Number.MAX_SAFE_INTEGER;
const expectedResult: boolean = false
let result: boolean;
before(() => {
result = NumberUtils.isUnsafeNumber(number);
});
it('should return false', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #3: positive unsafe integer', () => {
const number: number = Number.MAX_SAFE_INTEGER + 1;
const expectedResult: boolean = true
let result: boolean;
before(() => {
result = NumberUtils.isUnsafeNumber(number);
});
it('should return true', () => {
assert.equal(result, expectedResult);
});
});
});
describe('Negative number', () => {
describe('Variant #1: negative small safe integer', () => {
const number: number = -100;
const expectedResult: boolean = false
let result: boolean;
before(() => {
result = NumberUtils.isUnsafeNumber(number);
});
it('should return false', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #2: negative big safe integer', () => {
const number: number = Number.MIN_SAFE_INTEGER;
const expectedResult: boolean = false
let result: boolean;
before(() => {
result = NumberUtils.isUnsafeNumber(number);
});
it('should return false', () => {
assert.equal(result, expectedResult);
});
});
describe('Variant #3: negative unsafe integer', () => {
const number: number = Number.MIN_SAFE_INTEGER - 1;
const expectedResult: boolean = true
let result: boolean;
before(() => {
result = NumberUtils.isUnsafeNumber(number);
});
it('should return true', () => {
assert.equal(result, expectedResult);
});
});
});
describe('NaN', () => {
const number: number = NaN;
let testFunc: () => void;
before(() => {
testFunc = () => NumberUtils.isUnsafeNumber(number);
});
it('should throw an error', () => {
assert.throw(testFunc, 'Given value is NaN');
});
});
});
describe('getFactors', () => {
describe('Positive numbers', () => {
describe('Variant #1: positive number `1`', () => {
const number: number = 1;
const expectedFactors: number[] = [
-1,
1
];
let factors: number[];
before(() => {
factors = NumberUtils.getFactors(number);
});
it('should return factors of a number', () => {
assert.deepEqual(factors, expectedFactors);
});
});
describe('Variant #2: positive number `2`', () => {
const number: number = 2;
const expectedFactors: number[] = [
-2,
-1,
1,
2
];
let factors: number[];
before(() => {
factors = NumberUtils.getFactors(number);
});
it('should return factors of a number', () => {
assert.deepEqual(factors, expectedFactors);
});
});
describe('Variant #3: positive number `100`', () => {
const number: number = 100;
const expectedFactors: number[] = [
-100,
-50,
-25,
-20,
-10,
-5,
-4,
-2,
-1,
1,
2,
4,
5,
10,
20,
25,
50,
100
];
let factors: number[];
before(() => {
factors = NumberUtils.getFactors(number);
});
it('should return factors of a number', () => {
assert.deepEqual(factors, expectedFactors);
});
});
describe('Variant #4: positive number `9007199254740991`', () => {
const number: number = 9007199254740991;
const expectedFactors: number[] = [
Number.MIN_SAFE_INTEGER,
-1416003655831,
-129728784761,
-441650591,
-20394401,
-69431,
-6361,
-1,
1,
6361,
69431,
20394401,
441650591,
129728784761,
1416003655831,
Number.MAX_SAFE_INTEGER
];
let factors: number[];
before(() => {
factors = NumberUtils.getFactors(number);
});
it('should return factors of a number', () => {
assert.deepEqual(factors, expectedFactors);
});
});
})
describe('Negative numbers', () => {
describe('Variant #1: negative number `-1`', () => {
const number: number = -1;
const expectedFactors: number[] = [
-1,
1
];
let factors: number[];
before(() => {
factors = NumberUtils.getFactors(number);
});
it('should return factors of a number', () => {
assert.deepEqual(factors, expectedFactors);
});
});
describe('Variant #2: negative number `-2`', () => {
const number: number = -2;
const expectedFactors: number[] = [
-2,
-1,
1,
2
];
let factors: number[];
before(() => {
factors = NumberUtils.getFactors(number);
});
it('should return factors of a number', () => {
assert.deepEqual(factors, expectedFactors);
});
});
describe('Variant #3: negative number `-100`', () => {
const number: number = -100;
const expectedFactors: number[] = [
-100,
-50,
-25,
-20,
-10,
-5,
-4,
-2,
-1,
1,
2,
4,
5,
10,
20,
25,
50,
100
];
let factors: number[];
before(() => {
factors = NumberUtils.getFactors(number);
});
it('should return factors of a number', () => {
assert.deepEqual(factors, expectedFactors);
});
});
describe('Variant #4: negative number `-9007199254740991`', () => {
const number: number = -9007199254740991;
const expectedFactors: number[] = [
Number.MIN_SAFE_INTEGER,
-1416003655831,
-129728784761,
-441650591,
-20394401,
-69431,
-6361,
-1,
1,
6361,
69431,
20394401,
441650591,
129728784761,
1416003655831,
Number.MAX_SAFE_INTEGER
];
let factors: number[];
before(() => {
factors = NumberUtils.getFactors(number);
});
it('should return factors of a number', () => {
assert.deepEqual(factors, expectedFactors);
});
});
})
describe('zero number', () => {
const number: number = 0;
let testFunc: () => void;
before(() => {
testFunc = () => NumberUtils.getFactors(number);
});
it('should throw an error', () => {
assert.throw(testFunc, Error);
});
})
});
});