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.

153 lines
4.2 KiB

  1. import dis
  2. import opcode
  3. import random
  4. import types
  5. ############## CODE ###########################################################
  6. MARKOV_START = -1
  7. MARKOV_END = -2
  8. def make_chain(funcs):
  9. chain = {}
  10. for func in funcs:
  11. _disassemble_func(func, chain)
  12. return chain
  13. def make_function(chain, name, argcount=1):
  14. codes, constants, varnames = _make_codes(chain)
  15. nlocals = len(varnames) + argcount
  16. stacksize = 1024 # High limit?
  17. flags = 0 # Denotes funcs with *args and/or **kwargs; nothing for now
  18. codestring = "".join([chr(code) for code in codes])
  19. names = ()
  20. filename = "<smash>"
  21. firstlineno = 1
  22. lnotab = ""
  23. code = types.CodeType(argcount, nlocals, stacksize, flags, codestring,
  24. constants, names, varnames, filename, name,
  25. firstlineno, lnotab)
  26. func = types.FunctionType(code, globals(), name)
  27. return func
  28. def print_chain(chain):
  29. print "{"
  30. for key in sorted(chain.keys()):
  31. op = _int_to_opname(key)
  32. targets = {}
  33. for op2 in chain[key]:
  34. target = _int_to_opname(op2[0])
  35. if op2[0] >= opcode.HAVE_ARGUMENT:
  36. target = "{0}({1})".format(target, op2[1])
  37. try:
  38. targets[target] += 1
  39. except KeyError:
  40. targets[target] = 1
  41. targs = [t if targets[t] == 1 else "{0}x {1}".format(targets[t], t) for t in targets]
  42. targs.sort()
  43. tstring = ", ".join(targs)
  44. print op.rjust(20), "=> [{0}]".format(tstring)
  45. print "}"
  46. def _disassemble_func(func, chain):
  47. co = func.__code__
  48. code = co.co_code
  49. n = len(code)
  50. i = 0
  51. lastop = MARKOV_START
  52. while i < n:
  53. op = ord(code[i])
  54. i += 1
  55. if op >= opcode.HAVE_ARGUMENT:
  56. oparg = ord(code[i]) + ord(code[i + 1]) * 256
  57. i += 2
  58. if op in opcode.hasconst:
  59. arg = co.co_consts[oparg]
  60. elif op in opcode.haslocal:
  61. arg = co.co_varnames[oparg]
  62. elif op in opcode.hascompare:
  63. arg = opcode.cmp_op[oparg]
  64. else:
  65. raise NotImplementedError(op, opcode.opname[op])
  66. else:
  67. arg = None
  68. _chain_append(chain, lastop, (op, arg))
  69. lastop = op
  70. _chain_append(chain, op, (MARKOV_END, None))
  71. def _chain_append(chain, first, second):
  72. try:
  73. chain[first].append(second)
  74. except KeyError:
  75. chain[first] = [second]
  76. def _make_codes(chain):
  77. codes = []
  78. code = random.choice(chain[MARKOV_START])
  79. constants, varnames = [], []
  80. while 1:
  81. op, arg = code
  82. if op == MARKOV_END:
  83. break
  84. codes.append(op)
  85. if op >= opcode.HAVE_ARGUMENT:
  86. if op in opcode.hasconst:
  87. if arg not in constants:
  88. constants.append(arg)
  89. args = constants
  90. elif op in opcode.haslocal:
  91. if arg not in varnames:
  92. varnames.append(arg)
  93. args = varnames
  94. elif op in opcode.hascompare:
  95. args = opcode.cmp_op
  96. else:
  97. raise NotImplementedError(op, opcode.opname[op])
  98. codes.append(args.index(arg))
  99. codes.append(0)
  100. code = random.choice(chain[op])
  101. return codes, tuple(constants), tuple(varnames)
  102. def _int_to_opname(op):
  103. if op == MARKOV_START:
  104. return "START"
  105. elif op == MARKOV_END:
  106. return "END"
  107. return opcode.opname[op]
  108. ############## FUNCTION CORPUS ################################################
  109. def f1(a):
  110. b = a + 7.0
  111. return b
  112. def f2(a):
  113. b = a - 5.0
  114. return b
  115. def f3(a):
  116. b = a * 3.0
  117. return b
  118. def f4(a):
  119. b = a / 2.0
  120. return b
  121. corpus = [f1, f2, f3, f4]
  122. ############## DEMO ###########################################################
  123. if __name__ == "__main__":
  124. print "USING FUNCTION CORPUS:", [func.__name__ for func in corpus]
  125. chain = make_chain(corpus)
  126. print
  127. func = make_function(chain, "func")
  128. print "SMASHED FUNCTION CODE:", [ord(c) for c in func.__code__.co_code]
  129. print "FUNCTION DISASSEMBLY:"
  130. dis.dis(func)
  131. print
  132. n = 12.0
  133. print "func(%s) =" % n, func(n)