Skip to content

byte constants are not decompiled correctly #39

@mristin

Description

@mristin

Hi,

I've just encountered a serious bug with Python 3. Namely, the byte constants are not decompiled correctly.

The string constants are decompiled correctly:

>>> import ast
>>> import meta
>>> import meta.decompiler

>>> f=lambda: 'oi'
>>> tree = meta.decompiler.decompile_func(f)
>>> ast.dump(tree)
"Lambda(args=arguments(args=[], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=Return(value=Str(s='oi')))"

However, when I run the similar example with the bytes:

>>> import ast
>>> import meta
>>> import meta.decompiler

>>> f=lambda: b'oi'
>>> tree = meta.decompiler.decompile_func(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/marko/workspace/pqryopen/icontract/venv3/lib/python3.5/site-packages/meta/decompiler/__init__.py", line 37, in decompile_func
    ast_node = make_function(code, defaults=[], lineno=code.co_firstlineno)
  File "/home/marko/workspace/pqryopen/icontract/venv3/lib/python3.5/site-packages/meta/decompiler/instructions.py", line 137, in make_function
    stmnts = instructions.stmnt()
  File "/home/marko/workspace/pqryopen/icontract/venv3/lib/python3.5/site-packages/meta/decompiler/instructions.py", line 310, in stmnt
    self.visit(instr)
  File "/home/marko/workspace/pqryopen/icontract/venv3/lib/python3.5/site-packages/meta/decompiler/instructions.py", line 324, in visit
    method(instr)
  File "/home/marko/workspace/pqryopen/icontract/venv3/lib/python3.5/site-packages/meta/decompiler/simple_instructions.py", line 279, in RETURN_VALUE
    value = self.process_ifexpr(value)
  File "/home/marko/workspace/pqryopen/icontract/venv3/lib/python3.5/site-packages/meta/decompiler/simple_instructions.py", line 343, in process_ifexpr
    return ExpressionMutator().visit(node)
  File "/usr/lib/python3.5/ast.py", line 245, in visit
    return visitor(node)
  File "/home/marko/workspace/pqryopen/icontract/venv3/lib/python3.5/site-packages/meta/decompiler/expression_mutator.py", line 39, in generic_visit
    return NodeTransformer.generic_visit(self, node)
  File "/usr/lib/python3.5/ast.py", line 295, in generic_visit
    for field, old_value in iter_fields(node):
  File "/usr/lib/python3.5/ast.py", line 170, in iter_fields
    for field in node._fields:
AttributeError: 'bytes' object has no attribute '_fields'

I see that at File "/home/marko/workspace/pqryopen/icontract/venv3/lib/python3.5/site-packages/meta/decompiler/expression_mutator.py", line 39, in generic_visit you don't check for bytes, only for strings:

class ExpressionMutator(NodeTransformer):
    def visit_If(self, node):

        assert len(node.body) == 1

        assert len(node.orelse) == 1

        test = self.visit(node.test)
        then = self.visit(node.body[0])
        else_ = self.visit(node.orelse[0])

        if_exp = _ast.IfExp(test, then, else_, lineno=node.lineno, col_offset=0)
        return if_exp

    def visit_Return(self, node):
        return NodeTransformer.generic_visit(self, node.value)

    def visit_FunctionDef(self, node):
        return node

    def generic_visit(self, node):
        if node is None:
            return node
        if isinstance(node, (str)):
            import pdb;pdb.set_trace()
            return node

#        if not isinstance(node, (_ast.expr, _ast.expr_context, _ast.slice, _ast.operator, _ast.boolop)):
#            raise Exception("expected a Python '_ast.expr' node (got %r)" % (type(node),))
        return NodeTransformer.generic_visit(self, node)

This is a bit of a blocker for us since we need meta in our library icontract to impose contract validations (pre- and post-conditions).

I tried changing the generic_visit in expression mutator to:

    def generic_visit(self, node):
        if node is None:
            return node
        if isinstance(node, str):
            return _ast.Str(node)
        if isinstance(node, bytes):
            return _ast.Bytes(node)

        return NodeTransformer.generic_visit(self, node)

And that seems to work.

I'd like to fix the issue and make a pull request, but before I go forward with it, could you please explain me:

  • Why is pdb locally imported followed by a call pdb.set_trace()?
  • Why are strings returned as strings and not as _ast.Str?

Thaks for looking into this!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions