Verified Commit f23787d6 authored by Juliana Oliveira's avatar Juliana Oliveira Committed by Mattia Rizzolo

comparators: java: adds support for procyon-decompiler

Uses procyon-decompiler as main option for ClassFiles and javap
as a fallback. (Closes: #849386)
Signed-off-by: 's avatarJuliana Oliveira <>
Signed-off-by: Mattia Rizzolo's avatarMattia Rizzolo <>
parent e1869c39
......@@ -20,13 +20,32 @@
import re
import os.path
import logging
from import tool_required
from diffoscope.difference import Difference
from diffoscope.exc import RequiredToolNotFound
from .utils.file import File
from .utils.command import Command
logger = logging.getLogger(__name__)
class ProcyonDecompiler(Command):
def __init__(self, path, *args, **kwargs):
super().__init__(path, *args, **kwargs)
self.real_path = os.path.realpath(path)
def cmdline(self):
return ['procyon-decompiler', '-ec', self.path]
def filter(self, line):
if re.match(r'^(//)', line.decode('utf-8')):
return b''
return line
class Javap(Command):
def __init__(self, path, *args, **kwargs):
......@@ -46,5 +65,24 @@ class Javap(Command):
class ClassFile(File):
FILE_TYPE_RE = re.compile(r'^compiled Java class data\b')
decompilers = [ProcyonDecompiler, Javap]
def compare_details(self, other, source=None):
return [Difference.from_command(Javap, self.path, other.path)]
diff = None
for decompiler in self.decompilers:
diff = [Difference.from_command(decompiler,
if diff:
except RequiredToolNotFound:
logger.debug("Unable to find %s. Falling back...",
if not diff:
raise RequiredToolNotFound(self.decompilers[-1])
return diff
......@@ -257,6 +257,9 @@ EXTERNAL_TOOLS = {
'arch': 'unzip',
'FreeBSD': 'unzip',
'procyon-decompiler': {
'debian': 'procyon-decompiler',
# May be populated at runtime by remapped names like
......@@ -21,7 +21,7 @@ import pytest
import subprocess
from diffoscope.config import Config
from import ClassFile
from import ClassFile, ProcyonDecompiler, Javap
from diffoscope.comparators.missing_file import MissingFile
from import load_fixture, get_data
......@@ -50,19 +50,45 @@ def test_no_differences(class1):
def differences(class1, class2):
def differences_procyon(monkeypatch, class1, class2):
monkeypatch.setattr(class1, 'decompilers', [ProcyonDecompiler])
@skip_unless_tool_is_at_least('javap', javap_version, '1.8')
def test_diff(differences):
expected_diff = get_data('class_expected_diff')
def differences_javap(monkeypatch, class1, class2):
monkeypatch.setattr(class1, 'decompilers', [Javap])
def diff(differences, expected_diff_file):
expected_diff = get_data(expected_diff_file)
assert differences[0].unified_diff == expected_diff
def test_compare_non_existing(monkeypatch, class1):
def compare_non_existing(monkeypatch, class1, decompiler):
monkeypatch.setattr(Config(), 'new_file', True)
monkeypatch.setattr(class1, 'decompilers', [decompiler])
difference ='/nonexisting', class1))
assert difference.source2 == '/nonexisting'
assert len(difference.details) > 0
def test_diff_procyon(differences_procyon):
diff(differences_procyon, 'procyon_class_expected_diff')
@skip_unless_tool_is_at_least('javap', javap_version, '1.8')
def test_diff_javap(differences_javap):
diff(differences_javap, 'javap_class_expected_diff')
def test_compare_non_existing_procyon(monkeypatch, class1):
compare_non_existing(monkeypatch, class1, ProcyonDecompiler)
def test_compare_non_existing_javap(monkeypatch, class1):
compare_non_existing(monkeypatch, class1, Javap)
@@ -1,7 +1,7 @@
class Test
public static int main(final String[] array) {
- return 42;
+ return -1;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment