Generates random Python functions using Markov chains
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

220 lines
7.4 KiB

  1. import opcode
  2. import sys
  3. import func_smash
  4. OP_LOAD = {"LOAD_CONST": True, "LOAD_FAST": False, "LOAD_GLOBAL": False}
  5. OP_BINARY = {"BINARY_POWER": "**", "BINARY_MULTIPLY": "*",
  6. "BINARY_DIVIDE": "/", "BINARY_MODULO": "%", "BINARY_ADD": "+",
  7. "BINARY_SUBTRACT": "-", "BINARY_FLOOR_DIVIDE": "//",
  8. "BINARY_TRUE_DIVIDE": "/", "BINARY_LSHIFT": "<<",
  9. "BINARY_RSHIFT": ">>", "BINARY_AND": "&", "BINARY_XOR": "^",
  10. "BINARY_OR": "|"}
  11. OP_INPLACE = {"INPLACE_FLOOR_DIVIDE": "//", "INPLACE_TRUE_DIVIDE": "/",
  12. "INPLACE_ADD": "+", "INPLACE_SUBTRACT": "-",
  13. "INPLACE_MULTIPLY": "*", "INPLACE_DIVIDE": "/",
  14. "INPLACE_MODULO": "%", "INPLACE_POWER": "**",
  15. "INPLACE_LSHIFT": "<<", "INPLACE_RSHIFT": ">>",
  16. "INPLACE_AND": "&", "INPLACE_XOR": "^", "INPLACE_OR": "|"}
  17. OP_SUBSCR = ("BINARY_SUBSCR", "INPLACE_SUBSCR")
  18. OP_BUILD = {"BUILD_TUPLE": ("(", ")"), "BUILD_LIST": ("[", "]"),
  19. "BUILD_SET": ("{", "}")}
  20. TAB = 4
  21. def prettify_function(func, indent=0):
  22. args = _get_func_args(func)
  23. _print(indent, "def {0}({1}):".format(func.func_name, args))
  24. prettify_code(func.__code__, indent=indent+TAB)
  25. def prettify_code(codeobj, indent=0):
  26. codes = []
  27. codestring = codeobj.co_code
  28. length = len(codestring)
  29. i = 0
  30. while i < length:
  31. code = ord(codestring[i])
  32. i += 1
  33. if code >= opcode.HAVE_ARGUMENT:
  34. arg = func_smash._get_argument(codeobj, codestring, i, code)
  35. i += 2
  36. codes.append((code, arg))
  37. else:
  38. codes.append((code, None))
  39. skip_next_line = False
  40. lines = _parse_codestring(codes)
  41. for i, line in enumerate(lines):
  42. if skip_next_line:
  43. skip_next_line = False
  44. continue
  45. added_indent, code = line[0], line[1:]
  46. if code[0] == "else:" and lines[i+2][0] >= added_indent:
  47. # Remove the automatic "pass" after each "if" as it's not needed
  48. skip_next_line = True
  49. _print(indent + added_indent, *code)
  50. def _parse_codestring(codes):
  51. indent = 0
  52. lines = []
  53. stack = []
  54. print_buffer = []
  55. block_dedent_at = []
  56. block_else_at = []
  57. i = 0
  58. for instruction in codes:
  59. code, arg = instruction
  60. opname = opcode.opname[code]
  61. if code >= opcode.HAVE_ARGUMENT:
  62. i += 3
  63. else:
  64. i += 1
  65. for x in block_dedent_at:
  66. if i >= x:
  67. indent -= TAB
  68. block_dedent_at.remove(x)
  69. for x in block_else_at:
  70. if i >= x:
  71. lines.append((indent, "else:"))
  72. indent += TAB
  73. lines.append((indent, "pass"))
  74. block_else_at.remove(x)
  75. _print(indent, i, opname, arg, debug=True)
  76. if opname in OP_LOAD:
  77. _push(stack, arg, literal=OP_LOAD[opname])
  78. elif opname in OP_BINARY:
  79. tos, tos1 = _pop(stack), _pop(stack)
  80. _push(stack, " ".join((tos1, OP_BINARY[opname], tos)))
  81. elif opname in OP_INPLACE: # Works, but doesn't use inplace op magic
  82. tos, tos1 = _pop(stack), _pop(stack)
  83. _push(stack, " ".join((tos1, OP_INPLACE[opname], tos)))
  84. elif opname in OP_SUBSCR:
  85. tos, tos1 = _pop(stack), _pop(stack)
  86. _push(stack, "{0}[{1}]".format(tos1, tos))
  87. elif opname in OP_BUILD:
  88. args = []
  89. for i in xrange(arg):
  90. args.append(_pop(stack))
  91. args.reverse()
  92. start, end = OP_BUILD[opname]
  93. _push(stack, start + ", ".join(args) + end)
  94. elif opname == "BUILD_MAP":
  95. _push(stack, "{}")
  96. elif opname == "STORE_FAST":
  97. lines.append((indent, arg, "=", _pop(stack)))
  98. elif opname == "STORE_MAP":
  99. key, value = _pop(stack), _pop(stack)
  100. pair = ": ".join((key, value))
  101. oldmap = _pop(stack)
  102. if oldmap == "{}":
  103. newmap = "{" + pair + "}"
  104. else:
  105. newmap = oldmap[:-1] + ", " + pair + "}"
  106. _push(stack, newmap)
  107. elif opname == "LOAD_ATTR":
  108. tos = _pop(stack)
  109. new_tos = tos + "." + arg
  110. _push(stack, new_tos)
  111. elif opname == "POP_TOP":
  112. lines.append((indent, _pop(stack)))
  113. elif opname == "CALL_FUNCTION":
  114. numargs, numkwargs = arg
  115. args = []
  116. for i in xrange(numkwargs):
  117. value = _pop(stack)
  118. key = _pop(stack, never_literal=True)
  119. args.append("=".join((key, value)))
  120. for i in xrange(numargs):
  121. args.append(_pop(stack))
  122. args.reverse()
  123. funcname = _pop(stack)
  124. _push(stack, "{0}({1})".format(funcname, ", ".join(args)))
  125. elif opname == "PRINT_ITEM":
  126. print_buffer.append(_pop(stack))
  127. elif opname == "PRINT_NEWLINE":
  128. lines.append((indent, "print", ", ".join(print_buffer)))
  129. print_buffer = []
  130. elif opname == "RETURN_VALUE":
  131. lines.append((indent, "return", _pop(stack)))
  132. elif opname == "COMPARE_OP":
  133. tos, tos1 = _pop(stack), _pop(stack)
  134. compare = " ".join((tos1, arg, tos))
  135. _push(stack, compare)
  136. elif opname == "POP_JUMP_IF_FALSE":
  137. test = _pop(stack)
  138. block_dedent_at.append(arg)
  139. block_else_at.append(arg)
  140. lines.append((indent, "if {0}:".format(test)))
  141. indent += TAB
  142. lines.append((indent, "pass".format(test)))
  143. elif opname == "JUMP_ABSOLUTE":
  144. block_dedent_at.append(i)
  145. elif opname == "JUMP_FORWARD":
  146. block_dedent_at.append(i + arg)
  147. else:
  148. raise NotImplementedError(opname, arg, stack)
  149. return lines
  150. def _get_func_args(func):
  151. codeobj = func.__code__
  152. count = codeobj.co_argcount
  153. if count == 0:
  154. return ""
  155. return ", ".join([arg for arg in codeobj.co_varnames[:count]])
  156. def _push(stack, item, literal=False):
  157. stack.append((item, literal))
  158. def _pop(stack, never_literal=False):
  159. item, literal = stack.pop()
  160. return repr(item) if literal and not never_literal else item
  161. def _print(indentation, *args, **kwargs):
  162. argstring = " ".join([str(arg) for arg in args if str(arg)])
  163. if kwargs.get("debug"):
  164. # Ignore debug messages without -d flag
  165. if len(sys.argv) > 1 and sys.argv[1] == "-d":
  166. print " " * 50 + "#", argstring
  167. return
  168. print " " * indentation + argstring
  169. if __name__ == "__main__":
  170. def f1(a):
  171. b = a + 10 / 7.0
  172. d = long_func(a, b+9, c=42, d="43")
  173. print b, d
  174. ex_tuple = ("Hello", "world!", abcdef)
  175. ex_list = [1, 2, 3] * 3 + [4, 5, 6]
  176. ex_set = {99, 98, 97, 96, 95}
  177. ex_dict = {d: e, f: False, "testing": g}
  178. return ex_dict
  179. def f2(a, b, c):
  180. if cmp1:
  181. line1
  182. if cmp2:
  183. line2
  184. elif cmp3:
  185. line3
  186. elif cmp4:
  187. line4
  188. else:
  189. line5
  190. line6
  191. else:
  192. line7
  193. line8
  194. if cmp4:
  195. line9
  196. if cmp5:
  197. if cmp6:
  198. if cmp7:
  199. if cmp8:
  200. if cmp9:
  201. line10
  202. return line11
  203. prettify_function(f1)
  204. print
  205. prettify_function(f2)