Maarten Van Mechelen

44 papers A* 4A 1Misc 20Journal 10Unranked 9
YearRankTypeTitle / Venue / Authors
2025 J jnl
Int. J. Child Comput. Interact.
Mariana Aki Tamashiro, Marie-Monique A. Schaper, Ole Iversen, Maarten Van Mechelen, Rachel Charlotte Smith
2024 J jnl
Int. J. Child Comput. Interact.
Marie-Monique Schaper, Mariana Aki Tamashiro, Maarten Van Mechelen, Ole Sejer Iversen, Eva Eriksson
2024 conf
PDC (2)
Christian Dindler, Ole Sejer Iversen, Katrine Holm Kanstrup, Maarten Van Mechelen, Marie-Louise Wagner
2023 J jnl
Int. J. Child Comput. Interact.
Rachel Charlotte Smith, Marie-Monique Schaper, Mariana Aki Tamashiro, Maarten Van Mechelen, Marianne Graves Petersen, Ole Sejer Iversen
2023 J jnl
ACM Trans. Comput. Hum. Interact.
Maarten Van Mechelen, Rachel Charlotte Smith, Marie-Monique Schaper, Mariana Tamashiro, Karl-Emil Kjær Bilstrup, Mille Skovhus Lunding, Marianne Graves Petersen, Ole Sejer Iversen
2023 Misc conf
IDC
Marie-Monique Schaper, Mariana Aki Tamashiro, Rachel Charlotte Smith, Maarten Van Mechelen, Ole Sejer Iversen
2023 A conf
Conference on Designing Interactive Systems
Mariana Aki Tamashiro, Marie-Monique Schaper, Ane Jensen, Rune Heick, Brian Danielsen, Maarten Van Mechelen, Kasper Løvborg Jensen, Rachel Charlotte Smith, Ole Sejer Iversen
2022 J jnl
Int. J. Child Comput. Interact.
Marie-Monique Schaper, Rachel Charlotte Smith, Mariana Aki Tamashiro, Maarten Van Mechelen, Mille Skovhus Lunding, Karl-Emil Kjær Bilstrup, Magnus Høholt Kaspersen, Kasper Løvborg Jensen, Marianne Graves Petersen, Ole Sejer Iversen
2022 J jnl
Int. J. Child Comput. Interact.
Magnus Høholt Kaspersen, Karl-Emil Kjær Bilstrup, Maarten Van Mechelen, Arthur Hjorth, Niels Olof Bouvin, Marianne Graves Petersen
2022 A* conf
HRI
Maria Luce Lupetti, Maarten Van Mechelen
2022 Misc conf
IDC
Karl-Emil Kjær Bilstrup, Magnus Høholt Kaspersen, Mille Skovhus Lunding, Marie-Monique Schaper, Maarten Van Mechelen, Mariana Aki Tamashiro, Rachel Charlotte Smith, Ole Sejer Iversen, Marianne Graves Petersen
2021 Misc conf
IDC
Maarten Van Mechelen, Line Have Musaeus, Ole Sejer Iversen, Christian Dindler, Arthur Hjorth
2021 Misc conf
IDC
Maarten Van Mechelen, Marie-Louise Wagner, Gökçe Elif Baykal, Rachel Charlotte Smith, Ole Sejer Iversen
2021 Misc conf
IDC
Mariana Aki Tamashiro, Maarten Van Mechelen, Marie-Monique Schaper, Ole Sejer Iversen
2021 conf
FabLearn/MakeEd
Katrine Holm Kanstrup, Marie-Louise Wagner, Maarten Van Mechelen, Ole Sejer Iversen, Christian Dindler
2021 conf
FabLearn/MakeEd
Magnus Høholt Kaspersen, Karl-Emil Kjær Bilstrup, Maarten Van Mechelen, Arthur Hjorth, Niels Olof Bouvin, Marianne Graves Petersen
2021 J jnl
Int. J. Child Comput. Interact.
Gökçe Elif Baykal, Maarten Van Mechelen, Marie-Louise Wagner, Eva Eriksson
2020 Misc conf
IDC
Maarten Van Mechelen, Gökçe Elif Baykal, Christian Dindler, Eva Eriksson, Ole Sejer Iversen
2020 A* conf
CHI
Gökçe Elif Baykal, Maarten Van Mechelen, Eva Eriksson
2020 conf
IDC (Extended Abstracts)
Maarten Van Mechelen, Shuli Gilutz, Juan Pablo Hourcade, Gökçe Elif Baykal, Mathieu Gielen, Eva Eriksson, Greg Walsh, Janet C. Read, Ole Sejer Iversen
2019 Misc conf
IDC
Janet C. Read, Dan Fitton, Gavin Sim, Michail N. Giannakos, Maarten Van Mechelen, Martha Bjorklund, Suzanne Clarke, Nanna Borum, Steve Perry
2019 Misc conf
IDC
Maarten Van Mechelen, Alice Schut, Mathieu Gielen, Antonia Clasina Södergren
2019 J jnl
Int. J. Hum. Comput. Stud.
Maarten Van Mechelen, Ann Laenen, Bieke Zaman, Bert Willems, Vero Vanden Abeele
2019 J jnl
Int. J. Child Comput. Interact.
Gökçe Elif Baykal, Maarten Van Mechelen, Tilbe Göksun, Asim Evren Yantaç
2019 Misc conf
IDC
Antonia Clasina Södergren, Maarten Van Mechelen
2019 conf
FabLearn Europe
Eva Eriksson, Ole Sejer Iversen, Gökçe Elif Baykal, Maarten Van Mechelen, Rachel Charlotte Smith, Marie-Louise Wagner, Bjarke Vognstrup Fog, Clemens Nylandsted Klokmose, Bronwyn J. Cumbo, Arthur Hjorth, Line Have Musaeus, Marianne Graves Petersen, Niels Olof Bouvin
2018 conf
FabLearn Europe
Gökçe Elif Baykal, Maarten Van Mechelen, Tilbe Göksun, Asim Evren Yantaç
2018 Misc conf
IDC
Maarten Van Mechelen, Alice Schut, Mathieu Gielen, Remke Klapwijk
2018 Misc conf
IDC
Michael Soenthorn Speek, Maarten Van Mechelen
2018 conf
PDC (2)
Jonas Geuens, Luc Geurts, Thijs Willem Swinnen, René Westhovens, Maarten Van Mechelen, Vero Vanden Abeele
2018 Misc conf
IDC
Bieke Zaman, Maarten Van Mechelen, Lizzy Bleumers
2017 Misc conf
IDC
Maarten Van Mechelen, Marikken Høiseth, Gökçe Elif Baykal, Fenne van Doorn, Asimina Vasalou, Alice Schut
2017 Misc conf
IDC
Marikken Høiseth, Maarten Van Mechelen
2017 conf
PervasiveHealth
Karin Slegers, Maarten Van Mechelen, Jeroen Vanattenhoven
2017 A* conf
CHI
Arne Jansen, Maarten Van Mechelen, Karin Slegers
2017 J jnl
Int. J. Hum. Comput. Stud.
Maarten Van Mechelen, Jan Derboven, Ann Laenen, Bert Willems, David Geerts, Vero Vanden Abeele
2015 Misc conf
IDC
Marije Nouwen, Maarten Van Mechelen, Bieke Zaman
2015 Misc conf
IDC
Maarten Van Mechelen, Bieke Zaman, Ann Laenen, Vero Vanden Abeele
2015 A* conf
CHI
Jan Derboven, Maarten Van Mechelen, Karin Slegers
2015 Misc conf
IDC
Lizzy Bleumers, Karen Mouws, Jonathan Huyghe, Maarten Van Mechelen, Ilse Mariën, Bieke Zaman
2014 Misc conf
IDC
Maarten Van Mechelen, Gavin Sim, Bieke Zaman, Peggy Gregory, Karin Slegers, Matthew Horton
2014 Misc conf
IDC
Maarten Van Mechelen, Mathieu Gielen, Vero Vanden Abeele, Ann Laenen, Bieke Zaman
2014 Misc conf
CHI PLAY
Rilla Khaled, Vero Vanden Abeele, Maarten Van Mechelen, Asimina Vasalou
2013 conf
CHI Extended Abstracts
Arne Jansen, Nicky Sulmon, Maarten Van Mechelen, Bieke Zaman, Jeroen Vanattenhoven, Dirk De Grooff
tests/unit/test_decompile_decompiler.py
← Index tests/unit/test_decompile_decompiler.py python
"""Unit tests (mocked BN) for bninja/decompiler.py — BinaryNinjaDecompiler."""
import sys
import json
import time
import pytest
from unittest.mock import MagicMock, patch, mock_open

from tests.unit.conftest_binja_stubs import (
    install_binja_stubs,
    MockFunction,
    MockBasicBlock,
    MockDisassemblyLine,
    MockToken,
    MockSymbol,
    MockBinaryView,
    MockEdge,
    SymbolType,
    InstructionTextTokenType,
    BranchType,
)

install_binja_stubs()

from redb.extractors.decompiler.bninja.decompiler import (
    BinaryNinjaDecompiler,
    is_lib_or_thunk,
)
from redb.extractors.decompiler.bninja.function_type import FunctionType


# ============================================================================
# 10a. BinaryNinjaDecompiler
# ============================================================================


class TestBinaryNinjaDecompilerLogError:
    def _make_decompiler(self):
        with patch.object(BinaryNinjaDecompiler, "__init__", lambda self, *a, **kw: None):
            d = BinaryNinjaDecompiler.__new__(BinaryNinjaDecompiler)
            d.log = MagicMock()
            d.errors = []
            d.filepath = "/fake/binary"
            d.bv = None
            d.analysis_results = None
            d.exporters = []
            d.index_prefix = None
            d.filetype = None
            d.goresym = None
            d.BNINJA_TIMEOUT = 60
            return d

    def test_log_error_appends(self):
        d = self._make_decompiler()
        d.log_error("test error", "func1", 0x1000, Exception("boom"), "extract")
        assert len(d.errors) == 1

    def test_log_error_schema(self):
        d = self._make_decompiler()
        d.log_error("test error", "func1", 0x1000, Exception("boom"), "extract")
        error = d.errors[0]
        expected_keys = [
            "function_name",
            "function_address",
            "error_location",
            "error_message",
            "error_details",
            "error_type",
            "timestamp",
        ]
        for key in expected_keys:
            assert key in error, f"Missing key: {key}"


class TestBinaryNinjaDecompilerIsTooFewBlocks:
    def _make_decompiler(self):
        with patch.object(BinaryNinjaDecompiler, "__init__", lambda self, *a, **kw: None):
            d = BinaryNinjaDecompiler.__new__(BinaryNinjaDecompiler)
            d.log = MagicMock()
            d.errors = []
            d.MIN_FUNCTION_SIZE = 10
            return d

    def test_is_too_few_blocks_none(self):
        d = self._make_decompiler()
        func = MagicMock()
        func.basic_blocks = None
        assert d.is_too_few_blocks(func) is False

    def test_is_too_few_blocks_empty(self):
        # Empty basic_blocks now returns True because Binja creates function
        # stubs with empty blocks before analysis completes — we no longer
        # skip these so they get filtered downstream instead.
        d = self._make_decompiler()
        func = MagicMock()
        func.basic_blocks = []
        assert d.is_too_few_blocks(func) is True

    def test_is_too_few_blocks_small_single(self):
        d = self._make_decompiler()
        block = MagicMock()
        block.disassembly_text = [MagicMock() for _ in range(5)]  # < 10
        func = MagicMock()
        func.basic_blocks = [block]
        assert d.is_too_few_blocks(func) is False

    def test_is_too_few_blocks_large_single(self):
        d = self._make_decompiler()
        block = MagicMock()
        block.disassembly_text = [MagicMock() for _ in range(15)]  # >= 10
        func = MagicMock()
        func.basic_blocks = [block]
        assert d.is_too_few_blocks(func) is True

    def test_is_too_few_blocks_multiple(self):
        d = self._make_decompiler()
        func = MagicMock()
        func.basic_blocks = [MagicMock(), MagicMock()]
        assert d.is_too_few_blocks(func) is True


class TestIsLibOrThunk:
    def test_is_lib_or_thunk_user(self):
        func = MagicMock()
        func.symbol.type = SymbolType.FunctionSymbol
        func.is_thunk = False
        assert is_lib_or_thunk(func) is True  # NOT filtered

    def test_is_lib_or_thunk_thunk(self):
        func = MagicMock()
        func.symbol.type = SymbolType.FunctionSymbol
        func.is_thunk = True
        assert is_lib_or_thunk(func) is False  # Filtered out

    def test_is_lib_or_thunk_external(self):
        func = MagicMock()
        func.symbol.type = SymbolType.ImportedFunctionSymbol
        func.is_thunk = False
        assert is_lib_or_thunk(func) is False  # Filtered out


class TestBinaryNinjaDecompilerExtract:
    def _make_decompiler(self):
        with patch.object(BinaryNinjaDecompiler, "__init__", lambda self, *a, **kw: None):
            d = BinaryNinjaDecompiler.__new__(BinaryNinjaDecompiler)
            d.log = MagicMock()
            d.errors = []
            d.filepath = "/fake/binary"
            d.bv = MagicMock()
            d.analysis_results = None
            d.exporters = []
            d.index_prefix = None
            d.filetype = None
            d.goresym = None
            d.BNINJA_TIMEOUT = 60
            d.arch = MagicMock()
            return d

    def test_extract_success(self):
        d = self._make_decompiler()
        mock_results = {
            "decompiled": [{"test": True}],
            "disassembled": [],
            "cfg": [],
            "llil": [],
            "errors": [],
            "strings": [],
            "mlil": [],
        }
        with patch.object(d, "analyze_binary", return_value=mock_results):
            result = d.extract()
            assert result is True
            assert d.analysis_results == mock_results

    def test_extract_failure(self):
        d = self._make_decompiler()
        with patch.object(d, "analyze_binary", return_value=None):
            result = d.extract()
            assert result is False

    def test_tag(self):
        d = self._make_decompiler()
        assert d.tag() == "DECOMPILED"


class TestBinaryNinjaDecompilerAnalyzeBinary:
    def _make_decompiler(self):
        with patch.object(BinaryNinjaDecompiler, "__init__", lambda self, *a, **kw: None):
            d = BinaryNinjaDecompiler.__new__(BinaryNinjaDecompiler)
            d.log = MagicMock()
            d.errors = []
            d.filepath = "/fake/binary"
            d.analysis_results = None
            d.exporters = []
            d.index_prefix = None
            d.filetype = None
            d.goresym = None
            d.BNINJA_TIMEOUT = 60
            d.arch = MagicMock()
            d.MIN_FUNCTION_SIZE = 10
            return d

    def test_analyze_binary_links_hlil_disasm(self):
        """Bi-directional hash linkage between decompiled and disassembled."""
        d = self._make_decompiler()
        d.bv = MagicMock()
        d.bv.functions = []
        # Mock methods to return controlled data
        with patch.object(d, "extract_hlil") as mock_hlil, \
             patch.object(d, "extract_disasm") as mock_disasm, \
             patch.object(d, "extract_cfg", return_value=None), \
             patch.object(d, "extract_lowlevel", return_value=None):

            mock_hlil.return_value = {
                "decompiled_function_hash": "HLIL_HASH",
                "decompiled_function_name": "test",
                "decompiled_function": "code",
            }
            mock_disasm.return_value = {
                "disassembled_function_hash": "DISASM_HASH",
                "disassembled_function_no_addresses": "asm",
            }

            # Manually feed one function
            mock_func = MagicMock()
            mock_func.symbol.type = SymbolType.FunctionSymbol
            mock_func.is_thunk = False
            mock_func.basic_blocks = [MagicMock(), MagicMock()]
            d.bv.functions = [mock_func]

            results = d.analyze_binary()
            if results and results["decompiled"]:
                hlil_r = results["decompiled"][0]
                disasm_r = results["disassembled"][0]
                assert hlil_r["disassembled_function_hash"] == "DISASM_HASH"
                assert disasm_r["decompiled_function_hash"] == "HLIL_HASH"

    def test_analyze_binary_links_llil_disasm(self):
        """Bi-directional linkage between LLIL and disassembly."""
        d = self._make_decompiler()
        d.bv = MagicMock()

        with patch.object(d, "extract_hlil", return_value=None), \
             patch.object(d, "extract_disasm") as mock_disasm, \
             patch.object(d, "extract_cfg", return_value=None), \
             patch.object(d, "extract_lowlevel") as mock_llil:

            mock_disasm.return_value = {
                "disassembled_function_hash": "DISASM_HASH",
                "disassembled_function_no_addresses": "asm",
            }
            mock_llil.return_value = {
                "sha256_llil": "LLIL_HASH",
                "tlsh_llil": "tlsh_val",
            }

            mock_func = MagicMock()
            mock_func.symbol.type = SymbolType.FunctionSymbol
            mock_func.is_thunk = False
            mock_func.basic_blocks = [MagicMock(), MagicMock()]
            d.bv.functions = [mock_func]

            results = d.analyze_binary()
            if results and results["llil"]:
                llil_r = results["llil"][0]
                assert llil_r["disassembled_function_hash"] == "DISASM_HASH"

    def test_analyze_binary_links_cfg_disasm(self):
        """CFG gets disassembled_function_hash."""
        d = self._make_decompiler()
        d.bv = MagicMock()

        with patch.object(d, "extract_hlil", return_value=None), \
             patch.object(d, "extract_disasm") as mock_disasm, \
             patch.object(d, "extract_cfg") as mock_cfg, \
             patch.object(d, "extract_lowlevel", return_value=None):

            mock_disasm.return_value = {
                "disassembled_function_hash": "DISASM_HASH",
                "disassembled_function_no_addresses": "asm",
            }
            mock_cfg.return_value = {
                "function_address": 0x1000,
                "cyclomatic_complexity": 3,
            }

            mock_func = MagicMock()
            mock_func.symbol.type = SymbolType.FunctionSymbol
            mock_func.is_thunk = False
            mock_func.basic_blocks = [MagicMock(), MagicMock()]
            d.bv.functions = [mock_func]

            results = d.analyze_binary()
            if results and results["cfg"]:
                cfg_r = results["cfg"][0]
                assert cfg_r["disassembled_function_hash"] == "DISASM_HASH"

    def test_analyze_binary_sets_cyclomatic(self):
        """cyclomatic_complexity set on disasm from CFG."""
        d = self._make_decompiler()
        d.bv = MagicMock()

        with patch.object(d, "extract_hlil", return_value=None), \
             patch.object(d, "extract_disasm") as mock_disasm, \
             patch.object(d, "extract_cfg") as mock_cfg, \
             patch.object(d, "extract_lowlevel", return_value=None):

            mock_disasm.return_value = {
                "disassembled_function_hash": "HASH",
                "disassembled_function_no_addresses": "asm",
            }
            mock_cfg.return_value = {
                "function_address": 0x1000,
                "cyclomatic_complexity": 7,
            }

            mock_func = MagicMock()
            mock_func.symbol.type = SymbolType.FunctionSymbol
            mock_func.is_thunk = False
            mock_func.basic_blocks = [MagicMock(), MagicMock()]
            d.bv.functions = [mock_func]

            results = d.analyze_binary()
            if results and results["disassembled"]:
                disasm_r = results["disassembled"][0]
                assert disasm_r.get("cyclomatic_complexity") == 7


class TestApplyGoReSym:
    def _make_decompiler(self):
        with patch.object(BinaryNinjaDecompiler, "__init__", lambda self, *a, **kw: None):
            d = BinaryNinjaDecompiler.__new__(BinaryNinjaDecompiler)
            d.log = MagicMock()
            d.errors = []
            d.bv = MagicMock()
            return d

    def test_apply_goresym_user_functions(self):
        d = self._make_decompiler()
        d.goresym = "/tmp/goresym.json"
        data = {
            "UserFunctions": [{"Start": 0x1000, "FullName": "main.main"}],
        }
        with patch("builtins.open", mock_open(read_data=json.dumps(data))):
            d._BinaryNinjaDecompiler__apply_goresym()
            d.bv.define_user_symbol.assert_called()

    def test_apply_goresym_std_functions(self):
        d = self._make_decompiler()
        d.goresym = "/tmp/goresym.json"
        data = {
            "StdFunctions": [{"Start": 0x2000, "FullName": "runtime.main"}],
        }
        with patch("builtins.open", mock_open(read_data=json.dumps(data))):
            d._BinaryNinjaDecompiler__apply_goresym()
            d.bv.define_user_symbol.assert_called()

    def test_apply_goresym_invalid_json(self):
        d = self._make_decompiler()
        d.goresym = "/tmp/goresym.json"
        with patch("builtins.open", mock_open(read_data="not json {")):
            d._BinaryNinjaDecompiler__apply_goresym()
            # Should log error but not crash
            d.log.log_error if hasattr(d.log, 'log_error') else None
            # Just verify no exception raised