mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
refactoring + control flow storage test
This commit is contained in:
Vendored
+554
-525
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -20,7 +20,7 @@
|
||||
"javascript-obfuscator": "./bin/javascript-obfuscator.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "6.16.0",
|
||||
"babel-polyfill": "6.20.0",
|
||||
"chance": "1.0.4",
|
||||
"class-validator": "0.6.6",
|
||||
"commander": "2.9.0",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { nodeControlFlowTransformersModule } from './modules/node-transformers/N
|
||||
import { nodeObfuscatorsModule } from './modules/node-transformers/NodeObfuscatorsModule';
|
||||
import { nodeTransformersModule } from './modules/node-transformers/NodeTransformersModule';
|
||||
import { stackTraceAnalyzerModule } from './modules/stack-trace-analyzer/StackTraceAnalyzerModule';
|
||||
import { storagesModule } from './modules/storages/StoragesModule';
|
||||
|
||||
import { TInputOptions } from '../types/options/TInputOptions';
|
||||
|
||||
@@ -23,9 +24,6 @@ import { ObfuscationResult } from '../ObfuscationResult';
|
||||
import { Obfuscator } from '../Obfuscator';
|
||||
import { Options } from "../options/Options";
|
||||
import { SourceMapCorrector } from '../SourceMapCorrector';
|
||||
import { IStorage } from '../interfaces/storages/IStorage';
|
||||
import { ICustomNodeGroup } from '../interfaces/custom-nodes/ICustomNodeGroup';
|
||||
import { CustomNodeGroupStorage } from '../storages/custom-node-group/CustomNodeGroupStorage';
|
||||
|
||||
export class InversifyContainerFacade implements IInversifyContainerFacade {
|
||||
/**
|
||||
@@ -79,17 +77,13 @@ export class InversifyContainerFacade implements IInversifyContainerFacade {
|
||||
.to(SourceMapCorrector)
|
||||
.inSingletonScope();
|
||||
|
||||
this.container
|
||||
.bind<IStorage<ICustomNodeGroup>>(ServiceIdentifiers['IStorage<ICustomNodeGroup>'])
|
||||
.to(CustomNodeGroupStorage)
|
||||
.inSingletonScope();
|
||||
|
||||
this.container
|
||||
.bind<IObfuscationEventEmitter>(ServiceIdentifiers.IObfuscationEventEmitter)
|
||||
.to(ObfuscationEventEmitter)
|
||||
.inSingletonScope();
|
||||
|
||||
// modules
|
||||
this.container.load(storagesModule);
|
||||
this.container.load(stackTraceAnalyzerModule);
|
||||
this.container.load(customNodesModule);
|
||||
this.container.load(nodeTransformersModule);
|
||||
|
||||
@@ -6,6 +6,7 @@ export const ServiceIdentifiers: any = {
|
||||
'Factory<INodeTransformer[]>': Symbol('Factory<INodeTransformer[]>'),
|
||||
'Factory<IObfuscationResult>': Symbol('Factory<IObfuscationResult>'),
|
||||
'Factory<IReplacer>': Symbol('Factory<IReplacer>'),
|
||||
'Factory<IStorage<ICustomNode>>': Symbol('Factory<IStorage<ICustomNode>>'),
|
||||
ICalleeDataExtractor: Symbol('ICalleeDataExtractor'),
|
||||
ICustomNode: Symbol('ICustomNode'),
|
||||
ICustomNodeGroup: Symbol('ICustomNodeGroup'),
|
||||
@@ -19,5 +20,6 @@ export const ServiceIdentifiers: any = {
|
||||
IReplacer: Symbol('IReplacer'),
|
||||
ISourceMapCorrector: Symbol('ISourceMapCorrector'),
|
||||
IStackTraceAnalyzer: Symbol('IStackTraceAnalyzer'),
|
||||
'IStorage<ICustomNode>': Symbol('IStorage<ICustomNode>'),
|
||||
'IStorage<ICustomNodeGroup>': Symbol('IStorage<ICustomNodeGroup>')
|
||||
};
|
||||
|
||||
@@ -3,30 +3,30 @@ import { ServiceIdentifiers } from '../../ServiceIdentifiers';
|
||||
|
||||
import { IControlFlowReplacer } from '../../../interfaces/node-transformers/IControlFlowReplacer';
|
||||
|
||||
import { NodeControlFlowTransformersReplacers } from '../../../enums/container/NodeControlFlowTransformersReplacers';
|
||||
import { NodeControlFlowReplacers } from '../../../enums/container/NodeControlFlowReplacers';
|
||||
|
||||
import { BinaryExpressionControlFlowReplacer } from '../../../node-transformers/node-control-flow-transformers/control-flow-replacers/BinaryExpressionControlFlowReplacer';
|
||||
|
||||
export const nodeControlFlowTransformersModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
|
||||
bind<IControlFlowReplacer>(ServiceIdentifiers.IControlFlowReplacer)
|
||||
.to(BinaryExpressionControlFlowReplacer)
|
||||
.whenTargetNamed(NodeControlFlowTransformersReplacers.BinaryExpressionControlFlowReplacer);
|
||||
.whenTargetNamed(NodeControlFlowReplacers.BinaryExpressionControlFlowReplacer);
|
||||
|
||||
bind<IControlFlowReplacer>(ServiceIdentifiers['Factory<IControlFlowReplacer>'])
|
||||
.toFactory<IControlFlowReplacer>((context: interfaces.Context) => {
|
||||
const cache: Map <NodeControlFlowTransformersReplacers, IControlFlowReplacer> = new Map <NodeControlFlowTransformersReplacers, IControlFlowReplacer> ();
|
||||
const cache: Map <NodeControlFlowReplacers, IControlFlowReplacer> = new Map <NodeControlFlowReplacers, IControlFlowReplacer> ();
|
||||
|
||||
return (replacer: NodeControlFlowTransformersReplacers) => {
|
||||
if (cache.has(replacer)) {
|
||||
return <IControlFlowReplacer>cache.get(replacer);
|
||||
return (replacerName: NodeControlFlowReplacers) => {
|
||||
if (cache.has(replacerName)) {
|
||||
return <IControlFlowReplacer>cache.get(replacerName);
|
||||
}
|
||||
|
||||
const controlFlowReplacer: IControlFlowReplacer = context.container.getNamed<IControlFlowReplacer>(
|
||||
ServiceIdentifiers.IControlFlowReplacer,
|
||||
replacer
|
||||
replacerName
|
||||
);
|
||||
|
||||
cache.set(replacer, controlFlowReplacer);
|
||||
cache.set(replacerName, controlFlowReplacer);
|
||||
|
||||
return controlFlowReplacer;
|
||||
};
|
||||
|
||||
@@ -31,17 +31,17 @@ export const nodeObfuscatorsModule: interfaces.ContainerModule = new ContainerMo
|
||||
.toFactory<IReplacer>((context: interfaces.Context) => {
|
||||
const cache: Map <NodeObfuscatorsReplacers, IReplacer> = new Map <NodeObfuscatorsReplacers, IReplacer> ();
|
||||
|
||||
return (replacer: NodeObfuscatorsReplacers) => {
|
||||
if (cache.has(replacer)) {
|
||||
return <IReplacer>cache.get(replacer);
|
||||
return (replacerName: NodeObfuscatorsReplacers) => {
|
||||
if (cache.has(replacerName)) {
|
||||
return <IReplacer>cache.get(replacerName);
|
||||
}
|
||||
|
||||
const obfuscationReplacer: IReplacer = context.container.getNamed<IReplacer>(
|
||||
ServiceIdentifiers.IReplacer,
|
||||
replacer
|
||||
replacerName
|
||||
);
|
||||
|
||||
cache.set(replacer, obfuscationReplacer);
|
||||
cache.set(replacerName, obfuscationReplacer);
|
||||
|
||||
return obfuscationReplacer;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { ContainerModule, interfaces } from 'inversify';
|
||||
import { ServiceIdentifiers } from '../../ServiceIdentifiers';
|
||||
|
||||
import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
|
||||
import { ICustomNodeGroup } from '../../../interfaces/custom-nodes/ICustomNodeGroup';
|
||||
import { IStorage } from '../../../interfaces/storages/IStorage';
|
||||
|
||||
import { ControlFlowStorage } from '../../../storages/control-flow/ControlFlowStorage';
|
||||
import { CustomNodeGroupStorage } from '../../../storages/custom-node-group/CustomNodeGroupStorage';
|
||||
|
||||
export const storagesModule: interfaces.ContainerModule = new ContainerModule((bind: interfaces.Bind) => {
|
||||
// storages
|
||||
bind<IStorage<ICustomNodeGroup>>(ServiceIdentifiers['IStorage<ICustomNodeGroup>'])
|
||||
.to(CustomNodeGroupStorage)
|
||||
.inSingletonScope();
|
||||
|
||||
bind<IStorage<ICustomNode>>(ServiceIdentifiers['IStorage<ICustomNode>'])
|
||||
.to(ControlFlowStorage);
|
||||
|
||||
// controlFlowStorage factory
|
||||
bind<IStorage<ICustomNode>>(ServiceIdentifiers['Factory<IStorage<ICustomNode>>'])
|
||||
.toFactory<IStorage<ICustomNode>>((context: interfaces.Context) => {
|
||||
return () => {
|
||||
return context.container.get<IStorage<ICustomNode>>(ServiceIdentifiers['IStorage<ICustomNode>']);
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
export enum NodeControlFlowReplacers {
|
||||
BinaryExpressionControlFlowReplacer
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export enum NodeControlFlowTransformersReplacers {
|
||||
BinaryExpressionControlFlowReplacer
|
||||
}
|
||||
+41
-16
@@ -4,39 +4,65 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
|
||||
import * as estraverse from 'estraverse';
|
||||
import * as ESTree from 'estree';
|
||||
|
||||
import { TControlFlowReplacer } from '../../types/node-transformers/TControlFlowReplacer';
|
||||
import { TControlFlowReplacerFactory } from '../../types/container/TControlFlowReplacerFactory';
|
||||
import { TControlFlowStorageFactory } from '../../types/container/TControlFlowStorageFactory';
|
||||
import { TCustomNodeFactory } from '../../types/container/TCustomNodeFactory';
|
||||
import { TStatement } from '../../types/node/TStatement';
|
||||
|
||||
import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
|
||||
import { IOptions } from '../../interfaces/options/IOptions';
|
||||
import { IStorage } from '../../interfaces/storages/IStorage';
|
||||
|
||||
import { CustomNodes } from '../../enums/container/CustomNodes';
|
||||
import { NodeType } from '../../enums/NodeType';
|
||||
|
||||
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
|
||||
import { BinaryExpressionControlFlowReplacer } from './control-flow-replacers/BinaryExpressionControlFlowReplacer';
|
||||
import { ControlFlowStorage } from '../../storages/control-flow/ControlFlowStorage';
|
||||
import { ControlFlowStorageNode } from '../../custom-nodes/control-flow-storage-nodes/ControlFlowStorageNode';
|
||||
import { Node } from '../../node/Node';
|
||||
import { NodeAppender } from '../../node/NodeAppender';
|
||||
import { NodeControlFlowReplacers } from '../../enums/container/NodeControlFlowReplacers';
|
||||
import { RandomGeneratorUtils } from '../../utils/RandomGeneratorUtils';
|
||||
|
||||
@injectable()
|
||||
export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
|
||||
/**
|
||||
* @type {Map <string, IReplacer>}
|
||||
* @type {Map <string, NodeControlFlowReplacers>}
|
||||
*/
|
||||
private static readonly controlFlowReplacers: Map <string, TControlFlowReplacer> = new Map <string, TControlFlowReplacer> ([
|
||||
[NodeType.BinaryExpression, BinaryExpressionControlFlowReplacer]
|
||||
private static readonly controlFlowReplacersMap: Map <string, NodeControlFlowReplacers> = new Map <string, NodeControlFlowReplacers> ([
|
||||
[NodeType.BinaryExpression, NodeControlFlowReplacers.BinaryExpressionControlFlowReplacer]
|
||||
]);
|
||||
|
||||
/**
|
||||
* @type {TControlFlowReplacerFactory}
|
||||
*/
|
||||
private readonly controlFlowReplacerFactory: TControlFlowReplacerFactory;
|
||||
|
||||
/**
|
||||
* @type {TControlFlowStorageFactory}
|
||||
*/
|
||||
private readonly controlFlowStorageFactory: TControlFlowStorageFactory;
|
||||
|
||||
/**
|
||||
* @type {TCustomNodeFactory}
|
||||
*/
|
||||
private readonly customNodeFactory: TCustomNodeFactory;
|
||||
|
||||
/**
|
||||
* @param controlFlowStorageFactory
|
||||
* @param controlFlowReplacerFactory
|
||||
* @param customNodeFactory
|
||||
* @param options
|
||||
*/
|
||||
constructor (
|
||||
@inject(ServiceIdentifiers['Factory<IStorage<ICustomNode>>']) controlFlowStorageFactory: TControlFlowStorageFactory,
|
||||
@inject(ServiceIdentifiers['Factory<IControlFlowReplacer>']) controlFlowReplacerFactory: TControlFlowReplacerFactory,
|
||||
@inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
|
||||
@inject(ServiceIdentifiers.IOptions) options: IOptions
|
||||
) {
|
||||
super(options);
|
||||
|
||||
this.controlFlowStorageFactory = controlFlowStorageFactory;
|
||||
this.controlFlowReplacerFactory = controlFlowReplacerFactory;
|
||||
this.customNodeFactory = customNodeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,21 +80,20 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
|
||||
return;
|
||||
}
|
||||
|
||||
const controlFlowStorage: IStorage <ICustomNode> = new ControlFlowStorage();
|
||||
const controlFlowStorage: IStorage <ICustomNode> = this.controlFlowStorageFactory();
|
||||
const controlFlowStorageCustomNodeName: string = RandomGeneratorUtils.getRandomVariableName(6);
|
||||
|
||||
estraverse.replace(functionNode.body, {
|
||||
enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
|
||||
const controlFlowReplacer: TControlFlowReplacer | undefined = FunctionControlFlowTransformer
|
||||
.controlFlowReplacers.get(node.type);
|
||||
const controlFlowReplacerName: NodeControlFlowReplacers | undefined = FunctionControlFlowTransformer
|
||||
.controlFlowReplacersMap.get(node.type);
|
||||
|
||||
if (!controlFlowReplacer) {
|
||||
if (controlFlowReplacerName === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const controlFlowStorageCallCustomNode: ICustomNode | undefined = new controlFlowReplacer(
|
||||
this.options
|
||||
).replace(node, parentNode, controlFlowStorage, controlFlowStorageCustomNodeName);
|
||||
const controlFlowStorageCallCustomNode: ICustomNode | undefined = this.controlFlowReplacerFactory(controlFlowReplacerName)
|
||||
.replace(node, parentNode, controlFlowStorage, controlFlowStorageCustomNodeName);
|
||||
|
||||
if (!controlFlowStorageCallCustomNode) {
|
||||
return;
|
||||
@@ -78,7 +103,7 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
|
||||
// so we can get it by index `0`
|
||||
// also we need to return `expression` property of `ExpressionStatement` node because bug:
|
||||
// https://github.com/estools/escodegen/issues/289
|
||||
const statementNode: TStatement | undefined = controlFlowStorageCallCustomNode.getNode()[0];
|
||||
const statementNode: TStatement = controlFlowStorageCallCustomNode.getNode()[0];
|
||||
|
||||
if (!statementNode || !Node.isExpressionStatementNode(statementNode)) {
|
||||
throw new Error(`\`controlFlowStorageCallCustomNode.getNode()\` should returns array with \`ExpressionStatement\` node`);
|
||||
@@ -92,7 +117,7 @@ export class FunctionControlFlowTransformer extends AbstractNodeTransformer {
|
||||
return;
|
||||
}
|
||||
|
||||
const controlFlowStorageCustomNode: ICustomNode = new ControlFlowStorageNode(this.options);
|
||||
const controlFlowStorageCustomNode: ICustomNode = this.customNodeFactory(CustomNodes.ControlFlowStorageNode);
|
||||
|
||||
controlFlowStorageCustomNode.initialize(controlFlowStorage, controlFlowStorageCustomNodeName);
|
||||
|
||||
|
||||
+17
-9
@@ -4,23 +4,34 @@ import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
|
||||
import * as escodegen from 'escodegen';
|
||||
import * as ESTree from 'estree';
|
||||
|
||||
import { TCustomNodeFactory } from '../../../types/container/TCustomNodeFactory';
|
||||
|
||||
import { ICustomNode } from '../../../interfaces/custom-nodes/ICustomNode';
|
||||
import { IOptions } from '../../../interfaces/options/IOptions';
|
||||
import { IStorage } from '../../../interfaces/storages/IStorage';
|
||||
|
||||
import { CustomNodes } from '../../../enums/container/CustomNodes';
|
||||
|
||||
import { AbstractControlFlowReplacer } from './AbstractControlFlowReplacer';
|
||||
import { BinaryExpressionFunctionNode } from '../../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/BinaryExpressionFunctionNode';
|
||||
import { ControlFlowStorageCallNode } from '../../../custom-nodes/control-flow-replacers-nodes/binary-expression-control-flow-replacer-nodes/ControlFlowStorageCallNode';
|
||||
|
||||
@injectable()
|
||||
export class BinaryExpressionControlFlowReplacer extends AbstractControlFlowReplacer {
|
||||
/**
|
||||
* @type {TCustomNodeFactory}
|
||||
*/
|
||||
private readonly customNodeFactory: TCustomNodeFactory;
|
||||
|
||||
/**
|
||||
* @param customNodeFactory
|
||||
* @param options
|
||||
*/
|
||||
constructor (
|
||||
@inject(ServiceIdentifiers['Factory<ICustomNode>']) customNodeFactory: TCustomNodeFactory,
|
||||
@inject(ServiceIdentifiers.IOptions) options: IOptions
|
||||
) {
|
||||
super(options);
|
||||
|
||||
this.customNodeFactory = customNodeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,15 +58,10 @@ export class BinaryExpressionControlFlowReplacer extends AbstractControlFlowRepl
|
||||
controlFlowStorageCustomNodeName: string
|
||||
): ICustomNode {
|
||||
const key: string = AbstractControlFlowReplacer.getStorageKey();
|
||||
const binaryExpressionFunctionNode = new BinaryExpressionFunctionNode(this.options);
|
||||
const binaryExpressionFunctionNode: ICustomNode = this.customNodeFactory(CustomNodes.BinaryExpressionFunctionNode);
|
||||
const controlFlowStorageCallNode: ICustomNode = this.customNodeFactory(CustomNodes.ControlFlowStorageCallNode);
|
||||
|
||||
// TODO: pass through real stackTraceData
|
||||
binaryExpressionFunctionNode.initialize(binaryExpressionNode.operator);
|
||||
|
||||
controlFlowStorage.set(key, binaryExpressionFunctionNode);
|
||||
|
||||
const controlFlowStorageCallNode: ICustomNode = new ControlFlowStorageCallNode(this.options);
|
||||
|
||||
controlFlowStorageCallNode.initialize(
|
||||
controlFlowStorageCustomNodeName,
|
||||
key,
|
||||
@@ -63,6 +69,8 @@ export class BinaryExpressionControlFlowReplacer extends AbstractControlFlowRepl
|
||||
BinaryExpressionControlFlowReplacer.getExpressionValue(binaryExpressionNode.right)
|
||||
);
|
||||
|
||||
controlFlowStorage.set(key, binaryExpressionFunctionNode);
|
||||
|
||||
return controlFlowStorageCallNode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { IControlFlowReplacer } from '../../interfaces/node-transformers/IControlFlowReplacer';
|
||||
|
||||
import { NodeControlFlowReplacers } from '../../enums/container/NodeControlFlowReplacers';
|
||||
|
||||
export type TControlFlowReplacerFactory = (replacer: NodeControlFlowReplacers) => IControlFlowReplacer;
|
||||
@@ -0,0 +1,4 @@
|
||||
import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
|
||||
import { IStorage } from '../../interfaces/storages/IStorage';
|
||||
|
||||
export type TControlFlowStorageFactory = () => IStorage<ICustomNode>;
|
||||
@@ -1,4 +0,0 @@
|
||||
import { IControlFlowReplacer } from '../../interfaces/node-transformers/IControlFlowReplacer';
|
||||
import { IOptions } from '../../interfaces/options/IOptions';
|
||||
|
||||
export type TControlFlowReplacer = (new (options: IOptions) => IControlFlowReplacer);
|
||||
@@ -1,7 +1,8 @@
|
||||
/*
|
||||
import { ServiceIdentifiers } from '../../../src/container/ServiceIdentifiers';
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { TCustomNodeFactory } from '../../../src/types/container/TCustomNodeFactory';
|
||||
|
||||
import { ICustomNode } from '../../../src/interfaces/custom-nodes/ICustomNode';
|
||||
import { IInversifyContainerFacade } from '../../../src/interfaces/container/IInversifyContainerFacade';
|
||||
@@ -13,19 +14,29 @@ import { InversifyContainerFacade } from '../../../src/container/InversifyContai
|
||||
|
||||
describe('ControlFlowStorage', () => {
|
||||
describe('toString (): string', () => {
|
||||
it('should returns obfuscated code if `.toString()` was called on `ObfuscationResult` object', () => {
|
||||
it('should return correct ControlFlowStorage data after `.toString()` call', () => {
|
||||
const key1: string = 'key1';
|
||||
const key2: string = 'key2';
|
||||
const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade({
|
||||
controlFlowFlattening: true
|
||||
});
|
||||
const customNodeFactory: TCustomNodeFactory = inversifyContainerFacade
|
||||
.get<TCustomNodeFactory>(ServiceIdentifiers['Factory<ICustomNode>']);
|
||||
const controlFlowStorage: IStorage <ICustomNode> = inversifyContainerFacade
|
||||
.get<IStorage<ICustomNode>>(ServiceIdentifiers['IStorage<ICustomNode>']);
|
||||
const controlFlowStorageCallNode: ICustomNode = inversifyContainerFacade
|
||||
.getNamed<ICustomNode>(ServiceIdentifiers.ICustomNode, CustomNodes.ControlFlowStorageCallNode);
|
||||
const controlFlowStorageCallNode1: ICustomNode = customNodeFactory(CustomNodes.ControlFlowStorageCallNode);
|
||||
const controlFlowStorageCallNode2: ICustomNode = customNodeFactory(CustomNodes.ControlFlowStorageCallNode);
|
||||
|
||||
controlFlowStorage.set('key1', controlFlowStorageCallNode);
|
||||
controlFlowStorageCallNode1.initialize('controlFlowStorageName', key1, 1, 2);
|
||||
controlFlowStorageCallNode2.initialize('controlFlowStorageName', key2, 3, 4);
|
||||
|
||||
assert.equal(controlFlowStorage.toString(), [`key1: ${controlFlowStorageCallNode.getCode()}`]);
|
||||
controlFlowStorage.set(key1, controlFlowStorageCallNode1);
|
||||
controlFlowStorage.set(key2, controlFlowStorageCallNode2);
|
||||
|
||||
assert.equal(
|
||||
controlFlowStorage.toString(),
|
||||
`${key1}: ${controlFlowStorageCallNode1.getCode()},${key2}: ${controlFlowStorageCallNode2.getCode()}`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user