Module refinery.lib.scripts.js.deobfuscation.deadcode

Eliminate dead code branches guarded by constant conditions.

This transformer prunes unreachable branches from if/else statements when the test is a literal whose truthiness can be determined statically.

Expand source code Browse git
"""
Eliminate dead code branches guarded by constant conditions.

This transformer prunes unreachable branches from ``if``/``else`` statements when the test is a
literal whose truthiness can be determined statically.
"""
from __future__ import annotations

from refinery.lib.scripts import Node, Statement
from refinery.lib.scripts.js.deobfuscation.helpers import (
    BodyProcessingTransformer,
    is_statically_evaluable,
    is_truthy,
)
from refinery.lib.scripts.js.model import (
    JsBlockStatement,
    JsIfStatement,
)


class JsDeadCodeElimination(BodyProcessingTransformer):
    """
    Remove unreachable code guarded by constant conditions.
    """

    def _process_body(self, parent: Node, body: list[Statement]):
        result: list[Statement] = []
        changed = False
        for stmt in body:
            replacement = self._try_prune(stmt)
            if replacement is not None:
                result.extend(replacement)
                changed = True
            else:
                result.append(stmt)
        if changed:
            self._replace_body(parent, body, result)

    @staticmethod
    def _try_prune(stmt: Statement) -> list[Statement] | None:
        if not isinstance(stmt, JsIfStatement):
            return None
        if stmt.test is None or not is_statically_evaluable(stmt.test):
            return None
        truthy = is_truthy(stmt.test)
        if truthy is None:
            return None
        if truthy:
            return JsDeadCodeElimination._unwrap_branch(stmt.consequent)
        return JsDeadCodeElimination._unwrap_branch(stmt.alternate)

    @staticmethod
    def _unwrap_branch(branch: Statement | None) -> list[Statement]:
        """
        Extract the statements from a branch. If the branch is a block, return its body list
        contents; if it is a bare statement, wrap it in a single-element list.
        """
        if branch is None:
            return []
        if isinstance(branch, JsBlockStatement):
            return list(branch.body)
        return [branch]

Classes

class JsDeadCodeElimination

Remove unreachable code guarded by constant conditions.

Expand source code Browse git
class JsDeadCodeElimination(BodyProcessingTransformer):
    """
    Remove unreachable code guarded by constant conditions.
    """

    def _process_body(self, parent: Node, body: list[Statement]):
        result: list[Statement] = []
        changed = False
        for stmt in body:
            replacement = self._try_prune(stmt)
            if replacement is not None:
                result.extend(replacement)
                changed = True
            else:
                result.append(stmt)
        if changed:
            self._replace_body(parent, body, result)

    @staticmethod
    def _try_prune(stmt: Statement) -> list[Statement] | None:
        if not isinstance(stmt, JsIfStatement):
            return None
        if stmt.test is None or not is_statically_evaluable(stmt.test):
            return None
        truthy = is_truthy(stmt.test)
        if truthy is None:
            return None
        if truthy:
            return JsDeadCodeElimination._unwrap_branch(stmt.consequent)
        return JsDeadCodeElimination._unwrap_branch(stmt.alternate)

    @staticmethod
    def _unwrap_branch(branch: Statement | None) -> list[Statement]:
        """
        Extract the statements from a branch. If the branch is a block, return its body list
        contents; if it is a bare statement, wrap it in a single-element list.
        """
        if branch is None:
            return []
        if isinstance(branch, JsBlockStatement):
            return list(branch.body)
        return [branch]

Ancestors