Skip to content

byte constants are not decompiled correctly #39

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!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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