Skip to content

Commit

Permalink
Add bitfields to metadata (#20)
Browse files Browse the repository at this point in the history
Add bitfield extraction.  Closes #17.
  • Loading branch information
mat-1 authored and Pokechu22 committed Feb 17, 2022
1 parent 2b1b148 commit 15fbe0c
Showing 1 changed file with 78 additions and 3 deletions.
81 changes: 78 additions & 3 deletions burger/toppings/entitymetadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ def act(aggregate, classloader, verbose=False):
entity_classes = {e["class"]: e["name"] for e in six.itervalues(entities)}
parent_by_class = {}
metadata_by_class = {}
bitfields_by_class = {}

# this flag is shared among all entities
# getSharedFlag is currently the only method in Entity with those specific args and returns, this may change in the future! (hopefully not)
shared_get_flag_method = base_entity_cf.methods.find_one(args="I", returns="Z").name.value

def fill_class(cls):
# Returns the starting index for metadata in subclasses of cls
Expand Down Expand Up @@ -231,8 +236,76 @@ def on_new(self, ins, const):
elif cls == base_entity_class:
walk_method(cf, cf.methods.find_one(name="<init>"), MetadataDefaultsContext(True), verbose)

metadata_by_class[cls] = metadata
get_flag_method = None

# find if the class has a `boolean getFlag(int)` method
for method in cf.methods.find(args="I", returns="Z"):
previous_operators = []
for ins in method.code.disassemble():
if ins.mnemonic == "bipush":
# check for a series of operators that looks something like this
# `return ((Byte)this.R.a(bo) & var1) != 0;`
operator_matcher = ["aload", "getfield", "getstatic", "invokevirtual", "checkcast", "invokevirtual", "iload", "iand", "ifeq", "bipush", "goto"]
previous_operators_match = previous_operators == operator_matcher

if previous_operators_match and ins.operands[0].value == 0:
# store the method name as the result for later
get_flag_method = method.name.value

previous_operators.append(ins.mnemonic)

bitfields = []

# find the methods that get bit fields
for method in cf.methods.find(args="", returns="Z"):
if method.code:
bitmask_value = None
stack = []
for ins in method.code.disassemble():
# the method calls getField() or getSharedField()
if ins.mnemonic in ("invokevirtual", "invokespecial", "invokeinterface", "invokestatic"):
calling_method = ins.operands[0].name_and_type.name.value

has_correct_arguments = ins.operands[0].name_and_type.descriptor.value == "(I)Z"

is_getflag_method = has_correct_arguments and calling_method == get_flag_method
is_shared_getflag_method = has_correct_arguments and calling_method == shared_get_flag_method

# if it's a shared flag, update the bitfields_by_class for abstract_entity
if is_shared_getflag_method and stack:
bitmask_value = stack.pop()
if bitmask_value is not None:
base_entity_cls = base_entity_cf.this.name.value
if base_entity_cls not in bitfields_by_class:
bitfields_by_class[base_entity_cls] = []
bitfields_by_class[base_entity_cls].append({
# we include the class here so it can be easily figured out from the mappings
"class": cls,
"method": method.name.value,
"mask": 1 << bitmask_value
})
bitmask_value = None
elif is_getflag_method and stack:
bitmask_value = stack.pop()
break
elif ins.mnemonic == "iand":
# get the last item in the stack, since it's the bitmask
bitmask_value = stack[-1]
break
elif ins.mnemonic == "bipush":
stack.append(ins.operands[0].value)
if bitmask_value:
bitfields.append({
"method": method.name.value,
"mask": bitmask_value
})


metadata_by_class[cls] = metadata
if cls not in bitfields_by_class:
bitfields_by_class[cls] = bitfields
else:
bitfields_by_class[cls].extend(bitfields)
return index

for cls in six.iterkeys(entity_classes):
Expand All @@ -245,7 +318,8 @@ def on_new(self, ins, const):
if metadata_by_class[cls]:
metadata.append({
"class": cls,
"data": metadata_by_class[cls]
"data": metadata_by_class[cls],
"bitfields": bitfields_by_class[cls]
})

cls = parent_by_class[cls]
Expand All @@ -254,7 +328,8 @@ def on_new(self, ins, const):
if metadata_by_class[cls]:
metadata.insert(0, {
"class": cls,
"data": metadata_by_class[cls]
"data": metadata_by_class[cls],
"bitfields": bitfields_by_class[cls]
})
cls = parent_by_class[cls]

Expand Down

0 comments on commit 15fbe0c

Please sign in to comment.