-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathsimsym.py
1675 lines (1412 loc) · 59.4 KB
/
simsym.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""Simple symbolic execution engine for Python."""
import sys
import os
import z3
import types
import collections
import inspect
import graph
class options(object):
# If set, equality tests eagerly simplify expressions that are
# structurally identical to a concrete True. This can produce
# much simpler equality expressions, especially when comparing
# large structures, but can sometimes turn what looks like a
# symbolic branch into a concrete branch, which can be confusing
# when tracing symbolic execution.
eq_eliminate_structural = True
# Monkey-patch __nonzero__ on Z3 types to make sure we don't
# accidentally call it instead of our wrappers.
def z3_nonzero(self):
raise RuntimeError("Cannot __nonzero__ a %s" % self.__class__)
z3.ExprRef.__nonzero__ = z3_nonzero
del z3_nonzero
# Patch in a __hash__ for SortRef, to make it possible to put
# sorts as dictionary keys.
def z3_sort_hash(self):
return hash(str(self))
z3.SortRef.__hash__ = z3_sort_hash
del z3_sort_hash
anon_info = ""
def gen_name(template=None):
"""Generate a variable name from a template.
If the template is None or ends with *, this generates a fresh
anonymous name containing the template before the *, the value of
anon_info, and a nonce.
"""
if template is None:
template = "*"
if template.endswith("*"):
name = "%s%s_anon%d" % (template[:-1], anon_info, Env.current().anon_idx)
Env.current().anon_idx += 1
return name
return template
class Constant(object):
def __init__(self, name):
self.__name = name
def __repr__(self):
return __name__ + "." + self.__name
MODEL_FETCH = Constant("MODEL_FETCH")
REALM_IGNORE = Constant("REALM_IGNORE")
class Symbolic(object):
"""Base class of symbolic types. Symbolic types come in two
groups: constant and mutable. Symbolic constants are deeply
immutable. Generally they are primitive types, such as integers
and booleans, but more complex types can also be constants (e.g.,
an immutable symbolic tuple of constants). Mutable symbolic
values are used for compound and container types like maps and
structs. Mutable values act like lvalues except when assigned,
where they are copied at the point of assignment.
A subclass of Symbolic must have a __z3_sort__ class field giving
the compound z3.SortRef for the value's type. Subclasses must
also implement the _z3_value and _wrap_lvalue methods.
"""
def __init__(self):
raise RuntimeError("%s cannot be constructed directly" % strtype(self))
@classmethod
def _z3_sort(cls):
"""Return the compound Z3 sort represented by this class."""
return cls.__z3_sort__
def _z3_value(self):
"""Return the compound Z3 value wrapped by this object.
For mutable objects, this should be its current value.
"""
raise NotImplementedError("_z3_value is abstract")
@classmethod
def _new_lvalue(cls, init, model):
"""Return a new instance of Symbolic with the given initial value,
which must be a compound Z3 value. The returned instance's
state will not be shared with any existing instances. model,
if not None, provides the simsym.Model in which to evaluate
concrete values or MODEL_FETCH to retrieve an existing
variable without binding it to a model.
"""
val = [init]
def setter(nval):
val[0] = nval
obj = cls._wrap_lvalue(lambda: val[0], setter, model)
if model is None and isinstance(obj, Symbolic):
obj._declare_assumptions(assume)
return obj
@classmethod
def _wrap_lvalue(cls, getter, setter, model):
"""Return a new instance of this class.
Return an instance of this class wrapping the compound Z3
value returned by getter. If this object is immutable, then
it should call getter immediately to fetch the object's
current value. If this object is mutable, it should use
getter on demand and it should reflect changes to its value by
calling setter with its updated compound Z3 value.
model, if not None, provides the simsym.Model in which to
evaluate concrete values or is MODEL_FETCH. Immutable,
non-compound objects should use this to evaluate their
concrete Python value and return this concrete value.
Mutable, compound objects should pass this model down to their
components.
"""
raise NotImplementedError("_wrap_lvalue is abstract")
@classmethod
def var(cls, name=None, model=None):
"""Return a symbolic variable of this type.
Initially, the variable's value will be unconstrained. It may
become constrained or even concrete as the program progresses.
If this method is called multiple times with the same name,
the returned instances will represent the same underlying
symbolic value (though the instances themselves will be
distinct).
If model is provided, it must be a simsym.Model or
MODEL_FETCH. If it is a Model, this symbolic variable will be
interpreted in this model to get a concrete Python value. If
it is MODEL_FETCH, a variable equivalent to the initial value
of the variable of the given name will be returned, but not
bound in any model.
"""
name = gen_name(name)
if model is None:
Env.current().var_constructors[name] = cls.var
def mkValue(path, sort):
if isinstance(sort, dict):
return {k: mkValue(path + (k,), v)
for k, v in sort.iteritems()}
strname = ".".join((name,) + path)
# Record the simsym type of this constant
Env.current().const_types[strname] = (cls, path)
# Create the Z3 constant
return z3.Const(strname, sort)
return cls._new_lvalue(mkValue((), cls._z3_sort()), model)
def _declare_assumptions(self, assume):
"""Declare assumptions for a new lvalue self.
This is called for newly created lvalues and should be
implemented by subclasses requiring assumption declarations.
Implementations should call the argument 'assume' with boolean
expressions involving self that must be true. Usually
'assume' is simply simsym.assume, but in situations involving
extensional arrays (i.e., maps) it may bind quantifiers.
Subclasses that override this method should always invoke the
parent class' _declare_assumptions method.
For compound types, since only the compound itself is a newly
created lvalue, the implementation of _declare_assumptions
should project the components of the compound and call each
component's _declare_assumptions method.
"""
pass
def __eq__(self, o):
if options.eq_eliminate_structural and self.eq(o):
return True
return self._eq_internal(o)
def _eq_internal(self, o):
"""Abstract method for equality testing.
In general, subclasses should override _eq_internal to
implement equality testing, as __eq__ performs structural
equality optimization before calling this.
"""
raise NotImplementedError('_eq_internal is abstract')
def __ne__(self, o):
if options.eq_eliminate_structural and self.eq(o):
return False
return self._ne_internal(o)
def _ne_internal(self, o):
return symnot(self._eq_internal(o))
def eq(self, o):
"""Return True if self and o are structurally identical.
This is what Python's == would usually mean, but Symbolic
overrides == to return symbolic equality expressions.
"""
if self is o:
return True
if not isinstance(o, Symbolic):
return False
def rec(a, b):
if isinstance(a, dict) != isinstance(b, dict):
return False
if isinstance(a, dict):
if len(a) != len(b):
return False
for k in a.keys():
if k not in b or not rec(a[k], b[k]):
return False
elif z3.is_ast(a):
return z3.is_ast(b) and a.eq(b)
else:
return a == b
return rec(self._z3_value(), o._z3_value())
def __hash__(self):
return hash(compound_map(lambda val: val.hash(), self._z3_value()))
def copy(self):
"""Return a deep copy of this object.
For immutable values, this should be overridden to return
self.
"""
# Assumptions have already been declared on the underlying Z3
# value, so don't assume them again
return self.bind(MODEL_FETCH)
def bind(self, model):
"""Return a deep copy of this object that is bound to model."""
return self._new_lvalue(compound_map(lambda x:x, self._z3_value()),
model)
class SymbolicConst(Symbolic):
"""The base class for symbolic constants. Symbolic constants are
deeply immutable values such as primitive types."""
@classmethod
def var(cls, name=None, model=None):
# Const returns the most specific z3.*Ref type it can based on
# the sort. This is equivalent to Symbolic.var, but jumps
# through fewer hoops.
name = gen_name(name)
if model is None:
Env.current().var_constructors[name] = cls.var
Env.current().const_types[name] = (cls, ())
return cls._wrap(z3.Const(name, cls._z3_sort()), model)
@classmethod
def _wrap_lvalue(cls, getter, setter, model):
# Fetch the value immediately, rather than acting like an
# lvalue.
return cls._wrap(getter(), model)
def copy(self):
"""Return a deep copy of this object.
Since this is an immutable value, this returns self.
"""
return self
#
# Compounds
#
# A "compound X" is either an X or a dictionary from component names
# (e.g., struct fields) to compound X's.
def compound_map(func, *compounds):
if len(compounds) == 1:
return compound_map1(func, compounds[0])
else:
return compound_mapN(func, compounds)
def compound_map1(func, compound):
if isinstance(compound, dict):
return {k: compound_map1(func, v) for k, v in compound.iteritems()}
return func(compound)
def compound_mapN(func, compounds):
if isinstance(compounds[0], dict):
return {k: compound_mapN(func, [c[k] for c in compounds])
for k in compounds[0].iterkeys()}
return func(*compounds)
def flatten_compound(compound):
"""Return a list of compound's leafs.
Note that the returned leafs are in no particular order and the
order may even differ between flattenings of structurally
equivalent compounds.
"""
res = []
def rec(compound):
if isinstance(compound, dict):
for sub in compound.itervalues():
rec(sub)
else:
res.append(compound)
rec(compound)
return res
#
# Z3 wrappers
#
# Below, we construct Symbolic subclasses that parallel Z3 sorts.
# These are organized into a functional class hierarchy so that, for
# example, all sorts supporting arithmetic operations can share a
# superclass that wraps the arithmetic operations. (These functional
# superclasses are not strictly abstract; because our wrapping of Z3
# sorts is not complete, we may wrap a Z3 expression using one of
# these superclasses if we don't know its specific sort.) Most of the
# methods in these classes simply wrap underlying Z3 methods
# (unwrapping the arguments and re-wrapping the result).
class MetaZ3Wrapper(type):
"""Metaclass to generate wrappers for Z3 ref object methods.
The class must have a __ref_type__ field giving the Z3 ref type
wrapped by the class. The class may optionally have a
__pass_type__ field, giving a Python type or tuple or types that
should be passed through wrap untouched.
The class must also have a __wrap__ field giving a list of method
names to wrap. For each method in __wrap__, the generated class
will have a corresponding method with the same signature that
unwraps all arguments, calls the Z3 method, and re-wraps the
result.
"""
def __new__(cls, classname, bases, classdict):
if "__wrap__" in classdict:
OVERRIDES = {"__eq__" : "_eq_internal",
"__ne__" : "_ne_internal"}
ref_type = classdict["__ref_type__"]
for method in classdict.pop("__wrap__"):
base_method = getattr(ref_type, method)
nargs = base_method.__func__.__code__.co_argcount
args = ["o%d" % i for i in range(nargs - 1)]
dmethod = OVERRIDES.get(method, method)
code = "def %s(%s):\n" % (dmethod, ",".join(["self"] + args))
for o in args:
code += " if isinstance(%s, Symbolic): %s=%s._v\n" % \
(o, o, o)
code += " return wrap(self._v.%s(%s))" % (method, ",".join(args))
locals_dict = {}
exec code in globals(), locals_dict
classdict[dmethod] = locals_dict[dmethod]
return type.__new__(cls, classname, bases, classdict)
def _wrap(cls, z3ref, model):
"""Construct an instance of 'cls' wrapping the given Z3 ref
object."""
if not isinstance(z3ref, cls.__ref_type__):
if hasattr(cls, "__pass_type__") and \
isinstance(z3ref, cls.__pass_type__):
return z3ref
raise TypeError("%s expected %s, got %s" %
(cls.__name__, cls.__ref_type__.__name__,
strtype(z3ref)))
obj = cls.__new__(cls)
obj._v = z3ref
obj._model = model
return obj
class SExpr(Symbolic):
__metaclass__ = MetaZ3Wrapper
__ref_type__ = z3.ExprRef
__wrap__ = ["__eq__", "__ne__"]
def __str__(self):
return str(self._v)
def __repr__(self):
return repr(self._v)
def _z3_value(self):
return self._v
def eval(self, realm=None):
"""The concrete value of this constant in the bound model.
The concrete assignment of this constant will be recorded in
the bound model. realm specifies the assignment realm to
record it under. The default realm is None. REALM_IGNORE is
handled specially and will cause this assignment to not be
recorded.
"""
if self._model and self._model is not MODEL_FETCH:
return self._model._eval(self, realm)
raise ValueError("%s is not bound to a model" % self)
@property
def val(self):
"""The concrete value of this constant in the bound model.
This will track the evaluation in the model so that over
values can be enumerated by concolic execution.
"""
return self.eval()
@property
def someval(self):
"""Some concrete value of this constant in the bound model.
Unlike val, this does not track this evaluation in the model,
so it will not cause concolic execution to try other values.
"""
return self.eval(REALM_IGNORE)
class SArith(SExpr):
__ref_type__ = z3.ArithRef
__wrap__ = ["__add__", "__div__", "__mod__", "__mul__", "__pow__",
"__sub__", "__truediv__",
"__radd__", "__rdiv__", "__rmod__", "__rmul__", "__rpow__",
"__rsub__", "__rtruediv__",
"__ge__", "__gt__", "__le__", "__lt__",
"__neg__", "__pos__"]
class SInt(SArith, SymbolicConst):
__pass_type__ = int
__z3_sort__ = z3.IntSort()
# Note that we're wrapping ArithRefs, not IntNumRefs. Z3's Ref
# hierarchy reflects AST structure, while our types reflect Z3
# sorts (which Z3 can't do because its C implementation can't
# construct new types on the fly like we can in Python).
def is_concrete(self):
"""Return True if this can be resolved to a concrete value."""
return isinstance(self._v, z3.IntNumRef)
def get_concrete(self):
"""Return this as a Python int.
If this value is not concrete, throws ValueError.
"""
if not self.is_concrete():
raise ValueError("%r is not concrete" % self)
return self._v.as_long()
class UncheckableConstraintError(RuntimeError):
def __init__(self, expr, reason):
RuntimeError.__init__(self, reason)
self.expr = expr
class UnsatisfiablePath(RuntimeError):
pass
class ReplayDivergedError(RuntimeError):
def __init__(self, old, new):
RuntimeError.__init__(
self, 'Replay diverged\nOld: %s\nNew: %s' % (old, new))
class SBool(SExpr, SymbolicConst):
__ref_type__ = z3.BoolRef
__pass_type__ = bool
__z3_sort__ = z3.BoolSort()
def __nonzero__(self):
if self._model and self._model is not MODEL_FETCH:
return self.val
scheduler, path_state = Env.scheduler(), Env.path_state()
solver = path_state.solver
cursched = path_state.sched
if len(cursched) == path_state.schedidx:
# We've reached the end of replay; extend the schedule
solver.push()
solver.add(self._v)
canTrue = solver.check()
canTrueReason = solver.reason_unknown()
if canTrue == z3.unknown:
# Stack operations change how Z3 "compiles" formulas,
# so it's possible it can solve it in isolation.
s2 = z3.Solver()
s2.add(*solver.assertions())
canTrue = s2.check()
canTrueReason = s2.reason_unknown()
solver.pop()
solver.push()
solver.add(z3.Not(self._v))
canFalse = solver.check()
canFalseReason = solver.reason_unknown()
if canFalse == z3.unknown:
s2 = z3.Solver()
s2.add(*solver.assertions())
canFalse = s2.check()
canFalseReason = s2.reason_unknown()
solver.pop()
if canTrue == z3.unsat and canFalse == z3.unsat:
raise RuntimeError("Branch contradiction")
# Extend the schedule
if canTrue == z3.sat and canFalse == z3.unsat:
cursched.append(SchedNode("branch_det", self, True))
elif canTrue == z3.unsat and canFalse == z3.sat:
cursched.append(SchedNode("branch_det", self, False))
else:
# Both are possible (or at least one is unknown)
newsched = list(cursched)
if canTrue == z3.sat:
cursched.append(SchedNode("branch_nondet", self, True))
elif canTrue == z3.unknown:
cursched.append(
SchedNode("exception", True,
UncheckableConstraintError(
self._v, canTrueReason)))
else:
assert canTrue == z3.unsat and canFalse == z3.unknown
# There's actually only one way to go
newsched = cursched
if canFalse == z3.sat:
newsched.append(SchedNode("branch_nondet", self, False))
elif canFalse == z3.unknown:
newsched.append(
SchedNode("exception", False,
UncheckableConstraintError(
z3.Not(self._v), canFalseReason)))
else:
assert canFalse == z3.unsat and canTrue == z3.unknown
newsched = cursched
if newsched is not cursched:
scheduler.queue_schedule(newsched)
else:
# We're replaying; check that replay hasn't diverged
node = cursched[path_state.schedidx]
if node.is_branch() and not node.expr.eq(self):
raise ReplayDivergedError(node.expr, self)
# Follow the schedule (which we may have just extended)
node = cursched[path_state.schedidx]
path_state.schedidx += 1
if node.is_branch():
solver.add(unwrap(node.path_expr()))
return node.val
elif node.typ == "exception":
raise node.val
else:
raise ReplayDivergedError(node, "branch")
class SUninterpretedBase(SExpr):
pass
def tuninterpreted(name):
"""Return a new uninterpreted symbolic type.
This type is inhabited by an unbounded number of distinct
constants.
"""
return type(name, (SUninterpretedBase, SymbolicConst),
{"__z3_sort__": z3.DeclareSort(name)})
class SEnumBase(SExpr):
__ref_type__ = z3.DatatypeRef
def tenum(name, vals):
"""Return a symbolic constant enumeration type called 'name' with
the given values. 'vals' must be a list of strings or a string of
space-separated names. The returned type will have a class field
corresponding to each concrete value and inherit from SEnumBase
and SymbolicConst."""
if isinstance(vals, basestring):
vals = vals.split()
sort, consts = z3.EnumSort(name, vals)
fields = dict(zip(vals, consts))
fields["__z3_sort__"] = sort
return type(name, (SEnumBase, SymbolicConst), fields)
class STupleBase(SExpr):
__ref_type__ = z3.DatatypeRef
def ttuple(name, *types):
"""Return a symbolic constant named tuple type with the given
fields. Each 'type' argument must be a pair of name and type.
The returned type will inherit from STupleBase and SymbolicConst
and will have properties for retrieving each component of the
tuple."""
# XXX Broken: synonym types, assumptions
raise Exception("Sorry, ttuple is broken right now")
sort = z3.Datatype(name)
sort.declare(name, *[(fname, typ._z3_sort()) for fname, typ in types])
sort = sort.create()
fields = {"__z3_sort__" : sort}
for fname, typ in types:
code = """\
@property
def %s(self):
return wrap(self.__z3_sort__.%s(self._v))""" % (fname, fname)
locals_dict = {}
exec code in globals(), locals_dict
fields[fname] = locals_dict[fname]
return type(name, (STupleBase, SymbolicConst), fields)
class SConstMapBase(SExpr):
__ref_type__ = z3.ArrayRef
__wrap__ = ["__getitem__"]
@classmethod
def constVal(cls, value):
"""Return a map where all keys map to 'value'."""
return cls._wrap(z3.K(cls._z3_sort().domain(), unwrap(value)), None)
def store(self, index, value):
"""Return a new map that is identical for this map except that
'index' will map to 'value'."""
return self._wrap(z3.Store(unwrap(self), unwrap(index), unwrap(value)),
None)
def tconstmap(indexType, valueType):
"""Return an immutable map type (a z3 "array") that maps from
symbolic constants of 'indexType' to symbolic constants of
'valueType'. The returned type will inherit from SConstMapBase
and SymbolicConst."""
sort = z3.ArraySort(indexType._z3_sort(), valueType._z3_sort())
name = "SConstMap_%s_%s" % (indexType.__name__, valueType.__name__)
return type(name, (SConstMapBase, SymbolicConst), {"__z3_sort__" : sort})
#
# Type synonyms
#
def tsynonym(name, baseType):
"""Return a new type that's equivalent to baseType."""
return type(name, (baseType,), {})
#
# Compound objects
#
class SMapBase(Symbolic):
"""The base type of symbolic mutable mapping types. Objects of
this type map from symbolic values to symbolic values. Maps
support slicing and slice assignment."""
@classmethod
def constVal(cls, value):
"""Return a map where all keys initially map to 'value'."""
indexSort = cls._indexType._z3_sort()
return cls._new_lvalue(
compound_map(lambda val: z3.K(indexSort, val), unwrap(value)),
None)
@classmethod
def _wrap_lvalue(cls, getter, setter, model):
# XXX Make this generic?
obj = cls.__new__(cls)
obj._getter = getter
obj._setter = setter
obj._model = model
return obj
def _z3_value(self):
# XXX Make this generic, too?
return self._getter()
def _declare_assumptions(self, assume):
super(SMapBase, self)._declare_assumptions(assume)
x = self._indexType.var()
self[x]._declare_assumptions(lambda expr: assume(forall(x, expr)))
def _eq_internal(self, o):
if type(self) != type(o):
return NotImplemented
if isinstance(self._valueType, SExpr):
# Optimize away the forall
vs, vo = self._getter(), o._getter()
assert not isinstance(vs, dict)
assert not isinstance(vo, dict)
return wrap(vs == vo)
# valueType may have a complex __eq__. In many (all?) cases
# the forall isn't actually necessary, but we don't have the
# mechanism to push it down the AST and eliminate it.
x = self._indexType.var()
return forall(x, self[x] == o[x])
def __getitem__(self, idx):
"""Return the value at index 'idx'."""
z3idx = unwrap(idx)
return self._valueType._wrap_lvalue(
lambda: compound_map(
lambda z3val: z3.Select(z3val, z3idx), self._getter()),
lambda val: self.__setitem__(idx, val),
self._model)
def __setitem__(self, idx, val):
"""Change the value at index 'idx'."""
z3idx = unwrap(idx)
def set1(z3map, z3val):
# If we have an array of structs, assigning to a single
# struct field will cause an identity update of all of the
# other fields. Avoid building up huge, pointless Store
# expressions.
if z3.is_ast(z3val) and z3map[z3idx].eq(z3val):
return z3map
return z3.Store(z3map, z3idx, z3val)
self._setter(compound_map(set1, self._getter(), unwrap(val)))
def tmap(indexType, valueType):
"""Return a subclass of SMapBase that maps from 'indexType' to
'valueType', where both must be subclasses of Symbolic."""
# XXX We could accept a size and check indexes if indexType is an
# ordered sort
name = "SMap_%s_%s" % (indexType.__name__, valueType.__name__)
indexSort = indexType._z3_sort()
if isinstance(indexSort, dict):
raise TypeError("Map index may not be a compound type")
sort = compound_map(lambda z3sort: z3.ArraySort(indexSort, z3sort),
valueType._z3_sort())
return type(name, (SMapBase,),
{"_indexType" : indexType, "_valueType" : valueType,
"__z3_sort__" : sort})
class SStructBase(Symbolic):
"""The base type of symbolic mutable structure types. Structure
types have a fixed set of named fields, where the fields may have
different symbolic types."""
@classmethod
def var(cls, __name=None, __model=None, **fields):
"""Return a struct instance with specified field values.
Any fields omitted from 'fields' will be unspecified. If no
field values are provided, this is equivalent to Symbolic.var.
"""
__name = gen_name(__name)
if __model is None:
# Field values may be mutable Symbolic values, but we want
# to save their current value, so snapshot them by
# unwrapping their values.
fieldsSnapshot = {k: unwrap(v) for k,v in fields.items()}
Env.current().var_constructors[__name] \
= lambda _, model: cls.var(__name, model, **fieldsSnapshot)
def mkValue(path, sort):
if len(path) == 1 and path[0] in fields:
return unwrap(fields.pop(path[0]))
if isinstance(sort, dict):
return {k: mkValue(path + (k,), v)
for k, v in sort.iteritems()}
strname = ".".join((__name,) + path)
# Record the simsym type of this constant
Env.current().const_types[strname] = (cls, path)
# Create the Z3 constant
return z3.Const(strname, sort)
z3_val = mkValue((), cls._z3_sort())
if fields:
raise AttributeError("Unknown struct field %r" % fields.keys()[0])
return cls._new_lvalue(z3_val, __model)
@classmethod
def _wrap_lvalue(cls, getter, setter, model):
obj = cls.__new__(cls)
# Don't go through the overridden __setattr__.
object.__setattr__(obj, "_getter", getter)
object.__setattr__(obj, "_setter", setter)
object.__setattr__(obj, "_model", model)
return obj
def _z3_value(self):
return self._getter()
def _declare_assumptions(self, assume):
super(SStructBase, self)._declare_assumptions(assume)
for fname in self._fields:
sub = getattr(self, fname)
if isinstance(sub, Symbolic):
sub._declare_assumptions(assume)
def _eq_internal(self, o):
if type(self) != type(o):
return NotImplemented
return symand([getattr(self, name) == getattr(o, name)
for name in self._fields])
def __getattr__(self, name):
if name not in self._fields:
raise AttributeError(name)
return self._fields[name]._wrap_lvalue(
lambda: self._getter()[name],
lambda val: self.__setattr__(name, val),
self._model)
def __setattr__(self, name, val):
if name not in self._fields:
raise AttributeError(name)
cval = self._getter()
cval[name] = unwrap(val)
self._setter(cval)
def tstruct(**fields):
"""Return a subclass of SStructBase for a struct type with the
given fields. 'fields' must be a dictionary mapping from names to
symbolic types."""
name = "SStruct_" + "_".join(fields.keys())
sort = {fname: typ._z3_sort() for fname, typ in fields.items()}
type_fields = {"__slots__": [], "_fields": fields, "__z3_sort__": sort}
return type(name, (SStructBase,), type_fields)
#
# Constructors
#
def symand(exprlist):
# Remove concrete True values, short-circuit concrete False values
nexprlist = []
for x in exprlist:
if isinstance(x, Symbolic):
nexprlist.append(x)
elif not x:
return False
if len(nexprlist):
return wrap(z3.And([unwrap(x) for x in nexprlist]))
return True
def symor(exprlist):
# Remove concrete False values, short-circuit concrete True values
nexprlist = []
for x in exprlist:
if isinstance(x, Symbolic):
nexprlist.append(x)
elif x:
return True
if len(nexprlist):
return wrap(z3.Or([unwrap(x) for x in nexprlist]))
return False
def symnot(e):
if isinstance(e, Symbolic):
return wrap(z3.Not(unwrap(e)))
else:
return not e
def symeq(*exprs):
if len(exprs) == 2 and isinstance(exprs[0], tuple) \
and isinstance(exprs[1], tuple):
# XXX Do we still use this?
a, b = exprs
if len(a) != len(b):
return False
return symand([symeq(aa, bb) for (aa, bb) in zip(a, b)])
if len(exprs) == 2:
return exprs[0] == exprs[1]
return symand([a == b for a, b in zip(exprs, exprs[1:])])
def symif(pred, cons, alt):
e = z3.If(unwrap(pred), unwrap(cons), unwrap(alt))
# Wrap the result in the type of the consequent, rather than a
# generic SExpr.
if isinstance(cons, Symbolic):
return type(cons)._wrap(e, None)
if isinstance(alt, Symbolic):
return type(alt)._wrap(e, None)
return wrap(e)
def distinct(*exprlist):
return wrap(z3.Distinct(*map(unwrap, exprlist)))
def implies(a, b):
return wrap(z3.Implies(unwrap(a), unwrap(b)))
def exists(vars, e, patterns=[]):
if not isinstance(vars, (list, tuple)):
vars = [vars]
if not isinstance(e, (Symbolic, z3.ExprRef)):
return e
z3vars = []
for v in vars:
if isinstance(v, Symbolic):
z3vars.extend(flatten_compound(v._z3_value()))
elif isinstance(v, z3.ExprRef):
z3vars.append(v)
else:
raise TypeError("exists variable must be symbolic")
if len(z3vars) == 0:
return e
return wrap(z3.Exists(z3vars, unwrap(e), patterns=map(unwrap, patterns)))
def forall(vars, e, patterns=[]):
if not isinstance(vars, (list, tuple)):
vars = [vars]
if not isinstance(e, (Symbolic, z3.ExprRef)):
return e
z3vars = []
for v in vars:
if isinstance(v, Symbolic):
z3vars.extend(flatten_compound(v._z3_value()))
elif isinstance(v, z3.ExprRef):
z3vars.append(v)
else:
raise TypeError("forall variable must be symbolic")
if len(z3vars) == 0:
return e
return wrap(z3.ForAll(z3vars, unwrap(e), patterns=map(unwrap, patterns)))
#
# Conversions to Z3 types and wrapper types
#
def unwrap(val):
"""Convert a value to a Z3 value.
If val is a Symbolic, returns the wrapped Z3 value.
Otherwise, simply returns val."""
if isinstance(val, Symbolic):
return val._z3_value()
return val
def wrap(ref):
"""Convert a value to a simsym.Symbolic.
If ref is a Z3 symbolic value, wraps it in a simsym.Symbolic.
Otherwise, if ref is a concrete type supported by the symbolic
engine, returns value. Otherwise, raises TypeError."""
if isinstance(ref, (bool, int, long, float)):
# Concrete type supported by Z3
return ref
if not isinstance(ref, z3.ExprRef):
raise TypeError("Not a bool, int, long, float, or z3.ExprRef")
# XXX It's probably not correct to always pass None for the model
# below. For example, in SMapBase.__eq__, we should probably pass
# in the map's model.
if isinstance(ref, z3.ArithRef):
if ref.is_int():
return SInt._wrap(ref, None)
return SArith._wrap(ref, None)
if isinstance(ref, z3.BoolRef):
return SBool._wrap(ref, None)
# XXX Could be a tuinterpreted
return SExpr._wrap(ref, None)
def wraplist(reflist):
"""Convert a list of values to a list of simsym.Symbolic things,
by calling wrap() on each item in the list."""
return [wrap(ref) for ref in reflist]
#
# AST matching code. Unused because the simplifier aggressively
# rewrites things back to its own preferred representation, and
# it is difficult to rewrite just one child in an AST (no generic
# AST node constructors).
#
def matchvar(n):
return z3.Int('@match:%s' % n)
def ast_match2(a, pat, matchvars):
if pat.decl().name().startswith('@match:'):
varname = pat.decl().name()[7:]
cur = matchvars.get(varname, None)
if cur is None:
matchvars[varname] = a
return True
return cur.eq(a)
if a.__class__ != pat.__class__: return False
if not a.decl().eq(pat.decl()): return False
if a.num_args() != pat.num_args(): return False
for (aa, pa) in zip(a.children(), pat.children()):
if not ast_match2(aa, pa, matchvars): return False
return True
def ast_match(a, pat, matchvars):
matchvars.clear()
return ast_match2(a, pat, matchvars)
def ast_cleanup(a):