def _build_table_mass_assignment(state, addr, instruction): assignment = nodes.Assignment() base = instruction.A destination = nodes.TableElement() destination.key = nodes.MULTRES() destination.table = _build_slot(state, addr, base - 1) assignment.destinations.contents = [destination] assignment.expressions.contents = [nodes.MULTRES()] return assignment
def _build_call(state, addr, instruction): call = nodes.FunctionCall() call.function = _build_slot(state, addr, instruction.A) call.arguments.contents = _build_call_arguments(state, addr, instruction) line_marked = [call] if instruction.opcode <= ins.CALL.opcode: if instruction.B == 0: node = nodes.Assignment() node.destinations.contents.append(nodes.MULTRES()) node.expressions.contents.append(call) elif instruction.B == 1: node = call else: from_slot = instruction.A to_slot = instruction.A + instruction.B - 2 node = _build_range_assignment(state, addr, from_slot, to_slot) node.expressions.contents.append(call) else: assert instruction.opcode <= ins.CALLT.opcode node = nodes.Return() node.returns.contents.append(call) line_marked.append(node) return node, line_marked
def _build_call_arguments(state, addr, instruction): base = instruction.A last_argument_slot = base + instruction.CD is_variadic = (instruction.opcode == ins.CALLM.opcode or instruction.opcode == ins.CALLMT.opcode) if not is_variadic: last_argument_slot -= 1 arguments = [] slot = base + 1 # LJ_FR2 flag, required for 64-bit LuaJIT # To the best of my knowledge, with this flag enabled there is a empty space on the # stack between the function and the arguments - when normally you would push the function # onto the stack followed by arguments, like so: # # /---------- # | 0. FUNCTION OBJ # | 1. ARG1 # | 2. ARG2 # \---------- # # With the flag enabled, it would go like so # # /---------- # | 0. FUNCTION OBJ # | 1. <unused, reserved for runtime> # | 2. ARG1 # | 3. ARG2 # \---------- # # And thus we have to skip that bit # # (if you're curious about why it does this - it's to allow LuaJIT to store some pointers in the space # usually used by the stack contents. See https://github.com/LuaJIT/LuaJIT/issues/25#issuecomment-183660706 for # more information) if state.header.flags.fr2: slot += 1 last_argument_slot += 1 while slot <= last_argument_slot: argument = _build_slot(state, addr, slot) arguments.append(argument) slot += 1 if is_variadic: arguments.append(nodes.MULTRES()) return arguments
def _build_vararg(state, addr, instruction): base = instruction.A last_slot = base + instruction.B - 2 if last_slot < base: node = nodes.Assignment() node.destinations.contents.append(nodes.MULTRES()) node.expressions.contents.append(nodes.Vararg()) else: node = _build_range_assignment(state, addr, base, last_slot) node.expressions.contents.append(nodes.Vararg()) return node
def _build_return(state, addr, instruction): node = nodes.Return() base = instruction.A last_slot = base + instruction.CD - 1 if instruction.opcode != ins.RETM.opcode: last_slot -= 1 slot = base # Negative count for the RETM is OK while slot <= last_slot: variable = _build_slot(state, addr, slot) node.returns.contents.append(variable) slot += 1 if instruction.opcode == ins.RETM.opcode: node.returns.contents.append(nodes.MULTRES()) return node
def _build_call_arguments(state, addr, instruction): base = instruction.A last_argument_slot = base + instruction.CD is_variadic = (instruction.opcode == ins.CALLM.opcode or instruction.opcode == ins.CALLMT.opcode) if not is_variadic: last_argument_slot -= 1 arguments = [] slot = base + 1 while slot <= last_argument_slot: argument = _build_slot(state, addr, slot) arguments.append(argument) slot += 1 if is_variadic: arguments.append(nodes.MULTRES()) return arguments
def _build_call(state, addr, instruction): call = nodes.FunctionCall() call.function = _build_slot(state, addr, instruction.A) call.arguments.contents = _build_call_arguments(state, addr, instruction) call.name = _lookup_variable_name(state, addr, instruction.A) if gconfig.gVerbose: print("_build_call " + call.name) # CALLM Call: A, ..., A+B-2 = A(A+1, ..., A+C+MULTRES) # CALL Call: A, ..., A+B-2 = A(A+1, ..., A+C-1) if instruction.opcode <= ins.CALL.opcode: if instruction.B == 0: node = nodes.Assignment() node.destinations.contents.append(nodes.MULTRES()) node.expressions.contents.append(call) elif instruction.B == 1: node = call # elif call.name and call.name == "class" and instruction.B == 2: # class_name = _lookup_variable_name(state, addr, instruction.A+1) # print("class " + class_name) # assert class_name is not None # node = _build_class_assignment(addr, instruction.A, class_name) # node.expressions.contents.append(call) else: from_slot = instruction.A to_slot = instruction.A + instruction.B - 2 node = _build_range_assignment(state, addr, from_slot, to_slot) node.expressions.contents.append(call) # CALLMT Tailcall: return A(A+1, ..., A+D+MULTRES) # CALLT Tailcall: return A(A+1, ..., A+D-1) else: assert instruction.opcode <= ins.CALLT.opcode node = nodes.Return() node.returns.contents.append(call) return node