mirror of
https://github.com/bikini/patchwork
synced 2026-06-27 08:08:41 +00:00
43 lines
1.4 KiB
Python
43 lines
1.4 KiB
Python
from __future__ import annotations
|
|
import ast
|
|
import random
|
|
|
|
class OpaquePredicateInjector(ast.NodeTransformer):
|
|
|
|
def __init__(self, rng: random.Random, seed_name: str, prob: float=0.5):
|
|
self.rng = rng
|
|
self.seed_name = seed_name
|
|
self.prob = prob
|
|
self.injected = False
|
|
|
|
def visit_If(self, node: ast.If) -> ast.AST:
|
|
self.generic_visit(node)
|
|
if self.rng.random() < self.prob:
|
|
node.test = self._wrap(node.test)
|
|
self.injected = True
|
|
return node
|
|
|
|
def visit_While(self, node: ast.While) -> ast.AST:
|
|
self.generic_visit(node)
|
|
if self.rng.random() < self.prob * 0.4:
|
|
node.test = self._wrap(node.test)
|
|
self.injected = True
|
|
return node
|
|
|
|
def _wrap(self, test: ast.expr) -> ast.expr:
|
|
return ast.BoolOp(op=ast.And(), values=[test, self._tautology()])
|
|
|
|
def _tautology(self) -> ast.expr:
|
|
s = self.seed_name
|
|
kind = self.rng.choice(('parity', 'flt3', 'flt5'))
|
|
if kind == 'parity':
|
|
src = f'({s} * ({s} + 1)) % 2 == 0'
|
|
elif kind == 'flt3':
|
|
src = f'({s} * {s} * {s} - {s}) % 3 == 0'
|
|
else:
|
|
src = f'({s} * {s} * {s} * {s} * {s} - {s}) % 5 == 0'
|
|
return ast.parse(src, mode='eval').body
|
|
|
|
def build_seed_stmt(seed_name: str) -> ast.stmt:
|
|
return ast.parse(f'{seed_name} = id(int) & 0xFFFFFFFF').body[0]
|