Jelajahi Sumber

Merge branch '0.17.3'

# Conflicts:
#	dist/index.browser.js
#	dist/index.cli.js
#	dist/index.js
#	package.json
sanex3339 6 tahun lalu
induk
melakukan
e8188c364c
16 mengubah file dengan 304 tambahan dan 71 penghapusan
  1. 5 0
      CHANGELOG.md
  2. 0 0
      dist/index.browser.js
  3. 0 0
      dist/index.cli.js
  4. 0 0
      dist/index.js
  5. 3 3
      index.d.ts
  6. 13 18
      src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts
  7. 118 10
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/BlockStatementControlFlowTransformer.spec.ts
  8. 11 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/break-statement-3.js
  9. 0 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/break-statement-inside-while-statement-1.js
  10. 11 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/break-statement-inside-while-statement-2.js
  11. 11 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/continue-statement-3.js
  12. 0 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/continue-statement-inside-while-statement-1.js
  13. 11 0
      test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/continue-statement-inside-while-statement-2.js
  14. 94 40
      test/functional-tests/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.spec.ts
  15. 0 0
      test/functional-tests/node-transformers/dead-code-injection-transformers/fixtures/break-continue-statement-1.js
  16. 27 0
      test/functional-tests/node-transformers/dead-code-injection-transformers/fixtures/break-continue-statement-2.js

+ 5 - 0
CHANGELOG.md

@@ -4,6 +4,11 @@ v0.18.0
 ---
 * **New option:** `reservedStrings` disables transformation of string literals, which being matched by passed RegExp patterns
 
+v0.17.3
+---
+* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/303
+* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/302
+
 v0.17.2
 ---
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/297

File diff ditekan karena terlalu besar
+ 0 - 0
dist/index.browser.js


File diff ditekan karena terlalu besar
+ 0 - 0
dist/index.cli.js


File diff ditekan karena terlalu besar
+ 0 - 0
dist/index.js


+ 3 - 3
index.d.ts

@@ -1,9 +1,9 @@
 import { TInputOptions } from './src/types/options/TInputOptions';
 
-import { IObfuscationResult } from './src/interfaces/IObfuscationResult';
+import { IObfuscatedCode } from './src/interfaces/source-code/IObfuscatedCode';
 
 export type ObfuscatorOptions = TInputOptions;
 
-export interface ObfuscationResult extends IObfuscationResult {}
+export interface ObfuscatedCode extends IObfuscatedCode {}
 
-export function obfuscate (sourceCode: string, inputOptions?: ObfuscatorOptions): ObfuscationResult;
+export function obfuscate (sourceCode: string, inputOptions?: ObfuscatorOptions): ObfuscatedCode;

+ 13 - 18
src/node-transformers/control-flow-transformers/BlockStatementControlFlowTransformer.ts

@@ -52,22 +52,20 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme
     }
 
     /**
-     * @param {BlockStatement} blockStatementNode
+     * @param {Node} node
      * @returns {boolean}
      */
-    private static blockStatementHasProhibitedStatements (blockStatementNode: ESTree.BlockStatement): boolean {
-        return blockStatementNode.body.some((statement: ESTree.Statement) => {
-            const isBreakOrContinueStatement: boolean = NodeGuards.isBreakStatementNode(statement)
-                || NodeGuards.isContinueStatementNode(statement);
-            const isVariableDeclarationWithLetOrConstKind: boolean = NodeGuards.isVariableDeclarationNode(statement)
-                && (statement.kind === 'const' || statement.kind === 'let');
-            const isClassDeclaration: boolean = NodeGuards.isClassDeclarationNode(statement);
-
-            return NodeGuards.isFunctionDeclarationNode(statement)
-                || isBreakOrContinueStatement
-                || isVariableDeclarationWithLetOrConstKind
-                || isClassDeclaration;
-        });
+    private static isProhibitedStatementNode (node: ESTree.Node): boolean {
+        const isBreakOrContinueStatement: boolean = NodeGuards.isBreakStatementNode(node)
+            || NodeGuards.isContinueStatementNode(node);
+        const isVariableDeclarationWithLetOrConstKind: boolean = NodeGuards.isVariableDeclarationNode(node)
+            && (node.kind === 'const' || node.kind === 'let');
+        const isClassDeclaration: boolean = NodeGuards.isClassDeclarationNode(node);
+
+        return NodeGuards.isFunctionDeclarationNode(node)
+            || isBreakOrContinueStatement
+            || isVariableDeclarationWithLetOrConstKind
+            || isClassDeclaration;
     }
 
     /**
@@ -83,10 +81,7 @@ export class BlockStatementControlFlowTransformer extends AbstractNodeTransforme
                     return estraverse.VisitorOption.Skip;
                 }
 
-                if (
-                    NodeGuards.isBlockStatementNode(node)
-                    && BlockStatementControlFlowTransformer.blockStatementHasProhibitedStatements(node)
-                ) {
+                if (BlockStatementControlFlowTransformer.isProhibitedStatementNode(node)) {
                     canTransform = false;
                 }
             }

+ 118 - 10
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/BlockStatementControlFlowTransformer.spec.ts

@@ -309,7 +309,61 @@ describe('BlockStatementControlFlowTransformer', function () {
             });
         });
 
-        describe('Variant #8: block statement contain while statement with break statement', () => {
+        describe('Variant #8: block statement contain break statement #3', () => {
+            const statementRegExp: RegExp = /^\(function *\( *\) *\{ *while *\(!!\[\]\) *\{ *if *\(!!\[\]\) *break; *console\['log'\]\(0x1\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/break-statement-3.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        controlFlowFlattening: true,
+                        controlFlowFlatteningThreshold: 1
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('shouldn\'t transform block statement', () => {
+                assert.match(obfuscatedCode, statementRegExp);
+            });
+        });
+
+        describe('Variant #9: block statement contain while statement with break statement', () => {
+            const switchCaseRegExp: RegExp = /switch *\(_0x([a-f0-9]){4,6}\[_0x([a-f0-9]){4,6}\+\+\]\) *\{/;
+            const switchCaseLengthRegExp: RegExp = /case *'[0-5]': *console\['log'\]\(0x[0-6]\);/g;
+            const expectedSwitchCaseLength: number = 5;
+
+            let obfuscatedCode: string,
+                switchCaseLength: number;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/break-statement-inside-while-statement-1.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        controlFlowFlattening: true,
+                        controlFlowFlatteningThreshold: 1
+                    }
+                ).getObfuscatedCode();
+                switchCaseLength = obfuscatedCode.match(switchCaseLengthRegExp)!.length;
+            });
+
+            it('should wrap block statement statements in switch-case structure', () => {
+                assert.match(obfuscatedCode, switchCaseRegExp);
+            });
+
+            it('each statement should be wrapped by switch-case structure', () => {
+                assert.equal(switchCaseLength, expectedSwitchCaseLength);
+            });
+        });
+
+        describe('Variant #10: block statement contain while statement with break statement', () => {
             const switchCaseRegExp: RegExp = /switch *\(_0x([a-f0-9]){4,6}\[_0x([a-f0-9]){4,6}\+\+\]\) *\{/;
             const switchCaseLengthRegExp: RegExp = /case *'[0-5]': *console\['log'\]\(0x[0-6]\);/g;
             const expectedSwitchCaseLength: number = 5;
@@ -318,7 +372,7 @@ describe('BlockStatementControlFlowTransformer', function () {
                 switchCaseLength: number;
 
             before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/break-statement-inside-while-statement.js');
+                const code: string = readFileAsString(__dirname + '/fixtures/break-statement-inside-while-statement-2.js');
 
                 obfuscatedCode = JavaScriptObfuscator.obfuscate(
                     code,
@@ -340,7 +394,7 @@ describe('BlockStatementControlFlowTransformer', function () {
             });
         });
 
-        describe('Variant #9: block statement contain continue statement #1', () => {
+        describe('Variant #11: block statement contain continue statement #1', () => {
             const statementRegExp: RegExp = /^\(function *\( *\) *\{ *while *\(!!\[\]\) *\{ *continue; *console\['log'\]\(0x1\);/;
 
             let obfuscatedCode: string;
@@ -363,7 +417,7 @@ describe('BlockStatementControlFlowTransformer', function () {
             });
         });
 
-        describe('Variant #10: block statement contain continue statement #2', () => {
+        describe('Variant #12: block statement contain continue statement #2', () => {
             const statementRegExp: RegExp = /^\(function *\( *\) *\{ *while *\(!!\[\]\) *\{ *if *\(!!\[\]\) *\{ *continue; *\} *console\['log'\]\(0x1\);/;
 
             let obfuscatedCode: string;
@@ -386,7 +440,61 @@ describe('BlockStatementControlFlowTransformer', function () {
             });
         });
 
-        describe('Variant #11: block statement contain while statement with continue statement', () => {
+        describe('Variant #13: block statement contain continue statement #3', () => {
+            const statementRegExp: RegExp = /^\(function *\( *\) *\{ *while *\(!!\[\]\) *\{ *if *\(!!\[\]\) *continue; *console\['log'\]\(0x1\);/;
+
+            let obfuscatedCode: string;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/continue-statement-3.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        controlFlowFlattening: true,
+                        controlFlowFlatteningThreshold: 1
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('shouldn\'t transform block statement', () => {
+                assert.match(obfuscatedCode, statementRegExp);
+            });
+        });
+
+        describe('Variant #14: block statement contain while statement with continue statement', () => {
+            const switchCaseRegExp: RegExp = /switch *\(_0x([a-f0-9]){4,6}\[_0x([a-f0-9]){4,6}\+\+\]\) *\{/;
+            const switchCaseLengthRegExp: RegExp = /case *'[0-5]': *console\['log'\]\(0x[0-6]\);/g;
+            const expectedSwitchCaseLength: number = 5;
+
+            let obfuscatedCode: string,
+                switchCaseLength: number;
+
+            before(() => {
+                const code: string = readFileAsString(__dirname + '/fixtures/continue-statement-inside-while-statement-1.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        controlFlowFlattening: true,
+                        controlFlowFlatteningThreshold: 1
+                    }
+                ).getObfuscatedCode();
+                switchCaseLength = obfuscatedCode.match(switchCaseLengthRegExp)!.length;
+            });
+
+            it('should wrap block statement statements in switch-case structure', () => {
+                assert.match(obfuscatedCode, switchCaseRegExp);
+            });
+
+            it('each statement should be wrapped by switch-case structure', () => {
+                assert.equal(switchCaseLength, expectedSwitchCaseLength);
+            });
+        });
+
+        describe('Variant #15: block statement contain continue statement #4', () => {
             const switchCaseRegExp: RegExp = /switch *\(_0x([a-f0-9]){4,6}\[_0x([a-f0-9]){4,6}\+\+\]\) *\{/;
             const switchCaseLengthRegExp: RegExp = /case *'[0-5]': *console\['log'\]\(0x[0-6]\);/g;
             const expectedSwitchCaseLength: number = 5;
@@ -395,7 +503,7 @@ describe('BlockStatementControlFlowTransformer', function () {
                 switchCaseLength: number;
 
             before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/continue-statement-inside-while-statement.js');
+                const code: string = readFileAsString(__dirname + '/fixtures/continue-statement-inside-while-statement-2.js');
 
                 obfuscatedCode = JavaScriptObfuscator.obfuscate(
                     code,
@@ -417,7 +525,7 @@ describe('BlockStatementControlFlowTransformer', function () {
             });
         });
 
-        describe('Variant #12: block statement contain function declaration', () => {
+        describe('Variant #16: block statement contain function declaration', () => {
             const statementRegExp: RegExp = /^\(function *\( *\) *\{ *function *_0x([a-f0-9]){4,6} *\( *\) *\{ *\} *console\['log'\]\(0x1\);/
 
             let obfuscatedCode: string;
@@ -440,7 +548,7 @@ describe('BlockStatementControlFlowTransformer', function () {
             });
         });
 
-        describe('Variant #13: block statement contain class declaration', () => {
+        describe('Variant #17: block statement contain class declaration', () => {
             const statementRegExp: RegExp = /^\(function *\( *\) *{ * *class *_0x([a-f0-9]){4,6} *{.*?} *}.*class *_0x([a-f0-9]){4,6} *{.*?} *}.*class *_0x([a-f0-9]){4,6} *{.*?} *}/;
 
             let obfuscatedCode: string;
@@ -463,7 +571,7 @@ describe('BlockStatementControlFlowTransformer', function () {
             });
         });
 
-        describe('Variant #14: `controlFlowFlatteningThreshold` chance', () => {
+        describe('Variant #18: `controlFlowFlatteningThreshold` chance', () => {
             const samples: number = 1000;
             const delta: number = 0.1;
 
@@ -507,7 +615,7 @@ describe('BlockStatementControlFlowTransformer', function () {
             });
         });
 
-        describe('Variant #15: No `unreachable code after return statement` warning', () => {
+        describe('Variant #19: No `unreachable code after return statement` warning', () => {
             const switchCaseRegExp: RegExp = /switch *\(_0x([a-f0-9]){4,6}\[_0x([a-f0-9]){4,6}\+\+\]\) *\{/;
             const switchCaseLengthRegExp: RegExp = /case *'[0-5]': *console\['log'\]\(0x[0-6]\);/g;
             const returnStatementRegExp: RegExp = /case *'[0-5]': *return; *(case|})/;

+ 11 - 0
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/break-statement-3.js

@@ -0,0 +1,11 @@
+(function () {
+    while (true) {
+        if (true)
+            break;
+        console.log(1);
+        console.log(2);
+        console.log(3);
+        console.log(4);
+        console.log(5);
+    }
+})();

+ 0 - 0
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/break-statement-inside-while-statement.js → test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/break-statement-inside-while-statement-1.js


+ 11 - 0
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/break-statement-inside-while-statement-2.js

@@ -0,0 +1,11 @@
+(function () {
+    while (true) {
+        while (true)
+            break;
+        console.log(1);
+        console.log(2);
+        console.log(3);
+        console.log(4);
+        console.log(5);
+    }
+})();

+ 11 - 0
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/continue-statement-3.js

@@ -0,0 +1,11 @@
+(function () {
+    while (true) {
+        if (true)
+            continue;
+        console.log(1);
+        console.log(2);
+        console.log(3);
+        console.log(4);
+        console.log(5);
+    }
+})();

+ 0 - 0
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/continue-statement-inside-while-statement.js → test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/continue-statement-inside-while-statement-1.js


+ 11 - 0
test/functional-tests/node-transformers/control-flow-transformers/block-statement-control-flow-transformer/fixtures/continue-statement-inside-while-statement-2.js

@@ -0,0 +1,11 @@
+(function () {
+    while (true) {
+        while (true)
+            continue;
+        console.log(1);
+        console.log(2);
+        console.log(3);
+        console.log(4);
+        console.log(5);
+    }
+})();

+ 94 - 40
test/functional-tests/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.spec.ts

@@ -127,55 +127,109 @@ describe('DeadCodeInjectionTransformer', () => {
         });
 
         describe('Variant #4 - break or continue statement in block statement', () => {
-            const functionRegExp: RegExp = new RegExp(
-                `var *${variableMatch} *= *function *\\(\\) *\\{` +
-                    `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
-                `\\};`,
-                'g'
-            );
-            const loopRegExp: RegExp = new RegExp(
-                `for *\\(var *${variableMatch} *= *${hexMatch}; *${variableMatch} *< *${hexMatch}; *${variableMatch}\\+\\+\\) *\\{` +
-                    `(?:continue|break);` +
-                `\\}`,
-                'g'
-            );
-            const expectedFunctionMatchesLength: number = 4;
-            const expectedLoopMatchesLength: number = 2;
+            describe('Variant #1', () => {
+                const functionRegExp: RegExp = new RegExp(
+                    `var *${variableMatch} *= *function *\\(\\) *\\{` +
+                        `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                    `\\};`,
+                    'g'
+                );
+                const loopRegExp: RegExp = new RegExp(
+                    `for *\\(var *${variableMatch} *= *${hexMatch}; *${variableMatch} *< *${hexMatch}; *${variableMatch}\\+\\+\\) *\\{` +
+                        `(?:continue|break);` +
+                    `\\}`,
+                    'g'
+                );
+                const expectedFunctionMatchesLength: number = 4;
+                const expectedLoopMatchesLength: number = 2;
 
-            let functionMatchesLength: number = 0,
-                loopMatchesLength: number = 0;
+                let functionMatchesLength: number = 0,
+                    loopMatchesLength: number = 0;
 
-            before(() => {
-                const code: string = readFileAsString(__dirname + '/fixtures/break-continue-statement.js');
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/break-continue-statement-1.js');
 
-                const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
-                    code,
-                    {
-                        ...NO_ADDITIONAL_NODES_PRESET,
-                        deadCodeInjection: true,
-                        deadCodeInjectionThreshold: 1,
-                        stringArray: true,
-                        stringArrayThreshold: 1
+                    const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            deadCodeInjection: true,
+                            deadCodeInjectionThreshold: 1,
+                            stringArray: true,
+                            stringArrayThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                    const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionRegExp);
+                    const loopMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(loopRegExp);
+
+                    if (functionMatches) {
+                        functionMatchesLength = functionMatches.length;
                     }
-                ).getObfuscatedCode();
-                const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionRegExp);
-                const loopMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(loopRegExp);
 
-                if (functionMatches) {
-                    functionMatchesLength = functionMatches.length;
-                }
+                    if (loopMatches) {
+                        loopMatchesLength = loopMatches.length;
+                    }
+                });
 
-                if (loopMatches) {
-                    loopMatchesLength = loopMatches.length;
-                }
-            });
+                it('match #1: shouldn\'t add dead code', () => {
+                    assert.equal(functionMatchesLength, expectedFunctionMatchesLength);
+                });
 
-            it('match #1: shouldn\'t add dead code', () => {
-                assert.equal(functionMatchesLength, expectedFunctionMatchesLength);
+                it('match #2: shouldn\'t add dead code', () => {
+                    assert.equal(loopMatchesLength, expectedLoopMatchesLength);
+                });
             });
 
-            it('match #2: shouldn\'t add dead code', () => {
-                assert.equal(loopMatchesLength, expectedLoopMatchesLength);
+            describe('Variant #2', () => {
+                const functionRegExp: RegExp = new RegExp(
+                    `var *${variableMatch} *= *function *\\(\\) *\\{` +
+                        `console\\[${variableMatch}\\('${hexMatch}'\\)\\]\\(${variableMatch}\\('${hexMatch}'\\)\\);` +
+                    `\\};`,
+                    'g'
+                );
+                const loopRegExp: RegExp = new RegExp(
+                    `for *\\(var *${variableMatch} *= *${hexMatch}; *${variableMatch} *< *${hexMatch}; *${variableMatch}\\+\\+\\) *` +
+                        `(?:continue|break);`,
+                    'g'
+                );
+                const expectedFunctionMatchesLength: number = 4;
+                const expectedLoopMatchesLength: number = 2;
+
+                let functionMatchesLength: number = 0,
+                    loopMatchesLength: number = 0;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/break-continue-statement-2.js');
+
+                    const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            deadCodeInjection: true,
+                            deadCodeInjectionThreshold: 1,
+                            stringArray: true,
+                            stringArrayThreshold: 1
+                        }
+                    ).getObfuscatedCode();
+                    const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(functionRegExp);
+                    const loopMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(loopRegExp);
+
+                    if (functionMatches) {
+                        functionMatchesLength = functionMatches.length;
+                    }
+
+                    if (loopMatches) {
+                        loopMatchesLength = loopMatches.length;
+                    }
+                });
+
+                it('match #1: shouldn\'t add dead code', () => {
+                    assert.equal(functionMatchesLength, expectedFunctionMatchesLength);
+                });
+
+                it('match #2: shouldn\'t add dead code', () => {
+                    assert.equal(loopMatchesLength, expectedLoopMatchesLength);
+                });
             });
         });
 

+ 0 - 0
test/functional-tests/node-transformers/dead-code-injection-transformers/fixtures/break-continue-statement.js → test/functional-tests/node-transformers/dead-code-injection-transformers/fixtures/break-continue-statement-1.js


+ 27 - 0
test/functional-tests/node-transformers/dead-code-injection-transformers/fixtures/break-continue-statement-2.js

@@ -0,0 +1,27 @@
+(function(){
+    if (true) {
+        var foo = function () {
+            console.log('abc');
+        };
+        var bar = function () {
+            console.log('def');
+        };
+        var baz = function () {
+            console.log('ghi');
+        };
+        var bark = function () {
+            console.log('jkl');
+        };
+
+        for (var i = 0; i < 1; i++)
+            continue;
+
+        for (var i = 0; i < 1; i++)
+            break;
+
+        foo();
+        bar();
+        baz();
+        bark();
+    }
+})();

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini