diff --git a/burger/toppings/packetinstructions.py b/burger/toppings/packetinstructions.py index f3fedd7..200dde6 100644 --- a/burger/toppings/packetinstructions.py +++ b/burger/toppings/packetinstructions.py @@ -336,6 +336,26 @@ def operations(classloader, classname, classes, verbose, args=None, operations.append(Operation(instruction.pos, "write", type="nbtcompound", field=field)) + elif arg_type == "java/util/Collection" and \ + descriptor.args[1].name == "java/util/function/BiConsumer": + # Loop that calls the consumer with the packetbuffer + # and value for each value in collection + operations.append(Operation(instruction.pos, "store", + type="Iterator", + var="it", + value=field.value + ".iterator()")) + # TODO: inline the referenced function - would require + # changing how stringify_invokedynamic works + target = arguments[1][:arguments[1].find(":")] + name2 = arguments[1][arguments[1].rfind(":")+1:] + operations.append(Operation(instruction.pos, "loop", + condition="it.hasNext()")) + operations.append(Operation(instruction.pos, "interfacecall", + type="dynamic", + target=target, name=name, + method=name, field=target, + args="buf, it.next()")) + operations.append(Operation(instruction.pos, "endloop")) else: raise Exception("Unexpected descriptor " + desc) else: @@ -400,7 +420,7 @@ def operations(classloader, classname, classes, verbose, args=None, break elif mnemonic == "invokedynamic": - stack.append(stringify_invokedynamic(stack.pop(), instruction, cf)) + stack.append(stringify_invokedynamic(stack, instruction, cf)) # Conditional statements and loops elif mnemonic.startswith("if"): diff --git a/burger/util.py b/burger/util.py index 8920a2d..51723aa 100644 --- a/burger/util.py +++ b/burger/util.py @@ -45,11 +45,12 @@ def class_from_invokedynamic(ins, cf): assert desc.returns.name != "void" return desc.returns.name -def stringify_invokedynamic(obj, ins, cf): +def stringify_invokedynamic(stack, ins, cf): """ Converts an invokedynamic instruction into a string. - This is a rather limited implementation for now, only handling obj::method. + This is a rather limited implementation for now, only handling obj::method + and Class::method. """ const = cf.constants[ins.operands[0].value] # Hack due to packetinstructions not expanding constants bootstrap = cf.bootstrap_methods[const.method_attr_index] @@ -61,10 +62,18 @@ def stringify_invokedynamic(obj, ins, cf): assert len(bootstrap.bootstrap_args) == 3 # Num arguments # Actual implementation. methodhandle = cf.constants.get(bootstrap.bootstrap_args[1]) - if methodhandle.reference_kind == 7: # REF_invokeSpecial - return "%s::%s" % (obj, methodhandle.reference.name_and_type.name.value) + if methodhandle.reference_kind == 5: # REF_invokeVirtual + target = stack.pop() + name = methodhandle.reference.name_and_type.name.value + elif methodhandle.reference_kind == 6: # REF_invokeStatic + target = methodhandle.reference.class_.name.value + name = methodhandle.reference.name_and_type.name.value + elif methodhandle.reference_kind == 7: # REF_invokeSpecial + target = stack.pop() + name = methodhandle.reference.name_and_type.name.value else: raise Exception("Unhandled reference_kind %d" % methodhandle.reference_kind) + return "%s::%s" % (target, name) def try_eval_lambda(ins, args, cf): """