Skip to content

Commit

Permalink
Merge pull request #121 from d0c-s4vage/hotfix/120-integer_promotion
Browse files Browse the repository at this point in the history
Target types for integer promotion are now propagated correctly
  • Loading branch information
d0c-s4vage authored Jan 20, 2020
2 parents 7aa2577 + c73cc52 commit 0aa14fa
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 28 deletions.
12 changes: 12 additions & 0 deletions pfp/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,12 @@ def __init__(self, stream=None, metadata_processor=None):
if stream is not None:
self._pfp__parse(stream, save_offset=True)

def _pfp__get_class(self):
"""Return the class for this field. This would be used for things like
integer promotion and type casting.
"""
return self.__class__

# see #51 - fields should have a method of returning the full path of its name
def _pfp__path(self):
"""Return the full pathname of this field. E.g. given
Expand Down Expand Up @@ -678,6 +684,12 @@ def __init__(self, last_field, implicit_array):
super(Field, self).__setattr__("last_field", last_field)
super(Field, self).__setattr__("implicit_array", implicit_array)

def _pfp__get_class(self):
"""Return the last field class type and not the array type. Overrides
:any:`Field._pfp__get_class`.
"""
return self.last_field._pfp__get_class()

def __setattr__(self, name, value):
"""Custom setattr that forwards all sets to the last_field
"""
Expand Down
71 changes: 63 additions & 8 deletions pfp/interp.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ def push(self, new_scope=None):
"""
if new_scope is None:
new_scope = {"types": {}, "vars": {}}
new_scope = {"types": {}, "vars": {}, "meta": {}}
self._curr_scope = new_scope
self._dlog("pushing new scope, scope level = {}".format(self.level()))
self._scope_stack.append(self._curr_scope)
Expand Down Expand Up @@ -428,6 +428,34 @@ def pop(self):
self._dlog("popping scope, scope level = {}".format(self.level()))
self._curr_scope = self._scope_stack[-1]
return res

def clear_meta(self):
"""Clear metadata about the current statement
"""
self._curr_scope["meta"] = {}

def push_meta(self, meta_name, meta_value):
"""Push metadata about the current statement onto the metadata stack
for the current statement. Mostly used for tracking integer promotion
and casting types
"""
self._dlog("adding metadata '{}'".format(meta_name))
self._curr_scope["meta"].setdefault(meta_name, []).append(meta_value)

def get_meta(self, meta_name):
"""Get the current meta value named ``meta_name``
"""
self._dlog("getting metadata '{}'".format(meta_name))
return self._curr_scope["meta"].get(meta_name, [None])[-1]

def pop_meta(self, name):
"""Pop metadata about the current statement from the metadata stack
for the current statement.
:name: The name of the metadata
"""
self._dlog("getting meta '{}'".format(name))
return self._curr_scope["meta"][name].pop()

def add_var(self, field_name, field, root=False):
"""Add a var to the current scope (vars are fields that
Expand Down Expand Up @@ -455,7 +483,6 @@ def get_var(self, name, recurse=True):
:name: The name of the id
:recurse: Whether parent scopes should also be searched (defaults to True)
:returns: TODO
"""
self._dlog("getting var '{}'".format(name))
return self._search("vars", name, recurse)
Expand Down Expand Up @@ -853,7 +880,7 @@ def eval(self, statement, ctxt=None):
res = None
for child in ast.children():
res = self._handle_node(
child, self._scope, self._ctxt, self._stream
child, self._scope, self._ctxt, self._stream,
)
return res
except errors.InterpReturn as e:
Expand Down Expand Up @@ -1139,6 +1166,7 @@ def _handle_file_ast(self, node, scope, ctxt, stream):
and not is_forward_declared_struct(child):
continue
self._handle_node(child, scope, ctxt, stream)
scope.clear_meta()

for child in children:
if type(child) is tuple:
Expand Down Expand Up @@ -1176,9 +1204,13 @@ def _handle_cast(self, node, scope, ctxt, stream):
"""
self._dlog("handling cast")
to_type = self._handle_node(node.to_type, scope, ctxt, stream)

scope.push_meta("dest_type", to_type)
val_to_cast = self._handle_node(node.expr, scope, ctxt, stream)
scope.pop_meta("dest_type")

res = to_type()

res._pfp__set_value(val_to_cast)
return res

Expand Down Expand Up @@ -1584,6 +1616,7 @@ def _handle_union_decls(self, node, scope, ctxt, stream):
max_pos = 0
for decl in node.decls:
self._handle_node(decl, scope, ctxt, stream)
scope.clear_meta()

finally:
# the union will have reset the stream
Expand Down Expand Up @@ -1666,6 +1699,7 @@ def _handle_struct_decls(self, node, scope, ctxt, stream):
for decl in node.decls:
# new context! (struct)
self._handle_node(decl, scope, ctxt, stream)
scope.clear_meta()

ctxt._pfp__process_fields_metadata()

Expand Down Expand Up @@ -1821,18 +1855,29 @@ def _handle_binary_op(self, node, scope, ctxt, stream):
"%": lambda x, y: x % y,
">": lambda x, y: x > y,
"<": lambda x, y: x < y,
"||": lambda x, y: x or y,
"||": lambda x, y: 1 if x or y else 0,
">=": lambda x, y: x >= y,
"<=": lambda x, y: x <= y,
"==": lambda x, y: x == y,
"!=": lambda x, y: x != y,
"&&": lambda x, y: x and y,
"&&": lambda x, y: 1 if x and y else 0,
">>": lambda x, y: x >> y,
"<<": lambda x, y: x << y,
}

dest_type = scope.get_meta("dest_type")

left_val = self._handle_node(node.left, scope, ctxt, stream)
if dest_type is not None and not isinstance(left_val, dest_type):
new_left_val = dest_type()
new_left_val._pfp__set_value(left_val)
left_val = new_left_val

right_val = self._handle_node(node.right, scope, ctxt, stream)
if dest_type is not None and not isinstance(right_val, dest_type):
new_right_val = dest_type()
new_right_val._pfp__set_value(right_val)
right_val = new_right_val

if node.op not in switch:
raise errors.UnsupportedBinaryOperator(node.coord, node.op)
Expand Down Expand Up @@ -2057,10 +2102,20 @@ def assign_op(x, y):
"=": assign_op,
}

scope.clear_meta()

self._dlog("handling assignment")
field = self._handle_node(node.lvalue, scope, ctxt, stream)
self._dlog("field = {}".format(field))
value = self._handle_node(node.rvalue, scope, ctxt, stream)

scope.push_meta("dest_type", field._pfp__get_class())

value = self._handle_node(
node.rvalue,
scope,
ctxt,
stream,
)

if node.op is None:
self._dlog("value = {}".format(value))
Expand Down Expand Up @@ -2185,6 +2240,7 @@ def _handle_compound(self, node, scope, ctxt, stream):

try:
for child in node.children():
scope.clear_meta()
self._handle_node(child, scope, ctxt, stream)

# in case a return occurs, be sure to pop the scope
Expand Down Expand Up @@ -2398,7 +2454,7 @@ def _handle_do_while(self, node, scope, ctxt, stream):
except errors.InterpContinue as e:
pass
if node.cond is not None and not self._handle_node(
node.cond, scope, ctxt, stream
node.cond, scope, ctxt, stream,
):
break

Expand Down Expand Up @@ -2594,7 +2650,6 @@ def _get_value(self, node, scope, ctxt, stream):
:returns: TODO
"""

res = self._handle_node(node, scope, ctxt, stream)

if isinstance(res, fields.Field):
Expand Down
1 change: 0 additions & 1 deletion tests/test_arrays.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,6 @@ def test_array_overwrite_fetch(self):
uint array[1];
} TestStruct;
""",
verify=False,
)
struct = dom.TestStruct()
struct.array = [0xFFFF]
Expand Down
Loading

0 comments on commit 0aa14fa

Please sign in to comment.