Skip to content

Commit

Permalink
fix: update parent scope when merging dead code
Browse files Browse the repository at this point in the history
  • Loading branch information
j4k0xb committed Feb 24, 2024
1 parent acb62d5 commit 2ee01f4
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 22 deletions.
41 changes: 19 additions & 22 deletions packages/webcrack/src/deobfuscate/dead-code.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NodePath, Scope } from '@babel/traverse';
import type { NodePath } from '@babel/traverse';
import * as t from '@babel/types';
import * as m from '@codemod/matchers';
import type { Transform } from '../ast-utils';
Expand Down Expand Up @@ -28,25 +28,10 @@ export default {

if (!testMatcher.match(path.node.test)) return;

const { scope } = path;
// If statements can contain variables that shadow variables in the parent scope.
// Since the block scope is merged with the parent scope, we need to rename those
// variables to avoid duplicate declarations.
function renameShadowedVariables(localScope: Scope) {
if (localScope === scope) return;
for (const name in localScope.bindings) {
if (scope.hasBinding(name)) {
renameFast(localScope.bindings[name], scope.generateUid(name));
}
}
}

if (path.get('test').evaluateTruthy()) {
renameShadowedVariables(path.get('consequent').scope);
replace(path, path.node.consequent);
replace(path, path.get('consequent'));
} else if (path.node.alternate) {
renameShadowedVariables(path.get('alternate').scope);
replace(path, path.node.alternate);
replace(path, path.get('alternate') as NodePath);
} else {
path.remove();
}
Expand All @@ -58,10 +43,22 @@ export default {
},
} satisfies Transform;

function replace(path: NodePath, node: t.Node) {
if (t.isBlockStatement(node)) {
path.replaceWithMultiple(node.body);
function replace(path: NodePath<t.Conditional>, replacement: NodePath) {
if (t.isBlockStatement(replacement.node)) {
// If statements can contain variables that shadow variables in the parent scope.
// Since the block scope is merged with the parent scope, we need to rename those
// variables to avoid duplicate declarations.
const childBindings = replacement.scope.bindings;
for (const name in childBindings) {
const binding = childBindings[name];
if (path.scope.hasOwnBinding(name)) {
renameFast(binding, path.scope.generateUid(name));
}
binding.scope = path.scope;
path.scope.bindings[binding.identifier.name] = binding;
}
path.replaceWithMultiple(replacement.node.body);
} else {
path.replaceWith(node);
path.replaceWith(replacement);
}
}
34 changes: 34 additions & 0 deletions packages/webcrack/src/deobfuscate/test/dead-code.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { test } from 'vitest';
import { testTransform } from '../../../test';
import deadCode from '../dead-code';

const expectJS = testTransform(deadCode);

test('keep true branch', () =>
expectJS(`
if ("a" === "a") {
console.log("foo");
} else {
console.log("bar");
}
`).toMatchInlineSnapshot(`console.log("foo");`));

test('keep false branch', () =>
expectJS(`
if ("a" !== "a") {
console.log("foo");
} else {
console.log("bar");
}
`).toMatchInlineSnapshot(`console.log("bar");`));

test('merge scopes', () =>
expectJS(`
let foo = 1;
if ("a" === "a") {
let foo = 2;
}
`).toMatchInlineSnapshot(`
let foo = 1;
let _foo = 2;
`));

0 comments on commit 2ee01f4

Please sign in to comment.