mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Added numbersToExpressions option and NumberToNumericalExpressionTransformer
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Vendored
+7
-7
File diff suppressed because one or more lines are too long
Vendored
+1
-16233
File diff suppressed because one or more lines are too long
Vendored
+1
-15460
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "javascript-obfuscator",
|
||||
"version": "1.5.2",
|
||||
"version": "1.6.0",
|
||||
"description": "JavaScript obfuscator",
|
||||
"keywords": [
|
||||
"obfuscator",
|
||||
|
||||
@@ -75,6 +75,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
|
||||
NodeTransformer.MemberExpressionTransformer,
|
||||
NodeTransformer.MetadataTransformer,
|
||||
NodeTransformer.MethodDefinitionTransformer,
|
||||
NodeTransformer.NumberToNumericalExpressionTransformer,
|
||||
NodeTransformer.ObfuscatingGuardsTransformer,
|
||||
NodeTransformer.ObjectExpressionKeysTransformer,
|
||||
NodeTransformer.ObjectExpressionTransformer,
|
||||
|
||||
+35
-30
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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[];
|
||||
|
||||
+175
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -151,6 +151,12 @@ export class Options implements IOptions {
|
||||
@IsBoolean()
|
||||
public readonly log!: boolean;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
@IsBoolean()
|
||||
public readonly numbersToExpressions!: boolean;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
|
||||
identifiersDictionary: [],
|
||||
inputFileName: '',
|
||||
log: false,
|
||||
numbersToExpressions: false,
|
||||
renameGlobals: false,
|
||||
renameProperties: false,
|
||||
reservedNames: [],
|
||||
|
||||
@@ -20,6 +20,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
|
||||
identifiersDictionary: [],
|
||||
inputFileName: '',
|
||||
log: false,
|
||||
numbersToExpressions: false,
|
||||
renameGlobals: false,
|
||||
renameProperties: false,
|
||||
reservedNames: [],
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
|
||||
+167
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
const foo = {
|
||||
1: 'bar'
|
||||
};
|
||||
@@ -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';
|
||||
|
||||
+114
-33
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user