Jan A. Persson

65 papers B 1C 9Misc 4Journal 17Unranked 33
YearRankTypeTitle / Venue / Authors
2025 J jnl
IEEE Access
Kayode Sakariyah Adewole, Jan A. Persson, Andreas Jacobsson, Erdal Akin, Azad Shokrollahi, Reza Malekian, Héctor Caltenco, Marcus Valtonen Örnhag
2025 conf
ICAART (3)
Azad Shokrollahi, Fredrik Karlsson, Reza Malekian, Jan A. Persson, Arezoo Sarkheyli-Hägele
2025 J jnl
Internet Things
Azad Shokrollahi, Fredrik Karlsson, Reza Malekian, Jan A. Persson, Arezoo Sarkheyli-Hägele
2025 conf
FiCloud
Piotr Kolpa, Kayode S. Adewole, Jan A. Persson, Fredrik Karlsson
2024 J jnl
Public Transp.
Åse Jevinger, Chunli Zhao, Jan A. Persson, Paul Davidsson
2024 J jnl
Sensors
Azad Shokrollahi, Jan A. Persson, Reza Malekian, Arezoo Sarkheyli-Hägele, Fredrik Karlsson
2024 conf
WF-IoT
Erdal Akin, Kayode S. Adewole, Héctor Caltenco, Reza Malekian, Jan A. Persson
2024 conf
ICIT
Erdal Akin, Héctor Caltenco, Kayode S. Adewole, Reza Malekian, Jan A. Persson
2023 C conf
IPIN
Jimmy Engström, Jan A. Persson
2023 C conf
VEHITS
Åse Jevinger, Emil Johansson, Jan A. Persson, Johan Holmberg
2023 conf
HHAI
Agnes Tegen, Paul Davidsson, Jan A. Persson
2023 conf
ITSC
Sergei Dytckov, Jan A. Persson, Paul Davidsson
2023 J jnl
Int. J. Intell. Transp. Syst. Res.
Fabian Lorig, Jan A. Persson, Astrid Michielsen
2023 J jnl
Sensors
Jimmy Engström, Åse Jevinger, Carl Magnus Olsson, Jan A. Persson
2022 C conf
WEBIST
Joseph Bugeja, Jan A. Persson
2022 J jnl
Simul. Notes Eur.
Fabian Lorig, Emil Johansson, Paul Davidsson, Jan A. Persson
2021 conf
ADBIS (Short Papers)
Sadi Alawadi, Victor R. Kebande, Yuji Dong, Joseph Bugeja, Jan A. Persson, Carl Magnus Olsson
2021 C conf
ICMLA
Agnes Tegen, Paul Davidsson, Jan A. Persson
2021 J jnl
IEEE Access
Victor R. Kebande, Sadi Alawadi, Feras M. Awaysheh, Jan A. Persson
2020 conf
ECML/PKDD (2)
Agnes Tegen, Paul Davidsson, Jan A. Persson
2020 conf
INTSYS
Sergei Dytckov, Fabian Lorig, Johan Holmgren, Paul Davidsson, Jan A. Persson
2020 J jnl
CoRR
Victor R. Kebande, Joseph Bugeja, Jan A. Persson
2020 conf
IOT Companion
Victor R. Kebande, Sadi Alawadi, Joseph Bugeja, Jan A. Persson, Carl Magnus Olsson
2020 C conf
VEHITS
Sergei Dytckov, Fabian Lorig, Paul Davidsson, Johan Holmgren, Jan A. Persson
2020 conf
IAL@PKDD/ECML
Agnes Tegen, Paul Davidsson, Jan A. Persson
2020 Misc conf
NordiCHI
Maliheh Ghajargar, Jan A. Persson, Jeffrey Bardzell, Lars Holmberg, Agnes Tegen
2019 J jnl
Sensors
Agnes Tegen, Paul Davidsson, Radu-Casian Mihailescu, Jan A. Persson
2019 J jnl
Public Transp.
Åse Jevinger, Jan A. Persson
2019 conf
IOT
Agnes Tegen, Paul Davidsson, Jan A. Persson
2019 conf
CPSS@IOT
Victor R. Kebande, Joseph Bugeja, Jan A. Persson
2019 conf
UbiComp/ISWC Adjunct
Agnes Tegen, Paul Davidsson, Jan A. Persson
2018 conf
ANT/SEIT
Paul Davidsson, Jan A. Persson
2018 J jnl
Knowl. Eng. Rev.
Radu-Casian Mihailescu, Paul Davidsson, Ulrik Eklund, Jan A. Persson
2018 conf
IAL@PKDD/ECML
Agnes Tegen, Paul Davidsson, Jan A. Persson
2016 Misc conf
IDC
Radu-Casian Mihailescu, Jan A. Persson, Paul Davidsson, Ulrik Eklund
2015 conf
EUMAS/AT
Radu-Casian Mihailescu, Paul Davidsson, Jan A. Persson
2015 C conf
MABS
Banafsheh Hajinasab, Paul Davidsson, Jan A. Persson, Johan Holmgren
2014 conf
ANT/SEIT
Banafsheh Hajinasab, Paul Davidsson, Jan A. Persson
2014 conf
ANT/SEIT
Johan Holmgren, Linda Ramstedt, Paul Davidsson, Henrik Edwards, Jan A. Persson
2013 conf
ANT/SEIT
Johan Holmgren, Mattias Dahl, Paul Davidsson, Jan A. Persson
2013 J jnl
Comput. Ind.
Johan Woxenius, Jan A. Persson, Paul Davidsson
2012 Misc conf
NordiCHI
Banafsheh Hajinasab, Paul Davidsson, Jonas Löwgren, Jan A. Persson
2012 J jnl
J. Intell. Transp. Syst.
Gideon Mbiydzenyuy, Jan A. Persson, Paul Davidsson
2012 J jnl
Simul. Model. Pract. Theory
Johan Holmgren, Paul Davidsson, Jan A. Persson, Linda Ramstedt
2011 B conf
PRIMA
Åse Jevinger, Paul Davidsson, Jan A. Persson
2011 Misc conf
IDC
Åse Jevinger, Paul Davidsson, Jan A. Persson
2011 conf
PRIMA Workshops
Johan Holmgren, Linda Ramstedt, Paul Davidsson, Jan A. Persson
2011 conf
ITSC
Gideon Mbiydzenyuy, Jan A. Persson, Paul Davidsson
2009 J jnl
Auton. Agents Multi Agent Syst.
Lawrence Henesey, Paul Davidsson, Jan A. Persson
2009 conf
KES-AMSTA
Johan Holmgren, Jan A. Persson, Paul Davidsson
2009 conf
ICE-B
Paul Davidsson, Jan A. Persson, Andreas Jacobsson
2009 ch.
Multi-Agent Systems for Traffic and Transportation Engineering
Lawrence Henesey, Jan A. Persson
2009 conf
ITSC
Gideon Mbiydzenyuy, Jan A. Persson, Paul Davidsson
2009 conf
ICETE (Selected Papers)
Paul Davidsson, Johan Holmgren, Jan A. Persson, Andreas Jacobsson
2008 C conf
MABS
Lawrence Henesey, Paul Davidsson, Jan A. Persson
2008 conf
AAMAS (2)
Paul Davidsson, Johan Holmgren, Jan A. Persson, Linda Ramstedt
2008 conf
ATOP@AAMAS
Bengt Carlsson, Paul Davidsson, Andreas Jacobsson, Stefan J. Johansson, Jan A. Persson
2007 conf
KES-AMSTA
Paul Davidsson, Jan A. Persson, Johan Holmgren
2006 conf
MATES
Lawrence Henesey, Paul Davidsson, Jan A. Persson
2006 conf
ICEIS (4)
Paul Davidsson, Anders Hederstierna, Andreas Jacobsson, Jan A. Persson, Bengt Carlsson, Stefan J. Johansson, Anders Nilsson, Gunnar Ågren, Stefan Östholm
2005 C conf
EUMAS
Jan A. Persson, Paul Davidsson, Stefan J. Johansson, Fredrik Wernstedt
2005 conf
HICSS
Jan A. Persson, Paul Davidsson
2005 J jnl
Eur. J. Oper. Res.
Jan A. Persson, Maud Göthe-Lundgren
2005 conf
HICSS
Johanna Törnquist, Jan A. Persson
2004 C conf
MABS
Markus Bergkvist, Paul Davidsson, Jan A. Persson, Linda Ramstedt
tests/unit/test_decompile_medium_level.py
← Index tests/unit/test_decompile_medium_level.py python
# tests/unit/test_decompile_medium_level.py
"""Unit tests (mocked BN) for bninja/analysis/medium_level.py
   and bninja/analysis/medium_level_normalization.py."""
# tests/unit/test_decompile_medium_level.py
import sys
from unittest.mock import MagicMock, patch

# Installa gli stubs BN
from tests.unit.conftest_binja_stubs import install_binja_stubs
install_binja_stubs()

# ── Definisci MockMLILInstruction PRIMA di importare il modulo ──
class MockMLILInstruction:
    def __init__(self, operation, address=0, operands=None):
        self.operation = operation
        self.address = address
        self.operands = operands or []

# ── Patcha il modulo BN in modo che isinstance() funzioni ──
sys.modules["binaryninja"].MediumLevelILInstruction = MockMLILInstruction
sys.modules["binaryninja"].SSAVariable = type("SSAVariable", (), {})
sys.modules["binaryninja"].Variable = type("Variable", (), {})
sys.modules["binaryninja"].ILIntrinsic = type("ILIntrinsic", (), {})

# Ora importa il modulo — vede già i tipi corretti
from redb.extractors.decompiler.bninja.analysis.medium_level_normalization import (
    MediumLevelNormalization,
)	

class MockMLILFunction:
    def __init__(self, instructions):
        self._instructions = instructions

    @property
    def instructions(self):
        return iter(self._instructions)

    @property
    def basic_blocks(self):
        # one block containing all instructions, good enough for MinHasher
        block = MagicMock()
        block.__iter__ = lambda self_: iter([])  # not used by MediumLevelAnalysis
        return [block]


class MockFunction:
    def __init__(self, name="func", start=0x1000, mlil=None):
        self.name = name
        self.start = start
        self.mlil = mlil



class TestMediumLevelNormalization:
    def setup_method(self):
        from redb.extractors.decompiler.bninja.analysis.medium_level_normalization import (
            MediumLevelNormalization,
        )
        self.norm = MediumLevelNormalization()

    def test_normalize_skeleton_single_instruction(self):
        il = MockMLILInstruction(operation=42, operands=[])
        result = self.norm.normalize_instruction_all_levels(il)
        assert result == [42]

    def test_normalize_skeleton_nested(self):
        inner = MockMLILInstruction(operation=7, operands=[])
        outer = MockMLILInstruction(operation=1, operands=[inner])
        result = self.norm.normalize_instruction_all_levels(outer)
        assert result == [1, 7]

    def test_normalize_skeleton_with_list_operand(self):
        inner_a = MockMLILInstruction(operation=10, operands=[])
        inner_b = MockMLILInstruction(operation=11, operands=[])
        outer = MockMLILInstruction(operation=2, operands=[[inner_a, inner_b]])
        result = self.norm.normalize_instruction_all_levels(outer)
        assert result == [2, 10, 11]

    def test_normalize_skeleton_none(self):
        result = self.norm.normalize_instruction_all_levels(None)
        # collect on None should leave ops empty
        assert result == []

    def test_normalize_typed_appends_leaf_types(self):
        # operand is a plain int -> "CONST"
        il = MockMLILInstruction(operation=3, operands=[42])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [3, "CONST"]

    def test_normalize_typed_bool_before_int(self):
        # bool must be detected before int (since bool is an int subclass)
        il = MockMLILInstruction(operation=4, operands=[True])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [4, "BOOL"]

    def test_normalize_typed_float(self):
        il = MockMLILInstruction(operation=5, operands=[1.5])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [5, "FLOAT_CONST"]

    def test_normalize_typed_str(self):
        il = MockMLILInstruction(operation=6, operands=["hello"])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [6, "STR"]

    def test_normalize_typed_unknown_falls_back_to_typename(self):
        class Weird:
            pass
        il = MockMLILInstruction(operation=8, operands=[Weird()])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [8, "WEIRD"]

    def test_normalize_typed_nested_mlil(self):
        inner = MockMLILInstruction(operation=99, operands=[7])
        outer = MockMLILInstruction(operation=1, operands=[inner])
        result = self.norm.normalize_instr_with_operands(outer)
        assert result == [1, 99, "CONST"]

    def test_normalize_typed_list_mixed(self):
        inner = MockMLILInstruction(operation=50, operands=[])
        il = MockMLILInstruction(operation=2, operands=[[inner, 99]])
        result = self.norm.normalize_instr_with_operands(il)
        assert result == [2, 50, "CONST"]


class TestMediumLevelAnalysis:
    def _make_analysis(self, instructions=None, mlil=True, start=0x1000):
        from redb.extractors.decompiler.bninja.analysis.medium_level import (
            MediumLevelAnalysis,
        )
        mlil_func = MockMLILFunction(instructions or []) if mlil else None
        func = MockFunction(name="testfunc", start=start, mlil=mlil_func)
        bv = MagicMock()
        return MediumLevelAnalysis(func, bv, MagicMock())

    def test_collect_returns_empty_when_no_mlil(self):
        a = self._make_analysis(mlil=False)
        sk, sk_addr, ty, ty_addr = a._collect_mlil_skeleton_and_typed()
        assert sk == [] and sk_addr == [] and ty == [] and ty_addr == []

    def test_collect_skeleton_and_typed_basic(self):
        instrs = [
            MockMLILInstruction(operation=1, address=0x1000, operands=[]),
            MockMLILInstruction(operation=2, address=0x1004, operands=[42]),
        ]
        a = self._make_analysis(instructions=instrs, start=0x1000)
        sk, sk_addr, ty, ty_addr = a._collect_mlil_skeleton_and_typed()

        assert sk == [[1], [2]]
        assert ty == [[1], [2, "CONST"]]
        assert sk_addr == [(0, [1]), (4, [2])]
        assert ty_addr == [(0, [1]), (4, [2, "CONST"])]

    def test_collect_negative_offset_clamped_to_zero(self):
        instrs = [
            MockMLILInstruction(operation=1, address=0x900, operands=[]),
        ]
        a = self._make_analysis(instructions=instrs, start=0x1000)
        _, sk_addr, _, ty_addr = a._collect_mlil_skeleton_and_typed()
        assert sk_addr[0][0] == 0
        assert ty_addr[0][0] == 0

    def test_log_error_records_entry(self):
        a = self._make_analysis()
        a.log_error("boom", "fname", 0x1234, ValueError("x"), "loc")
        assert len(a.errors) == 1
        err = a.errors[0]
        assert err["function_name"] == "fname"
        assert err["function_address"] == "4660"  # hex 0x1234
        assert err["error_location"] == "loc"
        assert err["error_message"] == "boom"
        assert err["error_type"] == "ValueError"
        assert "timestamp" in err

    @patch(
        "redb.extractors.decompiler.bninja.analysis.medium_level.MinHasher"
    )
    def test_analyze_returns_expected_keys(self, mock_minhasher):
        mock_minhasher.return_value.calculateMinHash.return_value = [1, 2, 3]

        instrs = [
            MockMLILInstruction(operation=1, address=0x1000, operands=[]),
            MockMLILInstruction(operation=2, address=0x1004, operands=[42]),
            MockMLILInstruction(operation=3, address=0x1008, operands=[]),
        ]
        a = self._make_analysis(instructions=instrs, start=0x1000)
        result, errors = a.analyze()

        expected_keys = {
            "function_address",
            "body_mlil_skeleton_vector",
            "sha256_mlil_skeleton",
            "tlsh_mlil_skeleton",
            "minhash_mlil_skeleton",
            "body_mlil_typed_vector",
            "sha256_mlil_typed",
            "tlsh_mlil_typed",
            "minhash_mlil_typed",
        }
        assert set(result.keys()) == expected_keys
        assert result["function_address"] == 0x1000
        assert result["minhash_mlil_skeleton"] == [1, 2, 3]
        assert result["minhash_mlil_typed"] == [1, 2, 3]
        assert errors == []

    @patch(
        "redb.extractors.decompiler.bninja.analysis.medium_level.MinHasher"
    )
    def test_analyze_empty_mlil(self, mock_minhasher):
        mock_minhasher.return_value.calculateMinHash.return_value = []
        a = self._make_analysis(mlil=False)
        result, errors = a.analyze()
        assert result["body_mlil_skeleton_vector"] == []
        assert result["body_mlil_typed_vector"] == []
        assert errors == []

    @patch(
        "redb.extractors.decompiler.bninja.analysis.medium_level.MinHasher"
    )
    def test_analyze_sha256_differs_skeleton_vs_typed(self, mock_minhasher):
        mock_minhasher.return_value.calculateMinHash.return_value = []

        instrs = [
            MockMLILInstruction(operation=1, address=0x1000, operands=[42]),
            MockMLILInstruction(operation=2, address=0x1004, operands=["foo"]),
            MockMLILInstruction(operation=3, address=0x1008, operands=[True]),
        ]
        a = self._make_analysis(instructions=instrs)
        result, _ = a.analyze()
        # skeleton ignores operand leaves, typed includes them -> different hashes
        assert result["sha256_mlil_skeleton"] != result["sha256_mlil_typed"]


class TestMinHasherMLILKinds:
    def _make_func(self, instrs):
        # MinHasher iterates basic_blocks then over each block
        block = MagicMock()
        block.__iter__ = lambda self_: iter(instrs)
        f = MagicMock()
        f.basic_blocks = [block]
        return f

    def test_mlil_skeleton_uses_medium_normalizer(self):
        from redb.extractors.decompiler.bninja.similarity.minhasher import (
            MinHasher, TokenKind,
        )
        instrs = [
            MockMLILInstruction(operation=i, operands=[]) for i in range(5)
        ]
        func = self._make_func(instrs)
        hasher = MinHasher(seed=42, il_function=func, kind=TokenKind.MLIL)
        result = hasher.calculateMinHash()
        # 5 instructions -> 3 trigrams -> non-empty signature
        assert result != []

    def test_typed_mlil_differs_from_skeleton(self):
        from redb.extractors.decompiler.bninja.similarity.minhasher import (
            MinHasher, TokenKind,
        )
        instrs = [
            MockMLILInstruction(operation=1, operands=[42]),
            MockMLILInstruction(operation=2, operands=["s"]),
            MockMLILInstruction(operation=3, operands=[True]),
            MockMLILInstruction(operation=4, operands=[1.5]),
        ]
        func = self._make_func(instrs)
        skel = MinHasher(seed=42, il_function=func, kind=TokenKind.MLIL).calculateMinHash()
        typed = MinHasher(seed=42, il_function=func, kind=TokenKind.TYPED_MLIL).calculateMinHash()
        # Same seed, same instructions, but typed has extra leaf tokens
        # -> hashes should generally differ
        assert skel != typed

    def test_mlil_too_few_instructions(self):
        from redb.extractors.decompiler.bninja.similarity.minhasher import (
            MinHasher, TokenKind,
        )
        instrs = [MockMLILInstruction(operation=1, operands=[])] * 2
        func = self._make_func(instrs)
        hasher = MinHasher(seed=42, il_function=func, kind=TokenKind.MLIL)
        assert hasher.calculateMinHash() == []

    def test_unsupported_kind_raises(self):
        from redb.extractors.decompiler.bninja.similarity.minhasher import MinHasher
        func = self._make_func([])
        hasher = MinHasher(seed=42, il_function=func, kind="bogus")
        with pytest.raises(ValueError):
            hasher.calculateMinHash()