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.

120 lines
3.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. co = func.func_code
  12. code = co.co_code
  13. n = len(code)
  14. i = 0
  15. lastop = MARKOV_START
  16. while i < n:
  17. c = code[i]
  18. op = ord(c)
  19. i += 1
  20. if op >= opcode.HAVE_ARGUMENT:
  21. oparg = ord(code[i]) + ord(code[i + 1]) * 256
  22. i += 2
  23. if op in opcode.hasconst:
  24. arg = co.co_consts[oparg]
  25. elif op in opcode.haslocal:
  26. arg = co.co_varnames[oparg]
  27. else:
  28. raise NotImplementedError(op, opcode.opname[op])
  29. else:
  30. arg = None
  31. _chain_append(chain, lastop, (op, arg))
  32. lastop = op
  33. _chain_append(chain, op, MARKOV_END)
  34. return chain
  35. def make_function(chain, name, argcount=1):
  36. codes, constants, varnames = _make_codes(chain)
  37. nlocals = len(varnames) + argcount
  38. stacksize = 1024 # High limit?
  39. flags = 0 # Denotes funcs with *args and/or **kwargs; nothing for now
  40. codestring = "".join([chr(code) for code in codes])
  41. names = ()
  42. filename = "<smash>"
  43. firstlineno = 1
  44. lnotab = ""
  45. code = types.CodeType(argcount, nlocals, stacksize, flags, codestring,
  46. constants, names, varnames, filename, name,
  47. firstlineno, lnotab)
  48. func = types.FunctionType(code, globals(), name)
  49. return func
  50. def _chain_append(chain, first, second):
  51. try:
  52. chain[first].append(second)
  53. except KeyError:
  54. chain[first] = [second]
  55. def _make_codes(chain):
  56. codes = []
  57. code = random.choice(chain[MARKOV_START])
  58. constants, varnames = [], []
  59. while 1:
  60. if code == MARKOV_END:
  61. break
  62. op, arg = code
  63. codes.append(op)
  64. if op >= opcode.HAVE_ARGUMENT:
  65. if op in opcode.hasconst:
  66. args = constants
  67. elif op in opcode.haslocal:
  68. args = varnames
  69. else:
  70. raise NotImplementedError(op, opcode.opname[op])
  71. if arg not in args:
  72. args.append(arg)
  73. codes.append(args.index(arg))
  74. codes.append(0)
  75. code = random.choice(chain[op])
  76. return codes, tuple(constants), tuple(varnames)
  77. ############## FUNCTION CORPUS ################################################
  78. def f1(a):
  79. b = a + 7.0
  80. return b
  81. def f2(a):
  82. b = a - 5.0
  83. return b
  84. def f3(a):
  85. b = a * 3.0
  86. return b
  87. def f4(a):
  88. b = a / 2.0
  89. return b
  90. corpus = [f1, f2, f3, f4]
  91. ############## DEMO ###########################################################
  92. if __name__ == "__main__":
  93. print "USING FUNCTION CORPUS:", [func.__name__ for func in corpus]
  94. chain = make_chain(corpus)
  95. print "OPCODE MARKOV CHAIN:", chain
  96. print
  97. func = make_function(chain, "func")
  98. print "SMASHED FUNCTION CODE:", [ord(c) for c in func.func_code.co_code]
  99. print "FUNCTION DISASSEMBLY:"
  100. dis.dis(func)
  101. print
  102. n = 12.0
  103. print "func(%s) =" % n, func(n)