crosshair-tool 0.0.56__cp39-cp39-macosx_11_0_arm64.whl → 0.0.100__cp39-cp39-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- _crosshair_tracers.cpython-39-darwin.so +0 -0
- crosshair/__init__.py +1 -1
- crosshair/_mark_stacks.h +51 -24
- crosshair/_tracers.h +9 -5
- crosshair/_tracers_test.py +19 -9
- crosshair/auditwall.py +9 -8
- crosshair/auditwall_test.py +31 -19
- crosshair/codeconfig.py +3 -2
- crosshair/condition_parser.py +17 -133
- crosshair/condition_parser_test.py +54 -96
- crosshair/conftest.py +1 -1
- crosshair/copyext.py +91 -22
- crosshair/copyext_test.py +33 -0
- crosshair/core.py +259 -203
- crosshair/core_and_libs.py +20 -0
- crosshair/core_regestered_types_test.py +82 -0
- crosshair/core_test.py +693 -664
- crosshair/diff_behavior.py +76 -21
- crosshair/diff_behavior_test.py +132 -23
- crosshair/dynamic_typing.py +128 -18
- crosshair/dynamic_typing_test.py +91 -4
- crosshair/enforce.py +1 -6
- crosshair/enforce_test.py +15 -23
- crosshair/examples/check_examples_test.py +2 -1
- crosshair/fnutil.py +2 -3
- crosshair/fnutil_test.py +0 -7
- crosshair/fuzz_core_test.py +70 -83
- crosshair/libimpl/arraylib.py +10 -7
- crosshair/libimpl/binascii_ch_test.py +30 -0
- crosshair/libimpl/binascii_test.py +67 -0
- crosshair/libimpl/binasciilib.py +150 -0
- crosshair/libimpl/bisectlib_test.py +5 -5
- crosshair/libimpl/builtinslib.py +1002 -682
- crosshair/libimpl/builtinslib_ch_test.py +108 -30
- crosshair/libimpl/builtinslib_test.py +431 -143
- crosshair/libimpl/codecslib.py +22 -2
- crosshair/libimpl/codecslib_test.py +41 -9
- crosshair/libimpl/collectionslib.py +44 -8
- crosshair/libimpl/collectionslib_test.py +108 -20
- crosshair/libimpl/copylib.py +1 -1
- crosshair/libimpl/copylib_test.py +18 -0
- crosshair/libimpl/datetimelib.py +84 -67
- crosshair/libimpl/datetimelib_ch_test.py +12 -7
- crosshair/libimpl/datetimelib_test.py +5 -6
- crosshair/libimpl/decimallib.py +5257 -0
- crosshair/libimpl/decimallib_ch_test.py +78 -0
- crosshair/libimpl/decimallib_test.py +76 -0
- crosshair/libimpl/encodings/_encutil.py +21 -11
- crosshair/libimpl/fractionlib.py +16 -0
- crosshair/libimpl/fractionlib_test.py +80 -0
- crosshair/libimpl/functoolslib.py +19 -7
- crosshair/libimpl/functoolslib_test.py +22 -6
- crosshair/libimpl/hashliblib.py +30 -0
- crosshair/libimpl/hashliblib_test.py +18 -0
- crosshair/libimpl/heapqlib.py +32 -5
- crosshair/libimpl/heapqlib_test.py +15 -12
- crosshair/libimpl/iolib.py +7 -4
- crosshair/libimpl/ipaddresslib.py +8 -0
- crosshair/libimpl/itertoolslib_test.py +1 -1
- crosshair/libimpl/mathlib.py +165 -2
- crosshair/libimpl/mathlib_ch_test.py +44 -0
- crosshair/libimpl/mathlib_test.py +59 -16
- crosshair/libimpl/oslib.py +7 -0
- crosshair/libimpl/pathliblib_test.py +10 -0
- crosshair/libimpl/randomlib.py +1 -0
- crosshair/libimpl/randomlib_test.py +6 -4
- crosshair/libimpl/relib.py +180 -59
- crosshair/libimpl/relib_ch_test.py +26 -2
- crosshair/libimpl/relib_test.py +77 -14
- crosshair/libimpl/timelib.py +35 -13
- crosshair/libimpl/timelib_test.py +13 -3
- crosshair/libimpl/typeslib.py +15 -0
- crosshair/libimpl/typeslib_test.py +36 -0
- crosshair/libimpl/unicodedatalib_test.py +3 -3
- crosshair/libimpl/weakreflib.py +13 -0
- crosshair/libimpl/weakreflib_test.py +69 -0
- crosshair/libimpl/zliblib.py +15 -0
- crosshair/libimpl/zliblib_test.py +13 -0
- crosshair/lsp_server.py +21 -10
- crosshair/main.py +48 -28
- crosshair/main_test.py +59 -14
- crosshair/objectproxy.py +39 -14
- crosshair/objectproxy_test.py +27 -13
- crosshair/opcode_intercept.py +212 -24
- crosshair/opcode_intercept_test.py +172 -18
- crosshair/options.py +0 -1
- crosshair/patch_equivalence_test.py +5 -21
- crosshair/path_cover.py +7 -5
- crosshair/path_search.py +6 -4
- crosshair/path_search_test.py +1 -2
- crosshair/pathing_oracle.py +53 -10
- crosshair/pathing_oracle_test.py +21 -0
- crosshair/pure_importer_test.py +5 -21
- crosshair/register_contract.py +16 -6
- crosshair/register_contract_test.py +2 -14
- crosshair/simplestructs.py +154 -85
- crosshair/simplestructs_test.py +16 -2
- crosshair/smtlib.py +24 -0
- crosshair/smtlib_test.py +14 -0
- crosshair/statespace.py +319 -196
- crosshair/statespace_test.py +45 -0
- crosshair/stubs_parser.py +0 -2
- crosshair/test_util.py +87 -25
- crosshair/test_util_test.py +26 -0
- crosshair/tools/check_init_and_setup_coincide.py +0 -3
- crosshair/tools/generate_demo_table.py +2 -2
- crosshair/tracers.py +141 -49
- crosshair/type_repo.py +11 -4
- crosshair/unicode_categories.py +1 -0
- crosshair/util.py +158 -76
- crosshair/util_test.py +13 -20
- crosshair/watcher.py +4 -4
- crosshair/z3util.py +1 -1
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/METADATA +45 -36
- crosshair_tool-0.0.100.dist-info/RECORD +176 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/WHEEL +2 -1
- crosshair/examples/hypothesis/__init__.py +0 -2
- crosshair/examples/hypothesis/bugs_detected/simple_strategies.py +0 -74
- crosshair_tool-0.0.56.dist-info/RECORD +0 -152
- /crosshair/{examples/hypothesis/bugs_detected/__init__.py → py.typed} +0 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/entry_points.txt +0 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info/licenses}/LICENSE +0 -0
- {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/top_level.txt +0 -0
crosshair/libimpl/builtinslib.py
CHANGED
|
@@ -3,34 +3,37 @@ import collections
|
|
|
3
3
|
import copy
|
|
4
4
|
import enum
|
|
5
5
|
import io
|
|
6
|
-
import math
|
|
7
6
|
import operator as ops
|
|
7
|
+
import os
|
|
8
8
|
import re
|
|
9
9
|
import string
|
|
10
10
|
import sys
|
|
11
11
|
import typing
|
|
12
|
+
import warnings
|
|
12
13
|
from abc import ABCMeta
|
|
13
14
|
from array import array
|
|
15
|
+
from collections.abc import Mapping
|
|
14
16
|
from dataclasses import dataclass
|
|
15
|
-
from functools import wraps
|
|
16
17
|
from itertools import zip_longest
|
|
18
|
+
from math import inf, isfinite, isinf, isnan, nan
|
|
17
19
|
from numbers import Integral, Number, Real
|
|
18
|
-
from sys import maxunicode
|
|
20
|
+
from sys import maxunicode, version_info
|
|
19
21
|
from typing import (
|
|
20
22
|
Any,
|
|
21
23
|
BinaryIO,
|
|
22
|
-
ByteString,
|
|
23
24
|
Callable,
|
|
24
25
|
Dict,
|
|
25
26
|
FrozenSet,
|
|
26
27
|
Hashable,
|
|
27
28
|
Iterable,
|
|
28
29
|
List,
|
|
30
|
+
MutableMapping,
|
|
29
31
|
NamedTuple,
|
|
30
32
|
NoReturn,
|
|
31
33
|
Optional,
|
|
32
34
|
Sequence,
|
|
33
35
|
Set,
|
|
36
|
+
Sized,
|
|
34
37
|
SupportsAbs,
|
|
35
38
|
SupportsBytes,
|
|
36
39
|
SupportsComplex,
|
|
@@ -49,9 +52,16 @@ from typing import (
|
|
|
49
52
|
import typing_inspect # type: ignore
|
|
50
53
|
import z3 # type: ignore
|
|
51
54
|
|
|
55
|
+
try:
|
|
56
|
+
# For reasons unknown, different distributions of z3 4.13.0 use differnt names.
|
|
57
|
+
from z3 import fpEQ
|
|
58
|
+
except ImportError:
|
|
59
|
+
from z3 import FfpEQ as fpEQ
|
|
60
|
+
|
|
61
|
+
import sys
|
|
62
|
+
|
|
52
63
|
from crosshair.abcstring import AbcString
|
|
53
64
|
from crosshair.core import (
|
|
54
|
-
CrossHairValue,
|
|
55
65
|
SymbolicFactory,
|
|
56
66
|
deep_realize,
|
|
57
67
|
iter_types,
|
|
@@ -61,14 +71,16 @@ from crosshair.core import (
|
|
|
61
71
|
realize,
|
|
62
72
|
register_patch,
|
|
63
73
|
register_type,
|
|
74
|
+
with_checked_self,
|
|
64
75
|
with_realized_args,
|
|
65
76
|
with_symbolic_self,
|
|
66
77
|
with_uniform_probabilities,
|
|
67
78
|
)
|
|
68
79
|
from crosshair.objectproxy import ObjectProxy
|
|
69
80
|
from crosshair.simplestructs import (
|
|
81
|
+
FrozenSetBase,
|
|
82
|
+
LinearSet,
|
|
70
83
|
SequenceConcatenation,
|
|
71
|
-
SetBase,
|
|
72
84
|
ShellMutableMap,
|
|
73
85
|
ShellMutableSequence,
|
|
74
86
|
ShellMutableSet,
|
|
@@ -97,19 +109,30 @@ from crosshair.type_repo import PYTYPE_SORT, SymbolicTypeRepository
|
|
|
97
109
|
from crosshair.unicode_categories import UnicodeMaskCache
|
|
98
110
|
from crosshair.util import (
|
|
99
111
|
ATOMIC_IMMUTABLE_TYPES,
|
|
100
|
-
|
|
112
|
+
CrossHairInternal,
|
|
101
113
|
CrosshairUnsupported,
|
|
114
|
+
CrossHairValue,
|
|
115
|
+
IdKeyedDict,
|
|
102
116
|
IgnoreAttempt,
|
|
117
|
+
UnknownSatisfiability,
|
|
118
|
+
assert_tracing,
|
|
119
|
+
ch_stack,
|
|
103
120
|
debug,
|
|
121
|
+
is_bytes_like,
|
|
104
122
|
is_hashable,
|
|
105
123
|
is_iterable,
|
|
106
124
|
memo,
|
|
107
125
|
name_of_type,
|
|
108
126
|
smtlib_typename,
|
|
109
|
-
test_stack,
|
|
110
127
|
type_arg_of,
|
|
111
128
|
)
|
|
112
|
-
from crosshair.z3util import z3And, z3Eq, z3Ge, z3Gt, z3IntVal, z3Or
|
|
129
|
+
from crosshair.z3util import z3And, z3Eq, z3Ge, z3Gt, z3IntVal, z3Not, z3Or
|
|
130
|
+
|
|
131
|
+
if sys.version_info >= (3, 12):
|
|
132
|
+
from collections.abc import Buffer
|
|
133
|
+
else:
|
|
134
|
+
from collections.abc import ByteString as Buffer
|
|
135
|
+
|
|
113
136
|
|
|
114
137
|
_T = TypeVar("_T")
|
|
115
138
|
_VT = TypeVar("_VT")
|
|
@@ -145,6 +168,18 @@ def smt_or(a: bool, b: bool) -> bool:
|
|
|
145
168
|
return a or b
|
|
146
169
|
|
|
147
170
|
|
|
171
|
+
def smt_xor(a: bool, b: bool) -> bool:
|
|
172
|
+
with NoTracing():
|
|
173
|
+
if isinstance(a, SymbolicBool) or isinstance(b, SymbolicBool):
|
|
174
|
+
if isinstance(a, SymbolicBool) and isinstance(b, SymbolicBool):
|
|
175
|
+
return SymbolicBool(z3.Xor(a.var, b.var))
|
|
176
|
+
elif isinstance(a, bool):
|
|
177
|
+
return SymbolicBool(z3Not(b.var)) if a else b
|
|
178
|
+
elif isinstance(b, bool):
|
|
179
|
+
return SymbolicBool(z3Not(a.var)) if b else a
|
|
180
|
+
return a ^ b
|
|
181
|
+
|
|
182
|
+
|
|
148
183
|
def smt_not(x: object) -> Union[bool, "SymbolicBool"]:
|
|
149
184
|
with NoTracing():
|
|
150
185
|
if isinstance(x, SymbolicBool):
|
|
@@ -154,6 +189,7 @@ def smt_not(x: object) -> Union[bool, "SymbolicBool"]:
|
|
|
154
189
|
|
|
155
190
|
_NONHEAP_PYTYPES = set([int, float, bool, NoneType, complex])
|
|
156
191
|
|
|
192
|
+
|
|
157
193
|
# TODO: isn't this pretty close to isinstance(typ, AtomicSymbolicValue)?
|
|
158
194
|
def pytype_uses_heap(typ: Type) -> bool:
|
|
159
195
|
return not (typ in _NONHEAP_PYTYPES)
|
|
@@ -170,7 +206,6 @@ def typeable_value(val: object) -> object:
|
|
|
170
206
|
|
|
171
207
|
_SMT_INT_SORT = z3.IntSort()
|
|
172
208
|
_SMT_BOOL_SORT = z3.BoolSort()
|
|
173
|
-
_SMT_FLOAT_SORT = z3.RealSort() # difficulty getting the solver to use z3.Float64()
|
|
174
209
|
|
|
175
210
|
|
|
176
211
|
@memo
|
|
@@ -193,31 +228,11 @@ SymbolicGenerator = Callable[[Union[str, z3.ExprRef], type], object]
|
|
|
193
228
|
|
|
194
229
|
|
|
195
230
|
def origin_of(typ: Type) -> Type:
|
|
196
|
-
typ = _WRAPPER_TYPE_TO_PYTYPE.get(typ, typ) # TODO is the still required?
|
|
197
231
|
if hasattr(typ, "__origin__"):
|
|
198
232
|
return typ.__origin__
|
|
199
233
|
return typ
|
|
200
234
|
|
|
201
235
|
|
|
202
|
-
# TODO: refactor away casting in SMT-sapce:
|
|
203
|
-
def smt_int_to_float(a: z3.ExprRef) -> z3.ExprRef:
|
|
204
|
-
if _SMT_FLOAT_SORT == z3.Float64():
|
|
205
|
-
return z3.fpRealToFP(z3.RNE(), z3.ToReal(a), _SMT_FLOAT_SORT)
|
|
206
|
-
elif _SMT_FLOAT_SORT == z3.RealSort():
|
|
207
|
-
return z3.ToReal(a)
|
|
208
|
-
else:
|
|
209
|
-
raise CrosshairInternal()
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
def smt_bool_to_float(a: z3.ExprRef) -> z3.ExprRef:
|
|
213
|
-
if _SMT_FLOAT_SORT == z3.Float64():
|
|
214
|
-
return z3.If(a, z3.FPVal(1.0, _SMT_FLOAT_SORT), z3.FPVal(0.0, _SMT_FLOAT_SORT))
|
|
215
|
-
elif _SMT_FLOAT_SORT == z3.RealSort():
|
|
216
|
-
return z3.If(a, z3.RealVal(1), z3.RealVal(0))
|
|
217
|
-
else:
|
|
218
|
-
raise CrosshairInternal()
|
|
219
|
-
|
|
220
|
-
|
|
221
236
|
def smt_coerce(val: Any) -> z3.ExprRef:
|
|
222
237
|
if isinstance(val, SymbolicValue):
|
|
223
238
|
return val.var
|
|
@@ -249,17 +264,17 @@ def invoke_dunder(obj: object, method_name: str, *args, **kwargs):
|
|
|
249
264
|
class SymbolicValue(CrossHairValue):
|
|
250
265
|
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type):
|
|
251
266
|
if is_tracing():
|
|
252
|
-
raise
|
|
267
|
+
raise CrossHairInternal
|
|
253
268
|
self.snapshot = SnapshotRef(-1)
|
|
254
269
|
self.python_type = typ
|
|
255
270
|
if type(smtvar) is str:
|
|
256
|
-
self.var = self.__init_var__(typ, smtvar)
|
|
271
|
+
self.var: Any = self.__init_var__(typ, smtvar)
|
|
257
272
|
else:
|
|
258
273
|
self.var = smtvar
|
|
259
274
|
# TODO test that smtvar's sort matches expected?
|
|
260
275
|
|
|
261
276
|
def __init_var__(self, typ, varname):
|
|
262
|
-
raise
|
|
277
|
+
raise CrossHairInternal(f"__init_var__ not implemented in {type(self)}")
|
|
263
278
|
|
|
264
279
|
def __deepcopy__(self, memo):
|
|
265
280
|
result = copy.copy(self)
|
|
@@ -315,7 +330,7 @@ class SymbolicValue(CrossHairValue):
|
|
|
315
330
|
class AtomicSymbolicValue(SymbolicValue):
|
|
316
331
|
def __init_var__(self, typ, varname):
|
|
317
332
|
if is_tracing():
|
|
318
|
-
raise
|
|
333
|
+
raise CrossHairInternal("Tracing while creating symbolic")
|
|
319
334
|
z3type = self.__class__._ch_smt_sort()
|
|
320
335
|
return z3.Const(varname, z3type)
|
|
321
336
|
|
|
@@ -324,20 +339,20 @@ class AtomicSymbolicValue(SymbolicValue):
|
|
|
324
339
|
|
|
325
340
|
@classmethod
|
|
326
341
|
def _ch_smt_sort(cls) -> z3.SortRef:
|
|
327
|
-
raise
|
|
342
|
+
raise CrossHairInternal(f"_ch_smt_sort not implemented in {cls}")
|
|
328
343
|
|
|
329
344
|
@classmethod
|
|
330
345
|
def _pytype(cls) -> Type:
|
|
331
|
-
|
|
346
|
+
# TODO: unify this with __ch_pytype__()? (this is classmethod though)
|
|
347
|
+
raise CrossHairInternal(f"_pytype not implemented in {cls}")
|
|
332
348
|
|
|
333
349
|
@classmethod
|
|
334
|
-
def _smt_promote_literal(cls,
|
|
335
|
-
raise
|
|
350
|
+
def _smt_promote_literal(cls, literal: object) -> Optional[z3.ExprRef]:
|
|
351
|
+
raise CrossHairInternal(f"_smt_promote_literal not implemented in {cls}")
|
|
336
352
|
|
|
337
353
|
@classmethod
|
|
354
|
+
@assert_tracing(False)
|
|
338
355
|
def _coerce_to_smt_sort(cls, input_value: Any) -> Optional[z3.ExprRef]:
|
|
339
|
-
if is_tracing():
|
|
340
|
-
raise CrosshairInternal("_coerce_to_smt_sort called while tracing")
|
|
341
356
|
input_value = typeable_value(input_value)
|
|
342
357
|
target_pytype = cls._pytype()
|
|
343
358
|
|
|
@@ -376,17 +391,61 @@ class AtomicSymbolicValue(SymbolicValue):
|
|
|
376
391
|
|
|
377
392
|
|
|
378
393
|
_PYTYPE_TO_WRAPPER_TYPE: Dict[
|
|
379
|
-
type, Tuple[Type[AtomicSymbolicValue], ...]
|
|
394
|
+
type, Tuple[Tuple[Type[AtomicSymbolicValue], float], ...]
|
|
380
395
|
] = {} # to be populated later
|
|
381
|
-
_WRAPPER_TYPE_TO_PYTYPE: Dict[SymbolicGenerator, type] = {}
|
|
382
396
|
|
|
383
397
|
|
|
384
|
-
def crosshair_types_for_python_type(
|
|
398
|
+
def crosshair_types_for_python_type(
|
|
399
|
+
typ: Type,
|
|
400
|
+
) -> Tuple[Tuple[Type[AtomicSymbolicValue], float], ...]:
|
|
385
401
|
typ = normalize_pytype(typ)
|
|
386
402
|
origin = origin_of(typ)
|
|
387
403
|
return _PYTYPE_TO_WRAPPER_TYPE.get(origin, ())
|
|
388
404
|
|
|
389
405
|
|
|
406
|
+
def python_types_using_atomic_symbolics() -> Iterable[Type[AtomicSymbolicValue]]:
|
|
407
|
+
return _PYTYPE_TO_WRAPPER_TYPE.keys()
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class ModelingDirector:
|
|
411
|
+
def __init__(self, *a) -> None:
|
|
412
|
+
# Maps python type to the symbolic type we've chosen to represent it (on this iteration)
|
|
413
|
+
self.global_representations: MutableMapping[
|
|
414
|
+
type, Optional[Type[AtomicSymbolicValue]]
|
|
415
|
+
] = IdKeyedDict()
|
|
416
|
+
|
|
417
|
+
def get(self, typ: Type) -> Optional[Type[AtomicSymbolicValue]]:
|
|
418
|
+
representation_type = self.global_representations.get(typ, _MISSING)
|
|
419
|
+
if representation_type is _MISSING:
|
|
420
|
+
ch_types = crosshair_types_for_python_type(typ)
|
|
421
|
+
if not ch_types:
|
|
422
|
+
representation_type = None
|
|
423
|
+
elif len(ch_types) == 1:
|
|
424
|
+
representation_type = ch_types[0][0]
|
|
425
|
+
else:
|
|
426
|
+
space = context_statespace()
|
|
427
|
+
for option, probability in ch_types[:-1]:
|
|
428
|
+
# NOTE: fork_parallel() is closer to what we want than smt_fork();
|
|
429
|
+
# however, exhausting an incomplete representation path
|
|
430
|
+
# (e.g. RealBasedSymbolicFloat) should not exhaust the branch.
|
|
431
|
+
if space.smt_fork(
|
|
432
|
+
desc=f"use_{option.__name__}", probability_true=probability
|
|
433
|
+
):
|
|
434
|
+
representation_type = option
|
|
435
|
+
break
|
|
436
|
+
else:
|
|
437
|
+
representation_type = ch_types[-1][0]
|
|
438
|
+
self.global_representations[typ] = representation_type
|
|
439
|
+
return representation_type
|
|
440
|
+
|
|
441
|
+
def choose(self, typ: Type) -> Type[AtomicSymbolicValue]:
|
|
442
|
+
chosen = self.get(typ)
|
|
443
|
+
if chosen is None:
|
|
444
|
+
raise CrossHairInternal(f"No symbolics for {typ}")
|
|
445
|
+
return chosen
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
@assert_tracing(False)
|
|
390
449
|
def smt_to_ch_value(
|
|
391
450
|
space: StateSpace, snapshot: SnapshotRef, smt_val: z3.ExprRef, pytype: type
|
|
392
451
|
) -> object:
|
|
@@ -395,11 +454,13 @@ def smt_to_ch_value(
|
|
|
395
454
|
typ, smtlib_typename(typ) + "_inheap" + space.uniq(), allow_subtypes=True
|
|
396
455
|
)
|
|
397
456
|
|
|
457
|
+
if isinstance(pytype, SymbolicType):
|
|
458
|
+
pytype = realize(pytype)
|
|
459
|
+
|
|
398
460
|
if smt_val.sort() == HeapRef:
|
|
399
461
|
return space.find_key_in_heap(smt_val, pytype, proxy_generator, snapshot)
|
|
400
|
-
ch_type =
|
|
401
|
-
|
|
402
|
-
return ch_type[0](smt_val, pytype)
|
|
462
|
+
ch_type = space.extra(ModelingDirector).choose(pytype)
|
|
463
|
+
return ch_type(smt_val, pytype)
|
|
403
464
|
|
|
404
465
|
|
|
405
466
|
def force_to_smt_sort(
|
|
@@ -448,12 +509,24 @@ class FiniteFloat(KindedFloat):
|
|
|
448
509
|
|
|
449
510
|
|
|
450
511
|
class NonFiniteFloat(KindedFloat):
|
|
451
|
-
|
|
512
|
+
def get_finite_comparable(self, other: Union[FiniteFloat, "SymbolicFloat"]):
|
|
513
|
+
# These three cases help cover operations like `a * -inf` which is either
|
|
514
|
+
# positive of negative infinity depending on the sign of `a`.
|
|
515
|
+
if isinstance(other, FiniteFloat):
|
|
516
|
+
comparable: Union[float, SymbolicFloat] = other.val
|
|
517
|
+
else:
|
|
518
|
+
comparable = other
|
|
519
|
+
if comparable > 0: # type: ignore
|
|
520
|
+
return 1
|
|
521
|
+
elif comparable < 0:
|
|
522
|
+
return -1
|
|
523
|
+
else:
|
|
524
|
+
return 0
|
|
452
525
|
|
|
453
526
|
|
|
454
527
|
def numeric_binop(op: BinFn, a: Number, b: Number):
|
|
455
528
|
if not is_tracing():
|
|
456
|
-
raise
|
|
529
|
+
raise CrossHairInternal("Numeric operation on symbolic while not tracing")
|
|
457
530
|
with NoTracing():
|
|
458
531
|
return numeric_binop_internal(op, a, b)
|
|
459
532
|
|
|
@@ -563,15 +636,27 @@ _BITWISE_OPS: Set[BinFn] = {
|
|
|
563
636
|
ops.rshift,
|
|
564
637
|
ops.lshift,
|
|
565
638
|
}
|
|
639
|
+
_VALID_OPS_ON_COMPLEX_TYPES: Set[BinFn] = {
|
|
640
|
+
ops.eq,
|
|
641
|
+
ops.ne,
|
|
642
|
+
ops.add,
|
|
643
|
+
ops.sub,
|
|
644
|
+
ops.mul,
|
|
645
|
+
ops.truediv,
|
|
646
|
+
ops.pow,
|
|
647
|
+
}
|
|
566
648
|
|
|
567
649
|
|
|
568
650
|
def apply_smt(op: BinFn, x: z3.ExprRef, y: z3.ExprRef) -> z3.ExprRef:
|
|
569
651
|
# Mostly, z3 overloads operators and things just work.
|
|
570
652
|
# But some special cases need to be checked first.
|
|
653
|
+
# TODO: we should investigate using the op override mechanism to
|
|
654
|
+
# dispatch to the right SMT operations.
|
|
571
655
|
space = context_statespace()
|
|
572
656
|
if op in _ARITHMETIC_OPS:
|
|
573
657
|
if op in (ops.truediv, ops.floordiv, ops.mod):
|
|
574
|
-
if
|
|
658
|
+
iszero = (fpEQ(y, 0.0)) if isinstance(y, z3.FPRef) else (y == 0)
|
|
659
|
+
if space.smt_fork(iszero):
|
|
575
660
|
raise ZeroDivisionError
|
|
576
661
|
if op == ops.floordiv:
|
|
577
662
|
if space.smt_fork(y >= 0):
|
|
@@ -585,15 +670,18 @@ def apply_smt(op: BinFn, x: z3.ExprRef, y: z3.ExprRef) -> z3.ExprRef:
|
|
|
585
670
|
else:
|
|
586
671
|
return -x / -y
|
|
587
672
|
if op == ops.mod:
|
|
588
|
-
if space.smt_fork(y >= 0):
|
|
673
|
+
if space.smt_fork(z3.Or(y >= 0, x % y == 0)):
|
|
589
674
|
return x % y
|
|
590
|
-
elif space.smt_fork(x % y == 0):
|
|
591
|
-
return z3IntVal(0)
|
|
592
675
|
else:
|
|
593
676
|
return (x % y) + y
|
|
594
677
|
elif op == ops.pow:
|
|
595
678
|
if space.smt_fork(z3.And(x == 0, y < 0)):
|
|
596
679
|
raise ZeroDivisionError("zero cannot be raised to a negative power")
|
|
680
|
+
if z3.is_fp(x) or z3.is_fp(y):
|
|
681
|
+
# Smtlib does not support exponentiation on true floats
|
|
682
|
+
raise UnknownSatisfiability("pow on floats is not supported by smtlib")
|
|
683
|
+
if x.is_int() and y.is_int():
|
|
684
|
+
return z3.ToInt(op(x, y))
|
|
597
685
|
return op(x, y)
|
|
598
686
|
|
|
599
687
|
|
|
@@ -604,10 +692,10 @@ _ALL_OPS = _ARITHMETIC_AND_COMPARISON_OPS.union(_BITWISE_OPS)
|
|
|
604
692
|
def setup_binops():
|
|
605
693
|
# Lower entries take precendence when searching.
|
|
606
694
|
|
|
607
|
-
# We check NaN and infitity immediately;
|
|
608
|
-
#
|
|
695
|
+
# We check NaN and infitity immediately;
|
|
696
|
+
# RealBasedSymbolicFloats don't support these cases.
|
|
609
697
|
def _(a: Real, b: float):
|
|
610
|
-
if
|
|
698
|
+
if isfinite(b):
|
|
611
699
|
return (a, FiniteFloat(b)) # type: ignore
|
|
612
700
|
return (a, NonFiniteFloat(b))
|
|
613
701
|
|
|
@@ -623,7 +711,7 @@ def setup_binops():
|
|
|
623
711
|
# Implicitly upconvert symbolic ints to floats.
|
|
624
712
|
def _(a: SymbolicInt, b: Union[float, FiniteFloat, SymbolicFloat, complex]):
|
|
625
713
|
with NoTracing():
|
|
626
|
-
return (
|
|
714
|
+
return (a.__float__(), b)
|
|
627
715
|
|
|
628
716
|
setup_promotion(_, _ARITHMETIC_AND_COMPARISON_OPS)
|
|
629
717
|
|
|
@@ -641,61 +729,66 @@ def setup_binops():
|
|
|
641
729
|
|
|
642
730
|
# complex
|
|
643
731
|
def _(op: BinFn, a: SymbolicNumberAble, b: complex):
|
|
732
|
+
if op not in _VALID_OPS_ON_COMPLEX_TYPES:
|
|
733
|
+
raise TypeError
|
|
644
734
|
return op(complex(a), b) # type: ignore
|
|
645
735
|
|
|
646
736
|
setup_binop(_, _ALL_OPS)
|
|
647
737
|
|
|
648
738
|
# float
|
|
649
|
-
def _(op: BinFn, a: SymbolicFloat, b:
|
|
739
|
+
def _(op: BinFn, a: SymbolicFloat, b: KindedFloat):
|
|
650
740
|
with NoTracing():
|
|
651
|
-
|
|
741
|
+
symbolic_type = context_statespace().extra(ModelingDirector).choose(float)
|
|
742
|
+
bval = symbolic_type._smt_promote_literal(b.val)
|
|
743
|
+
return SymbolicBool(apply_smt(op, a.var, bval))
|
|
652
744
|
|
|
653
|
-
setup_binop(_,
|
|
745
|
+
setup_binop(_, _COMPARISON_OPS)
|
|
654
746
|
|
|
655
|
-
def _(op: BinFn, a: SymbolicFloat, b:
|
|
747
|
+
def _(op: BinFn, a: SymbolicFloat, b: KindedFloat):
|
|
656
748
|
with NoTracing():
|
|
657
|
-
|
|
749
|
+
symbolic_type = context_statespace().extra(ModelingDirector).choose(float)
|
|
750
|
+
bval = symbolic_type._smt_promote_literal(b.val)
|
|
751
|
+
return symbolic_type(apply_smt(op, a.var, bval), float)
|
|
658
752
|
|
|
659
|
-
setup_binop(_,
|
|
753
|
+
setup_binop(_, _ARITHMETIC_OPS)
|
|
660
754
|
|
|
661
|
-
def _(op: BinFn, a:
|
|
755
|
+
def _(op: BinFn, a: KindedFloat, b: SymbolicFloat):
|
|
662
756
|
with NoTracing():
|
|
663
|
-
|
|
757
|
+
symbolic_type = context_statespace().extra(ModelingDirector).choose(float)
|
|
758
|
+
aval = symbolic_type._smt_promote_literal(a.val)
|
|
759
|
+
return symbolic_type(apply_smt(op, aval, b.var), float)
|
|
664
760
|
|
|
665
761
|
setup_binop(_, _ARITHMETIC_OPS)
|
|
666
762
|
|
|
667
|
-
def _(op: BinFn, a:
|
|
763
|
+
def _(op: BinFn, a: SymbolicFloat, b: SymbolicFloat):
|
|
668
764
|
with NoTracing():
|
|
669
|
-
|
|
765
|
+
symbolic_type = context_statespace().extra(ModelingDirector).choose(float)
|
|
766
|
+
return symbolic_type(apply_smt(op, a.var, b.var), float)
|
|
670
767
|
|
|
671
768
|
setup_binop(_, _ARITHMETIC_OPS)
|
|
672
769
|
|
|
673
|
-
def _(op: BinFn, a:
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
return op(1, b.val) # type: ignore
|
|
682
|
-
elif comparable_a < 0:
|
|
683
|
-
return op(-1, b.val) # type: ignore
|
|
684
|
-
else:
|
|
685
|
-
return op(0, b.val) # type: ignore
|
|
770
|
+
def _(op: BinFn, a: SymbolicFloat, b: SymbolicFloat):
|
|
771
|
+
with NoTracing():
|
|
772
|
+
return SymbolicBool(apply_smt(op, a.var, b.var))
|
|
773
|
+
|
|
774
|
+
setup_binop(_, _COMPARISON_OPS)
|
|
775
|
+
|
|
776
|
+
def _(op: BinFn, a: Union[FiniteFloat, RealBasedSymbolicFloat], b: NonFiniteFloat):
|
|
777
|
+
return op(b.get_finite_comparable(a), b.val)
|
|
686
778
|
|
|
687
779
|
setup_binop(_, _ARITHMETIC_AND_COMPARISON_OPS)
|
|
688
780
|
|
|
689
|
-
def _(op: BinFn, a: NonFiniteFloat, b:
|
|
690
|
-
return op(a.val, b
|
|
781
|
+
def _(op: BinFn, a: NonFiniteFloat, b: Union[FiniteFloat, RealBasedSymbolicFloat]):
|
|
782
|
+
return op(a.val, a.get_finite_comparable(b))
|
|
691
783
|
|
|
692
784
|
setup_binop(_, _ARITHMETIC_AND_COMPARISON_OPS)
|
|
693
785
|
|
|
694
|
-
def _(
|
|
695
|
-
|
|
696
|
-
|
|
786
|
+
# def _(
|
|
787
|
+
# op: BinFn, a: NonFiniteFloat, b: NonFiniteFloat
|
|
788
|
+
# ): # TODO: isn't this impossible (one must be symbolic)?
|
|
789
|
+
# return op(a.val, b.val) # type: ignore
|
|
697
790
|
|
|
698
|
-
setup_binop(_,
|
|
791
|
+
# setup_binop(_, _ARITHMETIC_AND_COMPARISON_OPS)
|
|
699
792
|
|
|
700
793
|
# int
|
|
701
794
|
def _(op: BinFn, a: SymbolicInt, b: SymbolicInt):
|
|
@@ -746,19 +839,6 @@ def setup_binops():
|
|
|
746
839
|
|
|
747
840
|
setup_binop(_, {ops.lshift, ops.rshift})
|
|
748
841
|
|
|
749
|
-
_AND_MASKS_TO_MOD = {
|
|
750
|
-
# It's common to use & to mask low bits. We can avoid realization by converting
|
|
751
|
-
# these situations into mod operations.
|
|
752
|
-
0x01: 2,
|
|
753
|
-
0x03: 4,
|
|
754
|
-
0x07: 8,
|
|
755
|
-
0x0F: 16,
|
|
756
|
-
0x1F: 32,
|
|
757
|
-
0x3F: 64,
|
|
758
|
-
0x7F: 128,
|
|
759
|
-
0xFF: 256,
|
|
760
|
-
}
|
|
761
|
-
|
|
762
842
|
def _(op: BinFn, a: Integral, b: Integral):
|
|
763
843
|
with NoTracing():
|
|
764
844
|
if isinstance(b, SymbolicInt):
|
|
@@ -769,9 +849,12 @@ def setup_binops():
|
|
|
769
849
|
b = realize(b)
|
|
770
850
|
if b == 0:
|
|
771
851
|
return 0
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
852
|
+
# It's common to use & to mask low bits. We can avoid realization by converting
|
|
853
|
+
# these situations into mod operations.
|
|
854
|
+
mask_mod = b + 1
|
|
855
|
+
if b > 0 and mask_mod & b == 0 and isinstance(a, SymbolicInt): # type: ignore
|
|
856
|
+
space = context_statespace()
|
|
857
|
+
if space.smt_fork(a.var >= 0, probability_true=0.75):
|
|
775
858
|
return SymbolicInt(a.var % mask_mod)
|
|
776
859
|
else:
|
|
777
860
|
return SymbolicInt(b - ((-a.var - 1) % mask_mod))
|
|
@@ -782,9 +865,8 @@ def setup_binops():
|
|
|
782
865
|
setup_binop(_, {ops.and_})
|
|
783
866
|
|
|
784
867
|
# TODO: is this necessary still?
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
): # Floor division over ints requires realization, at present
|
|
868
|
+
# Floor division over ints requires realization, at present:
|
|
869
|
+
def _(op: BinFn, a: Integral, b: Integral):
|
|
788
870
|
return op(a.__index__(), b.__index__()) # type: ignore
|
|
789
871
|
|
|
790
872
|
setup_binop(_, {ops.truediv})
|
|
@@ -794,35 +876,74 @@ def setup_binops():
|
|
|
794
876
|
|
|
795
877
|
setup_promotion(_, {ops.truediv})
|
|
796
878
|
|
|
797
|
-
|
|
879
|
+
# TODO : precise float divmod
|
|
880
|
+
def _float_divmod(
|
|
881
|
+
a: Union[NonFiniteFloat, FiniteFloat, RealBasedSymbolicFloat],
|
|
882
|
+
b: Union[NonFiniteFloat, FiniteFloat, RealBasedSymbolicFloat],
|
|
883
|
+
):
|
|
798
884
|
with NoTracing():
|
|
799
|
-
smt_a = SymbolicFloat._coerce_to_smt_sort(a)
|
|
800
|
-
smt_b = SymbolicFloat._coerce_to_smt_sort(b)
|
|
801
|
-
if smt_a is None or smt_b is None:
|
|
802
|
-
raise CrosshairInternal
|
|
803
885
|
space = context_statespace()
|
|
886
|
+
bval = RealBasedSymbolicFloat._coerce_to_smt_sort(a)
|
|
887
|
+
# division by zero is checked first
|
|
888
|
+
if not isinstance(b, NonFiniteFloat):
|
|
889
|
+
smt_b = (
|
|
890
|
+
RealBasedSymbolicFloat._smt_promote_literal(b.val)
|
|
891
|
+
if isinstance(b, FiniteFloat)
|
|
892
|
+
else b.var
|
|
893
|
+
)
|
|
894
|
+
if space.smt_fork(smt_b == 0):
|
|
895
|
+
raise ZeroDivisionError
|
|
896
|
+
# then the non-finite cases:
|
|
897
|
+
if isinstance(a, NonFiniteFloat):
|
|
898
|
+
return (nan, nan)
|
|
899
|
+
smt_a = (
|
|
900
|
+
RealBasedSymbolicFloat._smt_promote_literal(a.val)
|
|
901
|
+
if isinstance(a, FiniteFloat)
|
|
902
|
+
else a.var
|
|
903
|
+
)
|
|
904
|
+
assert smt_a is not None
|
|
905
|
+
if isinstance(b, NonFiniteFloat):
|
|
906
|
+
if isnan(b.val):
|
|
907
|
+
return (nan, nan)
|
|
908
|
+
# Deduced the rules for infinitiy based on experimentation!:
|
|
909
|
+
positive_a = space.smt_fork(smt_a >= 0)
|
|
910
|
+
positive_b = b.val == inf
|
|
911
|
+
if positive_a ^ positive_b:
|
|
912
|
+
if space.smt_fork(smt_a == 0):
|
|
913
|
+
return (0.0, 0.0) if positive_b else (-0.0, -0.0)
|
|
914
|
+
return (-1.0, b.val)
|
|
915
|
+
else:
|
|
916
|
+
return (0.0, a.val if isinstance(a, FiniteFloat) else a)
|
|
917
|
+
assert smt_b is not None
|
|
918
|
+
|
|
804
919
|
remainder = z3.Real(f"remainder{space.uniq()}")
|
|
805
920
|
modproduct = z3.Int(f"modproduct{space.uniq()}")
|
|
806
921
|
# From https://docs.python.org/3.3/reference/expressions.html#binary-arithmetic-operations:
|
|
807
922
|
# The modulo operator always yields a result with the same sign as its second operand (or zero).
|
|
808
923
|
# absolute value of the result is strictly smaller than the absolute value of the second operand.
|
|
809
924
|
space.add(smt_b * modproduct + remainder == smt_a)
|
|
810
|
-
if space.smt_fork(smt_b
|
|
811
|
-
raise ZeroDivisionError
|
|
812
|
-
elif space.smt_fork(smt_b > 0):
|
|
925
|
+
if space.smt_fork(smt_b > 0):
|
|
813
926
|
space.add(remainder >= 0)
|
|
814
927
|
space.add(remainder < smt_b)
|
|
815
928
|
else:
|
|
816
929
|
space.add(remainder <= 0)
|
|
817
930
|
space.add(smt_b < remainder)
|
|
818
|
-
return (SymbolicInt(modproduct),
|
|
931
|
+
return (SymbolicInt(modproduct), RealBasedSymbolicFloat(remainder))
|
|
819
932
|
|
|
820
|
-
def _(
|
|
933
|
+
def _(
|
|
934
|
+
op: BinFn,
|
|
935
|
+
a: Union[NonFiniteFloat, FiniteFloat, RealBasedSymbolicFloat],
|
|
936
|
+
b: Union[NonFiniteFloat, FiniteFloat, RealBasedSymbolicFloat],
|
|
937
|
+
):
|
|
821
938
|
return _float_divmod(a, b)[1]
|
|
822
939
|
|
|
823
940
|
setup_binop(_, {ops.mod})
|
|
824
941
|
|
|
825
|
-
def _(
|
|
942
|
+
def _(
|
|
943
|
+
op: BinFn,
|
|
944
|
+
a: Union[NonFiniteFloat, FiniteFloat, RealBasedSymbolicFloat],
|
|
945
|
+
b: Union[NonFiniteFloat, FiniteFloat, RealBasedSymbolicFloat],
|
|
946
|
+
):
|
|
826
947
|
return _float_divmod(a, b)[0]
|
|
827
948
|
|
|
828
949
|
setup_binop(_, {ops.floordiv})
|
|
@@ -865,6 +986,9 @@ class SymbolicNumberAble(SymbolicValue, Real):
|
|
|
865
986
|
def __eq__(self, other):
|
|
866
987
|
return numeric_binop(ops.eq, self, other)
|
|
867
988
|
|
|
989
|
+
def __ne__(self, other):
|
|
990
|
+
return numeric_binop(ops.ne, self, other)
|
|
991
|
+
|
|
868
992
|
def __add__(self, other):
|
|
869
993
|
return numeric_binop(ops.add, self, other)
|
|
870
994
|
|
|
@@ -885,7 +1009,7 @@ class SymbolicNumberAble(SymbolicValue, Real):
|
|
|
885
1009
|
|
|
886
1010
|
def __pow__(self, other, mod=None):
|
|
887
1011
|
if mod is not None:
|
|
888
|
-
return pow(realize(self),
|
|
1012
|
+
return pow(realize(self), realize(other), realize(mod))
|
|
889
1013
|
return numeric_binop(ops.pow, self, other)
|
|
890
1014
|
|
|
891
1015
|
def __rpow__(self, other, mod=None):
|
|
@@ -989,6 +1113,7 @@ class SymbolicBool(SymbolicIntable, AtomicSymbolicValue):
|
|
|
989
1113
|
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type = bool):
|
|
990
1114
|
assert typ == bool
|
|
991
1115
|
SymbolicValue.__init__(self, smtvar, typ)
|
|
1116
|
+
self._ch_decision_triggers: List[Callable[[bool], None]] = []
|
|
992
1117
|
|
|
993
1118
|
@classmethod
|
|
994
1119
|
def _ch_smt_sort(cls) -> z3.SortRef:
|
|
@@ -999,14 +1124,21 @@ class SymbolicBool(SymbolicIntable, AtomicSymbolicValue):
|
|
|
999
1124
|
return bool
|
|
1000
1125
|
|
|
1001
1126
|
@classmethod
|
|
1002
|
-
def _smt_promote_literal(cls, literal) -> Optional[z3.
|
|
1127
|
+
def _smt_promote_literal(cls, literal) -> Optional[z3.ExprRef]:
|
|
1003
1128
|
if isinstance(literal, bool):
|
|
1004
1129
|
return z3.BoolVal(literal)
|
|
1005
1130
|
return None
|
|
1006
1131
|
|
|
1007
|
-
def __ch_realize__(self) ->
|
|
1132
|
+
def __ch_realize__(self) -> bool:
|
|
1008
1133
|
with NoTracing():
|
|
1009
|
-
|
|
1134
|
+
realized = context_statespace().choose_possible(self.var)
|
|
1135
|
+
for trigger in self._ch_decision_triggers:
|
|
1136
|
+
trigger(realized)
|
|
1137
|
+
return realized
|
|
1138
|
+
|
|
1139
|
+
def __abs__(self):
|
|
1140
|
+
with NoTracing():
|
|
1141
|
+
return SymbolicInt(z3.If(self.var, 1, 0))
|
|
1010
1142
|
|
|
1011
1143
|
def __neg__(self):
|
|
1012
1144
|
with NoTracing():
|
|
@@ -1022,9 +1154,8 @@ class SymbolicBool(SymbolicIntable, AtomicSymbolicValue):
|
|
|
1022
1154
|
with NoTracing():
|
|
1023
1155
|
return SymbolicInt(z3.If(self.var, 1, 0))
|
|
1024
1156
|
|
|
1025
|
-
def __bool__(self):
|
|
1026
|
-
|
|
1027
|
-
return context_statespace().choose_possible(self.var)
|
|
1157
|
+
def __bool__(self) -> bool:
|
|
1158
|
+
return self.__ch_realize__()
|
|
1028
1159
|
|
|
1029
1160
|
def __int__(self):
|
|
1030
1161
|
with NoTracing():
|
|
@@ -1032,7 +1163,10 @@ class SymbolicBool(SymbolicIntable, AtomicSymbolicValue):
|
|
|
1032
1163
|
|
|
1033
1164
|
def __float__(self):
|
|
1034
1165
|
with NoTracing():
|
|
1035
|
-
|
|
1166
|
+
symbolic_type = context_statespace().extra(ModelingDirector).choose(float)
|
|
1167
|
+
smt_false = symbolic_type._coerce_to_smt_sort(0)
|
|
1168
|
+
smt_true = symbolic_type._coerce_to_smt_sort(1)
|
|
1169
|
+
return symbolic_type(z3.If(self.var, smt_true, smt_false))
|
|
1036
1170
|
|
|
1037
1171
|
def __complex__(self):
|
|
1038
1172
|
with NoTracing():
|
|
@@ -1046,6 +1180,10 @@ class SymbolicBool(SymbolicIntable, AtomicSymbolicValue):
|
|
|
1046
1180
|
class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
|
|
1047
1181
|
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type = int):
|
|
1048
1182
|
assert typ == int
|
|
1183
|
+
if (not isinstance(smtvar, str)) and (not smtvar.is_int()):
|
|
1184
|
+
raise CrossHairInternal(
|
|
1185
|
+
f"non-integer SMT value given to SymbolicInt ({smtvar})"
|
|
1186
|
+
)
|
|
1049
1187
|
SymbolicIntable.__init__(self, smtvar, typ)
|
|
1050
1188
|
|
|
1051
1189
|
@classmethod
|
|
@@ -1057,7 +1195,7 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
|
|
|
1057
1195
|
return int
|
|
1058
1196
|
|
|
1059
1197
|
@classmethod
|
|
1060
|
-
def _smt_promote_literal(cls, literal) -> Optional[z3.
|
|
1198
|
+
def _smt_promote_literal(cls, literal) -> Optional[z3.ExprRef]:
|
|
1061
1199
|
if isinstance(literal, int):
|
|
1062
1200
|
return z3IntVal(literal)
|
|
1063
1201
|
return None
|
|
@@ -1071,12 +1209,18 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
|
|
|
1071
1209
|
|
|
1072
1210
|
def __repr__(self):
|
|
1073
1211
|
if self < 0:
|
|
1074
|
-
return "-" +
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1212
|
+
return "-" + (-self).__repr__()
|
|
1213
|
+
if self < 10:
|
|
1214
|
+
return LazyIntSymbolicStr([48 + self])
|
|
1215
|
+
codepoints = [48 + (self % 10)]
|
|
1216
|
+
cur_divisor = 10
|
|
1217
|
+
while True:
|
|
1218
|
+
leftover = self // cur_divisor
|
|
1219
|
+
if leftover != 0:
|
|
1220
|
+
codepoints.append(48 + (leftover % 10))
|
|
1221
|
+
cur_divisor *= 10
|
|
1222
|
+
else:
|
|
1223
|
+
break
|
|
1080
1224
|
with NoTracing():
|
|
1081
1225
|
codepoints.reverse()
|
|
1082
1226
|
return LazyIntSymbolicStr(codepoints)
|
|
@@ -1102,7 +1246,18 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
|
|
|
1102
1246
|
|
|
1103
1247
|
def __float__(self):
|
|
1104
1248
|
with NoTracing():
|
|
1105
|
-
|
|
1249
|
+
symbolic_type = context_statespace().extra(ModelingDirector).choose(float)
|
|
1250
|
+
if symbolic_type is RealBasedSymbolicFloat:
|
|
1251
|
+
return RealBasedSymbolicFloat(z3.ToReal(self.var))
|
|
1252
|
+
elif symbolic_type is PreciseIeeeSymbolicFloat:
|
|
1253
|
+
# TODO: We can likely do better with: int -> bit vector -> float
|
|
1254
|
+
return PreciseIeeeSymbolicFloat(
|
|
1255
|
+
z3.fpRealToFP(
|
|
1256
|
+
z3.RNE(), z3.ToReal(self.var), _PRECISE_IEEE_FLOAT_SORT
|
|
1257
|
+
)
|
|
1258
|
+
)
|
|
1259
|
+
else:
|
|
1260
|
+
raise CrossHairInternal
|
|
1106
1261
|
|
|
1107
1262
|
def __complex__(self):
|
|
1108
1263
|
return complex(self.__float__())
|
|
@@ -1165,7 +1320,7 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
|
|
|
1165
1320
|
z3.If(val < 128, 7, 8)))))))))
|
|
1166
1321
|
# fmt: on
|
|
1167
1322
|
|
|
1168
|
-
if
|
|
1323
|
+
if version_info >= (3, 12):
|
|
1169
1324
|
|
|
1170
1325
|
def is_integer(self):
|
|
1171
1326
|
return True
|
|
@@ -1199,41 +1354,123 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
|
|
|
1199
1354
|
return (self, 1)
|
|
1200
1355
|
|
|
1201
1356
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1357
|
+
class SymbolicBoundedInt(SymbolicInt):
|
|
1358
|
+
def __init__(
|
|
1359
|
+
self,
|
|
1360
|
+
smtvar: Union[str, z3.ExprRef],
|
|
1361
|
+
typ: Type,
|
|
1362
|
+
minimum: Optional[int] = None,
|
|
1363
|
+
maximum: Optional[int] = None,
|
|
1364
|
+
):
|
|
1365
|
+
SymbolicInt.__init__(self, smtvar, typ)
|
|
1366
|
+
space = context_statespace()
|
|
1367
|
+
self._ch_minimum = minimum
|
|
1368
|
+
self._ch_maximum = maximum
|
|
1369
|
+
if minimum is not None:
|
|
1370
|
+
space.add(self.var >= minimum)
|
|
1371
|
+
if maximum is not None:
|
|
1372
|
+
space.add(self.var <= maximum)
|
|
1373
|
+
|
|
1374
|
+
def __lt__(self, other):
|
|
1375
|
+
with NoTracing():
|
|
1376
|
+
if isinstance(other, int):
|
|
1377
|
+
if self._ch_minimum is not None and other < self._ch_minimum:
|
|
1378
|
+
return False
|
|
1379
|
+
if self._ch_maximum is not None and other > self._ch_maximum:
|
|
1380
|
+
return True
|
|
1381
|
+
with ResumedTracing():
|
|
1382
|
+
ret = super().__lt__(other)
|
|
1383
|
+
if isinstance(other, int):
|
|
1384
|
+
if isinstance(ret, SymbolicBool):
|
|
1385
|
+
ret._ch_decision_triggers.append(
|
|
1386
|
+
lambda islt: (
|
|
1387
|
+
self._ch_intersect_bounds(None, other - 1)
|
|
1388
|
+
if islt
|
|
1389
|
+
else self._ch_intersect_bounds(other, None)
|
|
1390
|
+
)
|
|
1391
|
+
)
|
|
1392
|
+
return ret
|
|
1212
1393
|
|
|
1394
|
+
def __gt__(self, other):
|
|
1395
|
+
with NoTracing():
|
|
1396
|
+
if isinstance(other, int):
|
|
1397
|
+
if self._ch_maximum is not None and other > self._ch_maximum:
|
|
1398
|
+
return False
|
|
1399
|
+
if self._ch_minimum is not None and other < self._ch_minimum:
|
|
1400
|
+
return True
|
|
1401
|
+
with ResumedTracing():
|
|
1402
|
+
ret = super().__gt__(other)
|
|
1403
|
+
if isinstance(other, int):
|
|
1404
|
+
if isinstance(ret, SymbolicBool):
|
|
1405
|
+
ret._ch_decision_triggers.append(
|
|
1406
|
+
lambda isgt: (
|
|
1407
|
+
self._ch_intersect_bounds(other + 1, None)
|
|
1408
|
+
if isgt
|
|
1409
|
+
else self._ch_intersect_bounds(None, other)
|
|
1410
|
+
)
|
|
1411
|
+
)
|
|
1412
|
+
return ret
|
|
1213
1413
|
|
|
1214
|
-
|
|
1414
|
+
def _ch_intersect_bounds(
|
|
1415
|
+
self, new_min: Optional[int], new_max: Optional[int]
|
|
1416
|
+
) -> None:
|
|
1417
|
+
space = context_statespace()
|
|
1418
|
+
if new_min is not None:
|
|
1419
|
+
if self._ch_minimum is None or new_min > self._ch_minimum:
|
|
1420
|
+
self._ch_minimum = new_min
|
|
1421
|
+
space.add(
|
|
1422
|
+
self.var >= int(new_min)
|
|
1423
|
+
) # cast b/c z3 isn't tolerant of enum ints
|
|
1424
|
+
if new_max is not None:
|
|
1425
|
+
if self._ch_maximum is None or new_max < self._ch_maximum:
|
|
1426
|
+
self._ch_maximum = new_max
|
|
1427
|
+
space.add(
|
|
1428
|
+
self.var <= int(new_max)
|
|
1429
|
+
) # cast b/c z3 isn't tolerant of enum ints
|
|
1215
1430
|
|
|
1431
|
+
def _unary_op(self, op):
|
|
1432
|
+
with NoTracing():
|
|
1433
|
+
if op is ops.neg:
|
|
1434
|
+
new_min = -self._ch_maximum if self._ch_maximum is not None else None
|
|
1435
|
+
new_max = -self._ch_minimum if self._ch_minimum is not None else None
|
|
1436
|
+
return SymbolicBoundedInt(op(self.var), int, new_min, new_max)
|
|
1437
|
+
elif op is ops.abs:
|
|
1438
|
+
if self._ch_minimum is not None and self._ch_minimum >= 0:
|
|
1439
|
+
return self
|
|
1440
|
+
if self._ch_maximum is not None and self._ch_maximum <= 0:
|
|
1441
|
+
return SymbolicBoundedInt(
|
|
1442
|
+
-self.var,
|
|
1443
|
+
int,
|
|
1444
|
+
-self._ch_maximum,
|
|
1445
|
+
-self._ch_minimum if self._ch_minimum is not None else None,
|
|
1446
|
+
)
|
|
1447
|
+
if self._ch_maximum is not None or self._ch_minimum is not None:
|
|
1448
|
+
# range includes zero; compute max abs value
|
|
1449
|
+
max_abs = max(
|
|
1450
|
+
abs(self._ch_minimum or 0),
|
|
1451
|
+
abs(self._ch_maximum or 0),
|
|
1452
|
+
)
|
|
1453
|
+
return SymbolicInt(op(self.var), int, 0, max_abs)
|
|
1454
|
+
return SymbolicInt(op(self.var), int, 0, None)
|
|
1455
|
+
elif op is ops.pos:
|
|
1456
|
+
return self
|
|
1457
|
+
elif op is ops.invert:
|
|
1458
|
+
pass # TODO: we could do something for bitwise negation
|
|
1459
|
+
# Default to unbounded:
|
|
1460
|
+
return SymbolicInt(op(self.var), int)
|
|
1216
1461
|
|
|
1217
|
-
class SymbolicFloat(SymbolicNumberAble, AtomicSymbolicValue):
|
|
1218
|
-
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type = float):
|
|
1219
|
-
assert typ is float, f"SymbolicFloat with unexpected python type ({type(typ)})"
|
|
1220
|
-
context_statespace().cap_result_at_unknown()
|
|
1221
|
-
SymbolicValue.__init__(self, smtvar, typ)
|
|
1222
1462
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1463
|
+
def make_bounded_int(
|
|
1464
|
+
varname: str, minimum: Optional[int] = None, maximum: Optional[int] = None
|
|
1465
|
+
) -> SymbolicInt:
|
|
1466
|
+
return SymbolicBoundedInt(varname, int, minimum, maximum)
|
|
1467
|
+
|
|
1226
1468
|
|
|
1469
|
+
class SymbolicFloat(SymbolicNumberAble, AtomicSymbolicValue):
|
|
1227
1470
|
@classmethod
|
|
1228
1471
|
def _pytype(cls) -> Type:
|
|
1229
1472
|
return float
|
|
1230
1473
|
|
|
1231
|
-
@classmethod
|
|
1232
|
-
def _smt_promote_literal(cls, literal) -> Optional[z3.SortRef]:
|
|
1233
|
-
if isinstance(literal, float):
|
|
1234
|
-
return z3.RealVal(literal)
|
|
1235
|
-
return None
|
|
1236
|
-
|
|
1237
1474
|
def __ch_realize__(self) -> object:
|
|
1238
1475
|
return context_statespace().find_model_value(self.var).__float__() # type: ignore
|
|
1239
1476
|
|
|
@@ -1247,11 +1484,6 @@ class SymbolicFloat(SymbolicNumberAble, AtomicSymbolicValue):
|
|
|
1247
1484
|
with NoTracing():
|
|
1248
1485
|
return SymbolicBool(self.var != 0).__bool__()
|
|
1249
1486
|
|
|
1250
|
-
def __int__(self):
|
|
1251
|
-
with NoTracing():
|
|
1252
|
-
var = self.var
|
|
1253
|
-
return SymbolicInt(z3.If(var >= 0, z3.ToInt(var), -z3.ToInt(-var)))
|
|
1254
|
-
|
|
1255
1487
|
def __float__(self):
|
|
1256
1488
|
with NoTracing():
|
|
1257
1489
|
return self.__ch_realize__()
|
|
@@ -1260,6 +1492,163 @@ class SymbolicFloat(SymbolicNumberAble, AtomicSymbolicValue):
|
|
|
1260
1492
|
with NoTracing():
|
|
1261
1493
|
return complex(self.__float__())
|
|
1262
1494
|
|
|
1495
|
+
def hex(self) -> str:
|
|
1496
|
+
return realize(self).hex()
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
_PRECISE_IEEE_FLOAT_SORT = {
|
|
1500
|
+
11: z3.Float16(),
|
|
1501
|
+
24: z3.Float32(),
|
|
1502
|
+
53: z3.Float64(),
|
|
1503
|
+
}[sys.float_info.mant_dig]
|
|
1504
|
+
|
|
1505
|
+
|
|
1506
|
+
class PreciseIeeeSymbolicFloat(SymbolicFloat):
|
|
1507
|
+
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type = float):
|
|
1508
|
+
if not isinstance(smtvar, str) and not z3.is_fp(smtvar):
|
|
1509
|
+
raise CrossHairInternal(
|
|
1510
|
+
f"non-float SMT value ({name_of_type(type(smtvar))}) given to PreciseIeeeSymbolicFloat"
|
|
1511
|
+
)
|
|
1512
|
+
SymbolicValue.__init__(self, smtvar, typ)
|
|
1513
|
+
|
|
1514
|
+
@classmethod
|
|
1515
|
+
def _ch_smt_sort(cls) -> z3.SortRef:
|
|
1516
|
+
return _PRECISE_IEEE_FLOAT_SORT
|
|
1517
|
+
|
|
1518
|
+
@classmethod
|
|
1519
|
+
def _smt_promote_literal(cls, literal) -> Optional[z3.ExprRef]:
|
|
1520
|
+
if isinstance(literal, float):
|
|
1521
|
+
return z3.FPVal(literal, cls._ch_smt_sort())
|
|
1522
|
+
return None
|
|
1523
|
+
|
|
1524
|
+
def __eq__(self, other):
|
|
1525
|
+
with NoTracing():
|
|
1526
|
+
coerced = type(self)._coerce_to_smt_sort(other)
|
|
1527
|
+
if coerced is None:
|
|
1528
|
+
return False
|
|
1529
|
+
return SymbolicBool(fpEQ(self.var, coerced))
|
|
1530
|
+
|
|
1531
|
+
# __hash__ has to be explicitly reassigned because we define __eq__
|
|
1532
|
+
__hash__ = SymbolicFloat.__hash__
|
|
1533
|
+
|
|
1534
|
+
def __bool__(self):
|
|
1535
|
+
with NoTracing():
|
|
1536
|
+
return not SymbolicBool(z3.fpIsZero(self.var))
|
|
1537
|
+
|
|
1538
|
+
def __ne__(self, other):
|
|
1539
|
+
with NoTracing():
|
|
1540
|
+
coerced = type(self)._coerce_to_smt_sort(other)
|
|
1541
|
+
if coerced is None:
|
|
1542
|
+
return True
|
|
1543
|
+
return SymbolicBool(z3.Not(fpEQ(self.var, coerced)))
|
|
1544
|
+
|
|
1545
|
+
def __int__(self):
|
|
1546
|
+
with NoTracing():
|
|
1547
|
+
self._check_finite_convert_to("integer")
|
|
1548
|
+
return SymbolicInt(
|
|
1549
|
+
z3.ToInt(z3.fpToReal(z3.fpRoundToIntegral(z3.RTZ(), self.var)))
|
|
1550
|
+
)
|
|
1551
|
+
|
|
1552
|
+
def __round__(self, ndigits=None):
|
|
1553
|
+
self_is_finite = isfinite(self)
|
|
1554
|
+
if ndigits is None:
|
|
1555
|
+
if not self_is_finite:
|
|
1556
|
+
# CPython only errors like this when ndigits is None (for ... reasons)
|
|
1557
|
+
if isinf(self):
|
|
1558
|
+
raise OverflowError("cannot convert float infinity to integer")
|
|
1559
|
+
else:
|
|
1560
|
+
raise ValueError("cannot convert float NaN to integer")
|
|
1561
|
+
elif ndigits != 0:
|
|
1562
|
+
if not isinstance(ndigits, int):
|
|
1563
|
+
raise TypeError(
|
|
1564
|
+
f"'{name_of_type(type(ndigits))}' object cannot be interpreted as an integer"
|
|
1565
|
+
)
|
|
1566
|
+
factor = 10**ndigits
|
|
1567
|
+
return round(self * factor, 0) / factor
|
|
1568
|
+
with NoTracing():
|
|
1569
|
+
if self_is_finite:
|
|
1570
|
+
smt_rounded_real = z3.fpRoundToIntegral(z3.RNE(), self.var)
|
|
1571
|
+
if ndigits is None:
|
|
1572
|
+
return SymbolicInt(z3.ToInt(z3.fpToReal(smt_rounded_real)))
|
|
1573
|
+
else:
|
|
1574
|
+
return PreciseIeeeSymbolicFloat(smt_rounded_real)
|
|
1575
|
+
# Non-finites returns themselves if you supply ndigits:
|
|
1576
|
+
return self
|
|
1577
|
+
|
|
1578
|
+
def _check_finite_convert_to(self, target: str) -> None:
|
|
1579
|
+
if isfinite(self):
|
|
1580
|
+
return
|
|
1581
|
+
elif isinf(self):
|
|
1582
|
+
raise OverflowError("cannot convert Infinity to " + target)
|
|
1583
|
+
else:
|
|
1584
|
+
raise ValueError("cannot convert NaN to " + target)
|
|
1585
|
+
|
|
1586
|
+
def __floor__(self):
|
|
1587
|
+
with NoTracing():
|
|
1588
|
+
self._check_finite_convert_to("integer")
|
|
1589
|
+
return PreciseIeeeSymbolicFloat(z3.fpRoundToIntegral(z3.RTN(), self.var))
|
|
1590
|
+
|
|
1591
|
+
def __floordiv__(self, other):
|
|
1592
|
+
r = self / other
|
|
1593
|
+
with NoTracing():
|
|
1594
|
+
return PreciseIeeeSymbolicFloat(z3.fpRoundToIntegral(z3.RTN(), r.var))
|
|
1595
|
+
|
|
1596
|
+
def __rfloordiv__(self, other):
|
|
1597
|
+
r = other / self
|
|
1598
|
+
with NoTracing():
|
|
1599
|
+
return PreciseIeeeSymbolicFloat(z3.fpRoundToIntegral(z3.RTN(), r.var))
|
|
1600
|
+
|
|
1601
|
+
def __ceil__(self):
|
|
1602
|
+
with NoTracing():
|
|
1603
|
+
self._check_finite_convert_to("integer")
|
|
1604
|
+
return PreciseIeeeSymbolicFloat(z3.fpRoundToIntegral(z3.RTP(), self.var))
|
|
1605
|
+
|
|
1606
|
+
def __pow__(self, other, mod=None):
|
|
1607
|
+
# TODO: consider losen-ing a little
|
|
1608
|
+
return pow(realize(self), realize(other), realize(mod))
|
|
1609
|
+
|
|
1610
|
+
def __trunc__(self):
|
|
1611
|
+
with NoTracing():
|
|
1612
|
+
self._check_finite_convert_to("integer")
|
|
1613
|
+
return PreciseIeeeSymbolicFloat(z3.fpRoundToIntegral(z3.RTZ(), self.var))
|
|
1614
|
+
|
|
1615
|
+
def as_integer_ratio(self) -> Tuple[Integral, Integral]:
|
|
1616
|
+
with NoTracing():
|
|
1617
|
+
self._check_finite_convert_to("integer ratio")
|
|
1618
|
+
return RealBasedSymbolicFloat(z3.fpToReal(self.var)).as_integer_ratio()
|
|
1619
|
+
|
|
1620
|
+
def is_integer(self) -> SymbolicBool:
|
|
1621
|
+
return self == self.__int__()
|
|
1622
|
+
# with NoTracing():
|
|
1623
|
+
# return SymbolicBool(z3.IsInt(self.var))
|
|
1624
|
+
|
|
1625
|
+
|
|
1626
|
+
_Z3_ONE_HALF = z3.RealVal("1/2")
|
|
1627
|
+
|
|
1628
|
+
|
|
1629
|
+
class RealBasedSymbolicFloat(SymbolicFloat):
|
|
1630
|
+
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type = float):
|
|
1631
|
+
assert (
|
|
1632
|
+
typ is float
|
|
1633
|
+
), f"RealBasedSymbolicFloat with unexpected python type ({type(typ)})"
|
|
1634
|
+
context_statespace().cap_result_at_unknown()
|
|
1635
|
+
SymbolicValue.__init__(self, smtvar, typ)
|
|
1636
|
+
|
|
1637
|
+
@classmethod
|
|
1638
|
+
def _ch_smt_sort(cls) -> z3.SortRef:
|
|
1639
|
+
return z3.RealSort()
|
|
1640
|
+
|
|
1641
|
+
@classmethod
|
|
1642
|
+
def _smt_promote_literal(cls, literal) -> Optional[z3.ExprRef]:
|
|
1643
|
+
if isinstance(literal, float) and isfinite(literal):
|
|
1644
|
+
return z3.RealVal(literal)
|
|
1645
|
+
return None
|
|
1646
|
+
|
|
1647
|
+
def __int__(self):
|
|
1648
|
+
with NoTracing():
|
|
1649
|
+
var = self.var
|
|
1650
|
+
return SymbolicInt(z3.If(var >= 0, z3.ToInt(var), -z3.ToInt(-var)))
|
|
1651
|
+
|
|
1263
1652
|
def __round__(self, ndigits=None):
|
|
1264
1653
|
if ndigits is not None:
|
|
1265
1654
|
factor = 10 ** realize(
|
|
@@ -1308,6 +1697,7 @@ class SymbolicFloat(SymbolicNumberAble, AtomicSymbolicValue):
|
|
|
1308
1697
|
denominator = SymbolicInt("denominator" + space.uniq())
|
|
1309
1698
|
space.add(denominator.var > 0)
|
|
1310
1699
|
space.add(numerator.var == denominator.var * self.var)
|
|
1700
|
+
|
|
1311
1701
|
# There are many valid integer ratios to return. Experimentally, both
|
|
1312
1702
|
# z3 and CPython tend to pick the same ones. But verify this, while
|
|
1313
1703
|
# deferring materialization:
|
|
@@ -1323,9 +1713,6 @@ class SymbolicFloat(SymbolicNumberAble, AtomicSymbolicValue):
|
|
|
1323
1713
|
with NoTracing():
|
|
1324
1714
|
return SymbolicBool(z3.IsInt(self.var))
|
|
1325
1715
|
|
|
1326
|
-
def hex(self) -> str:
|
|
1327
|
-
return realize(self).hex()
|
|
1328
|
-
|
|
1329
1716
|
|
|
1330
1717
|
class SymbolicDictOrSet(SymbolicValue):
|
|
1331
1718
|
"""
|
|
@@ -1335,15 +1722,16 @@ class SymbolicDictOrSet(SymbolicValue):
|
|
|
1335
1722
|
|
|
1336
1723
|
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type):
|
|
1337
1724
|
self.key_pytype = normalize_pytype(type_arg_of(typ, 0))
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1725
|
+
space = context_statespace()
|
|
1726
|
+
ch_type = space.extra(ModelingDirector).get(self.key_pytype)
|
|
1727
|
+
if ch_type:
|
|
1728
|
+
self.ch_key_type: Optional[Type[AtomicSymbolicValue]] = ch_type
|
|
1341
1729
|
self.smt_key_sort = self.ch_key_type._ch_smt_sort()
|
|
1342
1730
|
else:
|
|
1343
1731
|
self.ch_key_type = None
|
|
1344
1732
|
self.smt_key_sort = HeapRef
|
|
1345
1733
|
SymbolicValue.__init__(self, smtvar, typ)
|
|
1346
|
-
|
|
1734
|
+
space.add(self._len() >= 0)
|
|
1347
1735
|
|
|
1348
1736
|
def __ch_realize__(self):
|
|
1349
1737
|
return origin_of(self.python_type)(self)
|
|
@@ -1370,9 +1758,9 @@ class SymbolicDict(SymbolicDictOrSet, collections.abc.Mapping):
|
|
|
1370
1758
|
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type):
|
|
1371
1759
|
space = context_statespace()
|
|
1372
1760
|
self.val_pytype = normalize_pytype(type_arg_of(typ, 1))
|
|
1373
|
-
|
|
1374
|
-
if
|
|
1375
|
-
self.ch_val_type: Optional[Type[AtomicSymbolicValue]] =
|
|
1761
|
+
val_ch_type = space.extra(ModelingDirector).get(self.val_pytype)
|
|
1762
|
+
if val_ch_type:
|
|
1763
|
+
self.ch_val_type: Optional[Type[AtomicSymbolicValue]] = val_ch_type
|
|
1376
1764
|
self.smt_val_sort = self.ch_val_type._ch_smt_sort()
|
|
1377
1765
|
else:
|
|
1378
1766
|
self.ch_val_type = None
|
|
@@ -1385,7 +1773,7 @@ class SymbolicDict(SymbolicDictOrSet, collections.abc.Mapping):
|
|
|
1385
1773
|
self.val_constructor = arr_var.sort().range().constructor(1)
|
|
1386
1774
|
self.val_accessor = arr_var.sort().range().accessor(1, 0)
|
|
1387
1775
|
self.empty = z3.K(arr_var.sort().domain(), self.val_missing_constructor())
|
|
1388
|
-
self._iter_cache: List[z3.Const] = []
|
|
1776
|
+
self._iter_cache: List[z3.Const] = [] # TODO: is this used?
|
|
1389
1777
|
space.add((arr_var == self.empty) == (len_var == 0))
|
|
1390
1778
|
|
|
1391
1779
|
def dict_can_be_iterated():
|
|
@@ -1431,6 +1819,10 @@ class SymbolicDict(SymbolicDictOrSet, collections.abc.Mapping):
|
|
|
1431
1819
|
|
|
1432
1820
|
def __repr__(self):
|
|
1433
1821
|
return str(dict(self.items()))
|
|
1822
|
+
# TODO: symbolic repr; something like this?:
|
|
1823
|
+
# itemiter = self.items()
|
|
1824
|
+
# with NoTracing():
|
|
1825
|
+
# return "{" + ", ".join(f"{repr(deep_realize(k))}: {repr(deep_realize(v))}" for k, v in tracing_iter(itemiter)) + "}"
|
|
1434
1826
|
|
|
1435
1827
|
# TODO: __contains__ could be implemented without any path forks
|
|
1436
1828
|
|
|
@@ -1484,7 +1876,7 @@ class SymbolicDict(SymbolicDictOrSet, collections.abc.Mapping):
|
|
|
1484
1876
|
space.add(is_missing(z3.Select(remaining, k)))
|
|
1485
1877
|
|
|
1486
1878
|
if idx > len(iter_cache):
|
|
1487
|
-
raise
|
|
1879
|
+
raise CrossHairInternal()
|
|
1488
1880
|
if idx == len(iter_cache):
|
|
1489
1881
|
iter_cache.append(k)
|
|
1490
1882
|
else:
|
|
@@ -1504,26 +1896,69 @@ class SymbolicDict(SymbolicDictOrSet, collections.abc.Mapping):
|
|
|
1504
1896
|
return SymbolicDict(self.var, self.python_type)
|
|
1505
1897
|
|
|
1506
1898
|
|
|
1507
|
-
class
|
|
1899
|
+
class SymbolicFrozenSet(SymbolicDictOrSet, FrozenSetBase):
|
|
1508
1900
|
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type):
|
|
1901
|
+
if origin_of(typ) != frozenset:
|
|
1902
|
+
raise CrossHairInternal
|
|
1509
1903
|
SymbolicDictOrSet.__init__(self, smtvar, typ)
|
|
1510
1904
|
self._iter_cache: List[z3.Const] = []
|
|
1511
1905
|
self.empty = z3.K(self._arr().sort().domain(), False)
|
|
1512
|
-
|
|
1906
|
+
space = context_statespace()
|
|
1907
|
+
space.add((self._arr() == self.empty) == (self._len() == 0))
|
|
1908
|
+
space.defer_assumption("symbolic set is consistent", self._is_consistent)
|
|
1909
|
+
|
|
1910
|
+
@assert_tracing(True)
|
|
1911
|
+
def _is_consistent(self) -> SymbolicBool:
|
|
1912
|
+
"""
|
|
1913
|
+
Checks whether the set size is consistent with the SMT array size
|
|
1914
|
+
|
|
1915
|
+
Realizes the size. (but not the values)
|
|
1916
|
+
"""
|
|
1917
|
+
my_len = len(self)
|
|
1918
|
+
with NoTracing():
|
|
1919
|
+
target_len = 0
|
|
1920
|
+
space = context_statespace()
|
|
1921
|
+
comparison_smt_array = self.empty
|
|
1922
|
+
items = []
|
|
1923
|
+
while True:
|
|
1924
|
+
with ResumedTracing():
|
|
1925
|
+
if target_len == my_len:
|
|
1926
|
+
break
|
|
1927
|
+
item = z3.Const(f"set_{target_len}_{space.uniq()}", self.smt_key_sort)
|
|
1928
|
+
items.append(item)
|
|
1929
|
+
comparison_smt_array = z3.Store(comparison_smt_array, item, True)
|
|
1930
|
+
target_len += 1
|
|
1931
|
+
if len(items) >= 2:
|
|
1932
|
+
space.add(z3.Distinct(*items))
|
|
1933
|
+
return SymbolicBool(comparison_smt_array == self._arr())
|
|
1934
|
+
|
|
1935
|
+
def __ch_is_deeply_immutable__(self) -> bool:
|
|
1936
|
+
return True
|
|
1513
1937
|
|
|
1514
1938
|
def __ch_realize__(self):
|
|
1515
1939
|
return python_type(self)(map(realize, self))
|
|
1516
1940
|
|
|
1941
|
+
def __repr__(self):
|
|
1942
|
+
if self:
|
|
1943
|
+
return "frozenset({" + ", ".join(map(repr, self)) + "})"
|
|
1944
|
+
else:
|
|
1945
|
+
return "frozenset()"
|
|
1946
|
+
|
|
1947
|
+
def __hash__(self):
|
|
1948
|
+
return deep_realize(self).__hash__()
|
|
1949
|
+
|
|
1517
1950
|
def __eq__(self, other):
|
|
1518
1951
|
(self_arr, self_len) = self.var
|
|
1519
|
-
if isinstance(other,
|
|
1952
|
+
if isinstance(other, SymbolicFrozenSet):
|
|
1520
1953
|
(other_arr, other_len) = other.var
|
|
1521
1954
|
if other_arr.sort() == self_arr.sort():
|
|
1522
1955
|
# TODO: this is wrong for HeapRef sets (which could customize __eq__)
|
|
1523
1956
|
return SymbolicBool(
|
|
1524
1957
|
z3.And(self_len == other_len, self_arr == other_arr)
|
|
1525
1958
|
)
|
|
1526
|
-
if not isinstance(
|
|
1959
|
+
if not isinstance(
|
|
1960
|
+
other, (set, frozenset, SymbolicFrozenSet, collections.abc.Set)
|
|
1961
|
+
):
|
|
1527
1962
|
return False
|
|
1528
1963
|
# Manually check equality. Drive size from the (likely) concrete value 'other':
|
|
1529
1964
|
if len(self) != len(other):
|
|
@@ -1578,16 +2013,24 @@ class SymbolicSet(SymbolicDictOrSet, SetBase, collections.abc.Set):
|
|
|
1578
2013
|
arr_sort = self._arr().sort()
|
|
1579
2014
|
keys_on_heap = is_heapref_sort(arr_sort.domain())
|
|
1580
2015
|
already_yielded = []
|
|
1581
|
-
while
|
|
1582
|
-
if
|
|
1583
|
-
|
|
1584
|
-
|
|
2016
|
+
while True:
|
|
2017
|
+
if idx < len(iter_cache):
|
|
2018
|
+
k = iter_cache[idx]
|
|
2019
|
+
elif SymbolicBool(idx < len_var).__bool__():
|
|
2020
|
+
if space.choose_possible(
|
|
2021
|
+
arr_var == self.empty, probability_true=0.0
|
|
2022
|
+
):
|
|
2023
|
+
raise IgnoreAttempt("SymbolicFrozenSet in inconsistent state")
|
|
2024
|
+
k = z3.Const("k" + str(idx) + space.uniq(), arr_sort.domain())
|
|
2025
|
+
else:
|
|
2026
|
+
break
|
|
1585
2027
|
remaining = z3.Const("remaining" + str(idx) + space.uniq(), arr_sort)
|
|
1586
2028
|
space.add(arr_var == z3.Store(remaining, k, True))
|
|
2029
|
+
# TODO: this seems like it won't work the same for heaprefs which can be distinct but equal:
|
|
1587
2030
|
space.add(z3.Not(z3.Select(remaining, k)))
|
|
1588
2031
|
|
|
1589
2032
|
if idx > len(iter_cache):
|
|
1590
|
-
raise
|
|
2033
|
+
raise CrossHairInternal()
|
|
1591
2034
|
if idx == len(iter_cache):
|
|
1592
2035
|
iter_cache.append(k)
|
|
1593
2036
|
else:
|
|
@@ -1613,7 +2056,7 @@ class SymbolicSet(SymbolicDictOrSet, SetBase, collections.abc.Set):
|
|
|
1613
2056
|
# In this conditional, we reconcile the parallel symbolic variables for length
|
|
1614
2057
|
# and contents:
|
|
1615
2058
|
if space.choose_possible(arr_var != self.empty, probability_true=0.0):
|
|
1616
|
-
raise IgnoreAttempt("
|
|
2059
|
+
raise IgnoreAttempt("SymbolicFrozenSet in inconsistent state")
|
|
1617
2060
|
|
|
1618
2061
|
def _set_op(self, attr, other):
|
|
1619
2062
|
# We need to check the type of other here, because builtin sets
|
|
@@ -1657,40 +2100,15 @@ class SymbolicSet(SymbolicDictOrSet, SetBase, collections.abc.Set):
|
|
|
1657
2100
|
return self._set_op("__sub__", other)
|
|
1658
2101
|
|
|
1659
2102
|
|
|
1660
|
-
|
|
1661
|
-
def __repr__(self):
|
|
1662
|
-
return deep_realize(self).__repr__()
|
|
1663
|
-
|
|
1664
|
-
def __hash__(self):
|
|
1665
|
-
return deep_realize(self).__hash__()
|
|
1666
|
-
|
|
1667
|
-
@classmethod
|
|
1668
|
-
def _from_iterable(cls, it):
|
|
1669
|
-
# overrides collections.abc.Set's version
|
|
1670
|
-
return frozenset(it)
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
def flip_slice_vs_symbolic_len(
|
|
1674
|
-
space: StateSpace,
|
|
2103
|
+
def flip_slice_vs_bounded_len(
|
|
1675
2104
|
i: Union[int, slice],
|
|
1676
|
-
|
|
1677
|
-
) -> Union[
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
def normalize_symbolic_index(idx) -> z3.ExprRef:
|
|
1682
|
-
if type(idx) is int:
|
|
1683
|
-
return z3IntVal(idx) if idx >= 0 else (smt_len + z3IntVal(idx))
|
|
1684
|
-
else:
|
|
1685
|
-
smt_idx = SymbolicInt._coerce_to_smt_sort(idx)
|
|
1686
|
-
if space.smt_fork(smt_idx >= 0): # type: ignore
|
|
1687
|
-
return smt_idx
|
|
1688
|
-
else:
|
|
1689
|
-
return smt_len + smt_idx
|
|
2105
|
+
bounded_len: SymbolicBoundedInt,
|
|
2106
|
+
) -> Union[int, Tuple[int, int]]:
|
|
2107
|
+
def normalize_symbolic_index(idx: int) -> int:
|
|
2108
|
+
return idx if idx >= 0 else (bounded_len + idx)
|
|
1690
2109
|
|
|
1691
2110
|
if isinstance(i, Integral):
|
|
1692
|
-
|
|
1693
|
-
if space.smt_fork(z3.Or(smt_i >= smt_len, smt_i < -smt_len)):
|
|
2111
|
+
if any([i >= bounded_len, i < -bounded_len]):
|
|
1694
2112
|
raise IndexError
|
|
1695
2113
|
return normalize_symbolic_index(i)
|
|
1696
2114
|
elif isinstance(i, slice):
|
|
@@ -1700,17 +2118,12 @@ def flip_slice_vs_symbolic_len(
|
|
|
1700
2118
|
raise TypeError(
|
|
1701
2119
|
"slice indices must be integers or None or have an __index__ method"
|
|
1702
2120
|
)
|
|
1703
|
-
if step is not None:
|
|
1704
|
-
with ResumedTracing(): # Resume tracing for symbolic equality comparison:
|
|
1705
|
-
if step != 1:
|
|
1706
|
-
# TODO: do more with slices and steps
|
|
1707
|
-
raise CrosshairUnsupported("slice steps not handled")
|
|
1708
2121
|
if i.start is None:
|
|
1709
|
-
start =
|
|
2122
|
+
start = 0
|
|
1710
2123
|
else:
|
|
1711
2124
|
start = normalize_symbolic_index(start)
|
|
1712
2125
|
if i.stop is None:
|
|
1713
|
-
stop =
|
|
2126
|
+
stop = bounded_len
|
|
1714
2127
|
else:
|
|
1715
2128
|
stop = normalize_symbolic_index(stop)
|
|
1716
2129
|
return (start, stop)
|
|
@@ -1718,32 +2131,30 @@ def flip_slice_vs_symbolic_len(
|
|
|
1718
2131
|
raise TypeError("indices must be integers or slices, not " + str(type(i)))
|
|
1719
2132
|
|
|
1720
2133
|
|
|
1721
|
-
def
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
stop = smt_len
|
|
2134
|
+
def clip_range_to_bounded_len(
|
|
2135
|
+
start: int,
|
|
2136
|
+
stop: int,
|
|
2137
|
+
bounded_len: SymbolicBoundedInt,
|
|
2138
|
+
) -> Tuple[int, int]:
|
|
2139
|
+
if start < 0:
|
|
2140
|
+
start = 0
|
|
2141
|
+
elif bounded_len < start:
|
|
2142
|
+
start = bounded_len # type: ignore
|
|
2143
|
+
if stop < 0:
|
|
2144
|
+
stop = 0
|
|
2145
|
+
elif bounded_len < stop:
|
|
2146
|
+
stop = bounded_len # type: ignore
|
|
1735
2147
|
return (start, stop)
|
|
1736
2148
|
|
|
1737
2149
|
|
|
1738
|
-
def
|
|
1739
|
-
space: StateSpace,
|
|
2150
|
+
def process_slice_vs_bounded_len(
|
|
1740
2151
|
i: Union[int, slice],
|
|
1741
|
-
|
|
1742
|
-
) -> Union[
|
|
1743
|
-
ret =
|
|
2152
|
+
bounded_len: SymbolicBoundedInt,
|
|
2153
|
+
) -> Union[int, Tuple[int, int]]:
|
|
2154
|
+
ret = flip_slice_vs_bounded_len(i, bounded_len)
|
|
1744
2155
|
if isinstance(ret, tuple):
|
|
1745
2156
|
(start, stop) = ret
|
|
1746
|
-
return
|
|
2157
|
+
return clip_range_to_bounded_len(start, stop, bounded_len)
|
|
1747
2158
|
return ret
|
|
1748
2159
|
|
|
1749
2160
|
|
|
@@ -1788,17 +2199,17 @@ class SymbolicArrayBasedUniformTuple(SymbolicSequence):
|
|
|
1788
2199
|
assert len(smtvar) == 2
|
|
1789
2200
|
|
|
1790
2201
|
self.val_pytype = normalize_pytype(type_arg_of(typ, 0))
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
2202
|
+
space = context_statespace()
|
|
2203
|
+
val_ch_type = space.extra(ModelingDirector).get(self.val_pytype)
|
|
2204
|
+
if val_ch_type:
|
|
2205
|
+
self.ch_item_type: Optional[Type[AtomicSymbolicValue]] = val_ch_type
|
|
1794
2206
|
self.item_smt_sort = self.ch_item_type._ch_smt_sort()
|
|
1795
2207
|
else:
|
|
1796
2208
|
self.ch_item_type = None
|
|
1797
2209
|
self.item_smt_sort = HeapRef
|
|
1798
2210
|
|
|
1799
2211
|
SymbolicValue.__init__(self, smtvar, typ)
|
|
1800
|
-
|
|
1801
|
-
context_statespace().add(len_var >= 0)
|
|
2212
|
+
self._len_int = SymbolicBoundedInt(self._len(), int, minimum=0)
|
|
1802
2213
|
|
|
1803
2214
|
def __init_var__(self, typ, varname):
|
|
1804
2215
|
assert typ == self.python_type
|
|
@@ -1816,12 +2227,10 @@ class SymbolicArrayBasedUniformTuple(SymbolicSequence):
|
|
|
1816
2227
|
return self.var[1]
|
|
1817
2228
|
|
|
1818
2229
|
def __len__(self):
|
|
1819
|
-
|
|
1820
|
-
return SymbolicInt(self._len())
|
|
2230
|
+
return self._len_int
|
|
1821
2231
|
|
|
1822
2232
|
def __bool__(self) -> bool:
|
|
1823
|
-
|
|
1824
|
-
return SymbolicBool(self._len() != 0).__bool__()
|
|
2233
|
+
return self._len_int > 0
|
|
1825
2234
|
|
|
1826
2235
|
def __eq__(self, other):
|
|
1827
2236
|
with NoTracing():
|
|
@@ -1851,9 +2260,10 @@ class SymbolicArrayBasedUniformTuple(SymbolicSequence):
|
|
|
1851
2260
|
def __iter__(self):
|
|
1852
2261
|
with NoTracing():
|
|
1853
2262
|
space = context_statespace()
|
|
1854
|
-
arr_var,
|
|
2263
|
+
arr_var, _ = self.var
|
|
2264
|
+
len_int = self._len_int
|
|
1855
2265
|
idx = 0
|
|
1856
|
-
while
|
|
2266
|
+
while idx < len_int:
|
|
1857
2267
|
val = smt_to_ch_value(
|
|
1858
2268
|
space, self.snapshot, z3.Select(arr_var, idx), self.val_pytype
|
|
1859
2269
|
)
|
|
@@ -1904,16 +2314,14 @@ class SymbolicArrayBasedUniformTuple(SymbolicSequence):
|
|
|
1904
2314
|
and i.step is None
|
|
1905
2315
|
):
|
|
1906
2316
|
return self
|
|
1907
|
-
|
|
2317
|
+
with ResumedTracing():
|
|
2318
|
+
idx_or_pair = process_slice_vs_bounded_len(i, self._len_int)
|
|
1908
2319
|
if isinstance(idx_or_pair, tuple):
|
|
1909
2320
|
(start, stop) = idx_or_pair
|
|
1910
|
-
(myarr, mylen) = self.var
|
|
1911
|
-
start = SymbolicInt(start)
|
|
1912
|
-
stop = SymbolicInt(smt_min(mylen, smt_coerce(stop)))
|
|
1913
2321
|
with ResumedTracing():
|
|
1914
2322
|
return SliceView.slice(self, start, stop)
|
|
1915
2323
|
else:
|
|
1916
|
-
smt_result = z3.Select(self._arr(), idx_or_pair)
|
|
2324
|
+
smt_result = z3.Select(self._arr(), smt_coerce(idx_or_pair))
|
|
1917
2325
|
return smt_to_ch_value(
|
|
1918
2326
|
space, self.snapshot, smt_result, self.val_pytype
|
|
1919
2327
|
)
|
|
@@ -2007,7 +2415,7 @@ class SymbolicRange:
|
|
|
2007
2415
|
return False
|
|
2008
2416
|
if len(self) != len(other):
|
|
2009
2417
|
return False
|
|
2010
|
-
for
|
|
2418
|
+
for v1, v2 in zip(self, other):
|
|
2011
2419
|
if v1 != v2:
|
|
2012
2420
|
return False
|
|
2013
2421
|
return True
|
|
@@ -2072,6 +2480,11 @@ class SymbolicList(
|
|
|
2072
2480
|
def _spawn(self, items: Sequence) -> "ShellMutableSequence":
|
|
2073
2481
|
return SymbolicList(items)
|
|
2074
2482
|
|
|
2483
|
+
def __eq__(self, other):
|
|
2484
|
+
if not isinstance(other, list):
|
|
2485
|
+
return False
|
|
2486
|
+
return ShellMutableSequence.__eq__(self, other)
|
|
2487
|
+
|
|
2075
2488
|
def __lt__(self, other):
|
|
2076
2489
|
if not isinstance(other, (list, SymbolicList)):
|
|
2077
2490
|
raise TypeError
|
|
@@ -2097,6 +2510,10 @@ class SymbolicType(AtomicSymbolicValue, SymbolicValue, Untracable):
|
|
|
2097
2510
|
# no paramaterized types allowed, e.g. SymbolicType("t", Type[List[int]])
|
|
2098
2511
|
assert not hasattr(captype, "__args__")
|
|
2099
2512
|
self.pytype_cap = origin_of(captype)
|
|
2513
|
+
if isinstance(self.pytype_cap, CrossHairValue):
|
|
2514
|
+
raise CrossHairInternal(
|
|
2515
|
+
"Cannot create symbolic type capped at a symbolic type"
|
|
2516
|
+
)
|
|
2100
2517
|
assert isinstance(self.pytype_cap, (type, ABCMeta))
|
|
2101
2518
|
type_repo = space.extra(SymbolicTypeRepository)
|
|
2102
2519
|
smt_cap = type_repo.get_type(self.pytype_cap)
|
|
@@ -2115,13 +2532,13 @@ class SymbolicType(AtomicSymbolicValue, SymbolicValue, Untracable):
|
|
|
2115
2532
|
return type
|
|
2116
2533
|
|
|
2117
2534
|
@classmethod
|
|
2118
|
-
def _smt_promote_literal(cls, literal) -> Optional[z3.
|
|
2535
|
+
def _smt_promote_literal(cls, literal) -> Optional[z3.ExprRef]:
|
|
2119
2536
|
if isinstance(literal, type):
|
|
2120
2537
|
return context_statespace().extra(SymbolicTypeRepository).get_type(literal)
|
|
2121
2538
|
return None
|
|
2122
2539
|
|
|
2540
|
+
@assert_tracing(False)
|
|
2123
2541
|
def _is_superclass_of_(self, other):
|
|
2124
|
-
assert not is_tracing()
|
|
2125
2542
|
if self is SymbolicType:
|
|
2126
2543
|
return False
|
|
2127
2544
|
if type(other) is SymbolicType:
|
|
@@ -2224,23 +2641,24 @@ class SymbolicType(AtomicSymbolicValue, SymbolicValue, Untracable):
|
|
|
2224
2641
|
return hash(self._realized())
|
|
2225
2642
|
|
|
2226
2643
|
|
|
2644
|
+
@assert_tracing(True)
|
|
2227
2645
|
def symbolic_obj_binop(symbolic_obj: "SymbolicObject", other, op):
|
|
2228
2646
|
other_type = type(other)
|
|
2229
2647
|
with NoTracing():
|
|
2230
2648
|
mytype = symbolic_obj._typ
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2649
|
+
if isinstance(mytype, SymbolicType):
|
|
2650
|
+
# This just encourages a useful type realization; we discard the result:
|
|
2651
|
+
other_smt_type = SymbolicType._coerce_to_smt_sort(other_type)
|
|
2652
|
+
if other_smt_type is not None:
|
|
2653
|
+
space = context_statespace()
|
|
2654
|
+
space.smt_fork(z3Eq(mytype.var, other_smt_type), probability_true=0.9)
|
|
2655
|
+
|
|
2656
|
+
# The following call then lowers the type cap.
|
|
2657
|
+
# TODO: This does more work than is really needed. But it might be good for
|
|
2658
|
+
# subclass realizations. We want the equality check above mostly because
|
|
2659
|
+
# `object`` realizes to int|str and we don't want to spend lots of time
|
|
2660
|
+
# considering (usually enum-based) int and str subclasses.
|
|
2661
|
+
mytype._is_subclass_of_(other_type)
|
|
2244
2662
|
obj_with_known_type = symbolic_obj._wrapped()
|
|
2245
2663
|
return op(obj_with_known_type, other)
|
|
2246
2664
|
|
|
@@ -2254,31 +2672,30 @@ class SymbolicObject(ObjectProxy, CrossHairValue, Untracable):
|
|
|
2254
2672
|
members can be.
|
|
2255
2673
|
"""
|
|
2256
2674
|
|
|
2675
|
+
@assert_tracing(False)
|
|
2257
2676
|
def __init__(self, smtvar: str, typ: Type):
|
|
2677
|
+
if not isinstance(typ, type):
|
|
2678
|
+
raise CrossHairInternal(f"Creating SymbolicObject with non-type {typ}")
|
|
2679
|
+
if isinstance(typ, CrossHairValue):
|
|
2680
|
+
raise CrossHairInternal(f"Creating SymbolicObject with symbolic type {typ}")
|
|
2258
2681
|
object.__setattr__(self, "_typ", SymbolicType(smtvar + "_type", Type[typ]))
|
|
2259
2682
|
object.__setattr__(self, "_space", context_statespace())
|
|
2260
2683
|
object.__setattr__(self, "_varname", smtvar)
|
|
2261
2684
|
|
|
2685
|
+
@assert_tracing(False)
|
|
2262
2686
|
def _realize(self):
|
|
2263
2687
|
object.__getattribute__(self, "_space")
|
|
2264
2688
|
varname = object.__getattribute__(self, "_varname")
|
|
2265
2689
|
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2690
|
+
pytype = realize(object.__getattribute__(self, "_typ"))
|
|
2691
|
+
debug(
|
|
2692
|
+
"materializing the type of symbolic", varname, "to be", pytype, ch_stack()
|
|
2693
|
+
)
|
|
2694
|
+
object.__setattr__(self, "_typ", pytype)
|
|
2269
2695
|
if pytype is object:
|
|
2270
2696
|
return object()
|
|
2271
2697
|
return proxy_for_type(pytype, varname, allow_subtypes=False)
|
|
2272
2698
|
|
|
2273
|
-
def _wrapped(self):
|
|
2274
|
-
with NoTracing():
|
|
2275
|
-
try:
|
|
2276
|
-
inner = object.__getattribute__(self, "_inner")
|
|
2277
|
-
except AttributeError:
|
|
2278
|
-
inner = self._realize()
|
|
2279
|
-
object.__setattr__(self, "_inner", inner)
|
|
2280
|
-
return inner
|
|
2281
|
-
|
|
2282
2699
|
def __ch_realize__(self):
|
|
2283
2700
|
return realize(self._wrapped())
|
|
2284
2701
|
|
|
@@ -2290,6 +2707,8 @@ class SymbolicObject(ObjectProxy, CrossHairValue, Untracable):
|
|
|
2290
2707
|
# That's usually bad for LazyObjects, which want to defer their
|
|
2291
2708
|
# realization, so we simply don't do mutation checking for these
|
|
2292
2709
|
# kinds of values right now.
|
|
2710
|
+
# TODO: we should do something else here; realizing the type doesn't
|
|
2711
|
+
# seem THAT terrible?
|
|
2293
2712
|
result = self
|
|
2294
2713
|
else:
|
|
2295
2714
|
result = copy.deepcopy(inner)
|
|
@@ -2334,10 +2753,25 @@ class SymbolicCallable:
|
|
|
2334
2753
|
__annotations__: dict = {}
|
|
2335
2754
|
|
|
2336
2755
|
def __init__(self, values: list):
|
|
2756
|
+
"""
|
|
2757
|
+
A function that will ignore its arguments and produce return values
|
|
2758
|
+
from the list given.
|
|
2759
|
+
If the given list is exhausted, the function will just repeatedly
|
|
2760
|
+
return the final value in the list.
|
|
2761
|
+
|
|
2762
|
+
If `values` is concrete, it must be non-mepty.
|
|
2763
|
+
If `values` is a symbolic list, it will be forced to be non-empty
|
|
2764
|
+
(the caller must enure that's possible).
|
|
2765
|
+
"""
|
|
2337
2766
|
assert not is_tracing()
|
|
2338
2767
|
with ResumedTracing():
|
|
2339
|
-
|
|
2340
|
-
|
|
2768
|
+
has_values = len(values) > 0
|
|
2769
|
+
if isinstance(values, CrossHairValue):
|
|
2770
|
+
space = context_statespace()
|
|
2771
|
+
assert space.is_possible(has_values)
|
|
2772
|
+
space.add(has_values)
|
|
2773
|
+
else:
|
|
2774
|
+
assert has_values
|
|
2341
2775
|
self.values = values
|
|
2342
2776
|
self.idx = 0
|
|
2343
2777
|
|
|
@@ -2361,6 +2795,7 @@ class SymbolicCallable:
|
|
|
2361
2795
|
if idx >= len(values):
|
|
2362
2796
|
return values[-1]
|
|
2363
2797
|
else:
|
|
2798
|
+
self.idx += 1
|
|
2364
2799
|
return values[idx]
|
|
2365
2800
|
|
|
2366
2801
|
def __bool__(self):
|
|
@@ -2383,33 +2818,49 @@ class SymbolicUniformTuple(
|
|
|
2383
2818
|
def __hash__(self):
|
|
2384
2819
|
return tuple(self).__hash__()
|
|
2385
2820
|
|
|
2386
|
-
|
|
2387
|
-
|
|
2821
|
+
def __eq__(self, other):
|
|
2822
|
+
if not isinstance(other, tuple):
|
|
2823
|
+
return False
|
|
2824
|
+
return SymbolicArrayBasedUniformTuple.__eq__(self, other)
|
|
2388
2825
|
|
|
2389
2826
|
|
|
2390
2827
|
class SymbolicBoundedIntTuple(collections.abc.Sequence):
|
|
2391
2828
|
def __init__(self, ranges: List[Tuple[int, int]], varname: str):
|
|
2392
2829
|
assert not is_tracing()
|
|
2830
|
+
assert ranges
|
|
2393
2831
|
self._ranges = ranges
|
|
2394
|
-
space = context_statespace()
|
|
2395
|
-
smtlen = z3.Int(varname + "len" + space.uniq())
|
|
2396
|
-
space.add(smtlen >= 0)
|
|
2397
2832
|
self._varname = varname
|
|
2398
|
-
self._len =
|
|
2833
|
+
self._len = SymbolicBoundedInt(varname + "len", int, 0, None)
|
|
2399
2834
|
self._created_vars: List[SymbolicInt] = []
|
|
2400
2835
|
|
|
2836
|
+
def __ch_deep_realize__(self, memo):
|
|
2837
|
+
# Right now, SymbolicBoundedIntTuple falls short of being a CrossHairValue,
|
|
2838
|
+
# but it happens to be handy to have it realize like one would.
|
|
2839
|
+
concrete_size = realize(self._len)
|
|
2840
|
+
self._create_up_to(concrete_size)
|
|
2841
|
+
return tuple(realize(v) for v in self._created_vars[:concrete_size])
|
|
2842
|
+
|
|
2401
2843
|
def _create_up_to(self, size: int) -> None:
|
|
2402
2844
|
space = context_statespace()
|
|
2403
2845
|
created_vars = self._created_vars
|
|
2846
|
+
ranges = self._ranges
|
|
2404
2847
|
for idx in range(len(created_vars), size):
|
|
2405
2848
|
assert idx == len(created_vars)
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2849
|
+
varname = self._varname + "@" + str(idx)
|
|
2850
|
+
if len(ranges) <= 1:
|
|
2851
|
+
created_vars.append(SymbolicBoundedInt(varname, int, *ranges[0]))
|
|
2852
|
+
else:
|
|
2853
|
+
smtval = z3.Int(varname)
|
|
2854
|
+
constraints = [
|
|
2855
|
+
z3And(minval <= smtval, smtval <= maxval)
|
|
2856
|
+
for minval, maxval in ranges
|
|
2857
|
+
]
|
|
2858
|
+
space.add(z3Or(*constraints))
|
|
2859
|
+
global_min = min(start for start, _ in ranges)
|
|
2860
|
+
global_max = max(end for _, end in ranges)
|
|
2861
|
+
created_vars.append(
|
|
2862
|
+
SymbolicBoundedInt(smtval, int, global_min, global_max)
|
|
2863
|
+
)
|
|
2413
2864
|
if idx % 1_000 == 999:
|
|
2414
2865
|
space.check_timeout()
|
|
2415
2866
|
|
|
@@ -2417,8 +2868,7 @@ class SymbolicBoundedIntTuple(collections.abc.Sequence):
|
|
|
2417
2868
|
return self._len
|
|
2418
2869
|
|
|
2419
2870
|
def __bool__(self) -> bool:
|
|
2420
|
-
|
|
2421
|
-
return SymbolicBool(self._len.var == 0).__bool__()
|
|
2871
|
+
return self._len.var > 0
|
|
2422
2872
|
|
|
2423
2873
|
def __eq__(self, other):
|
|
2424
2874
|
if self is other:
|
|
@@ -2431,7 +2881,7 @@ class SymbolicBoundedIntTuple(collections.abc.Sequence):
|
|
|
2431
2881
|
with NoTracing():
|
|
2432
2882
|
self._create_up_to(realize(otherlen))
|
|
2433
2883
|
constraints = []
|
|
2434
|
-
for
|
|
2884
|
+
for int1, int2 in zip(self._created_vars, tracing_iter(other)):
|
|
2435
2885
|
smtint2 = force_to_smt_sort(int2, SymbolicInt)
|
|
2436
2886
|
constraints.append(int1.var == smtint2)
|
|
2437
2887
|
return SymbolicBool(z3.And(*constraints))
|
|
@@ -2441,17 +2891,18 @@ class SymbolicBoundedIntTuple(collections.abc.Sequence):
|
|
|
2441
2891
|
|
|
2442
2892
|
def __iter__(self):
|
|
2443
2893
|
with NoTracing():
|
|
2444
|
-
|
|
2894
|
+
my_len = self._len
|
|
2445
2895
|
created_vars = self._created_vars
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2896
|
+
idx = 0
|
|
2897
|
+
while True:
|
|
2898
|
+
needed_size = idx + 1
|
|
2899
|
+
with ResumedTracing():
|
|
2900
|
+
if not (idx < my_len):
|
|
2901
|
+
return
|
|
2902
|
+
self._create_up_to(needed_size)
|
|
2903
|
+
with ResumedTracing():
|
|
2904
|
+
yield created_vars[idx]
|
|
2450
2905
|
idx += 1
|
|
2451
|
-
if not space.smt_fork(idx < my_smt_len):
|
|
2452
|
-
return
|
|
2453
|
-
self._create_up_to(idx + 1)
|
|
2454
|
-
yield created_vars[idx]
|
|
2455
2906
|
|
|
2456
2907
|
def __add__(self, other: object):
|
|
2457
2908
|
if isinstance(other, collections.abc.Sequence):
|
|
@@ -2558,7 +3009,7 @@ class AnySymbolicStr(AbcString):
|
|
|
2558
3009
|
raise TypeError
|
|
2559
3010
|
if self == other:
|
|
2560
3011
|
return True if op in (ops.le, ops.ge) else False
|
|
2561
|
-
for
|
|
3012
|
+
for mych, otherch in zip_longest(iter(self), iter(other)):
|
|
2562
3013
|
if mych == otherch:
|
|
2563
3014
|
continue
|
|
2564
3015
|
if mych is None:
|
|
@@ -2588,7 +3039,7 @@ class AnySymbolicStr(AbcString):
|
|
|
2588
3039
|
def capitalize(self):
|
|
2589
3040
|
if self.__len__() == 0:
|
|
2590
3041
|
return ""
|
|
2591
|
-
if
|
|
3042
|
+
if version_info >= (3, 8):
|
|
2592
3043
|
firstchar = self[0].title()
|
|
2593
3044
|
else:
|
|
2594
3045
|
firstchar = self[0].upper()
|
|
@@ -2820,19 +3271,19 @@ class AnySymbolicStr(AbcString):
|
|
|
2820
3271
|
|
|
2821
3272
|
else:
|
|
2822
3273
|
raise TypeError
|
|
2823
|
-
for
|
|
3274
|
+
for idx, ch in enumerate(self):
|
|
2824
3275
|
if not filter(ch):
|
|
2825
3276
|
return self[idx:]
|
|
2826
3277
|
return ""
|
|
2827
3278
|
|
|
2828
3279
|
def splitlines(self, keepends=False):
|
|
2829
|
-
if
|
|
3280
|
+
if version_info < (3, 12):
|
|
2830
3281
|
if not isinstance(keepends, int):
|
|
2831
3282
|
raise TypeError
|
|
2832
3283
|
mylen = self.__len__()
|
|
2833
3284
|
if mylen == 0:
|
|
2834
3285
|
return []
|
|
2835
|
-
for
|
|
3286
|
+
for idx, ch in enumerate(self):
|
|
2836
3287
|
codepoint = ord(ch)
|
|
2837
3288
|
with NoTracing():
|
|
2838
3289
|
space = context_statespace()
|
|
@@ -3070,6 +3521,26 @@ class AnySymbolicStr(AbcString):
|
|
|
3070
3521
|
return "0" * fill_length + self
|
|
3071
3522
|
|
|
3072
3523
|
|
|
3524
|
+
def _unfindable_range(start: Optional[int], end: Optional[int], mylen: int) -> bool:
|
|
3525
|
+
"""
|
|
3526
|
+
Emulates some preliminary checks that CPython makes before searching
|
|
3527
|
+
for substrings within some bounds. (in e.g. str.find, str.startswith, etc)
|
|
3528
|
+
"""
|
|
3529
|
+
if start is None or start == 0 or start <= -mylen:
|
|
3530
|
+
return False
|
|
3531
|
+
|
|
3532
|
+
# At this point, we know that `start` is defined and points to an index after 0
|
|
3533
|
+
if end is None or end >= mylen:
|
|
3534
|
+
return start > mylen
|
|
3535
|
+
|
|
3536
|
+
# At this point, we know that `end` is defined and points to an index before the end of the string
|
|
3537
|
+
if start < 0:
|
|
3538
|
+
start += mylen
|
|
3539
|
+
if end < 0:
|
|
3540
|
+
end += mylen
|
|
3541
|
+
return end < start
|
|
3542
|
+
|
|
3543
|
+
|
|
3073
3544
|
class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
3074
3545
|
"""
|
|
3075
3546
|
A symbolic string that lazily generates SymbolicInt-based characters as needed.
|
|
@@ -3091,25 +3562,24 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
|
3091
3562
|
SymbolicBoundedIntTuple,
|
|
3092
3563
|
SliceView,
|
|
3093
3564
|
SequenceConcatenation,
|
|
3094
|
-
list,
|
|
3095
|
-
|
|
3565
|
+
list, # TODO: are we sharing mutable state here?
|
|
3566
|
+
tuple,
|
|
3096
3567
|
),
|
|
3097
3568
|
):
|
|
3098
3569
|
self._codepoints = smtvar
|
|
3570
|
+
elif isinstance(smtvar, SymbolicList):
|
|
3571
|
+
self._codepoints = smtvar.inner # use the (immutable) contents
|
|
3099
3572
|
else:
|
|
3100
|
-
raise
|
|
3573
|
+
raise CrossHairInternal(
|
|
3101
3574
|
f"Unexpected LazyIntSymbolicStr initializer of type {type(smtvar)}"
|
|
3102
3575
|
)
|
|
3103
3576
|
|
|
3104
3577
|
def __ch_realize__(self) -> object:
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
return "".join(chr(realize(x)) for x in codepoints)
|
|
3578
|
+
codepoints = deep_realize(self._codepoints)
|
|
3579
|
+
return "".join(map(chr, codepoints))
|
|
3108
3580
|
|
|
3109
|
-
# This is normally an AtomicSymbolicValue method, but sometimes it's used in a
|
|
3110
|
-
# duck-typing way.
|
|
3111
3581
|
@classmethod
|
|
3112
|
-
def
|
|
3582
|
+
def _ch_create_from_literal(cls, val: object) -> Optional[CrossHairValue]:
|
|
3113
3583
|
if isinstance(val, str):
|
|
3114
3584
|
return LazyIntSymbolicStr(list(map(ord, val)))
|
|
3115
3585
|
return None
|
|
@@ -3136,10 +3606,6 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
|
3136
3606
|
otherpoints = [ord(ch) for ch in other]
|
|
3137
3607
|
with ResumedTracing():
|
|
3138
3608
|
return mypoints.__eq__(otherpoints)
|
|
3139
|
-
elif isinstance(other, SeqBasedSymbolicStr):
|
|
3140
|
-
with ResumedTracing():
|
|
3141
|
-
otherpoints = [ord(ch) for ch in other]
|
|
3142
|
-
return mypoints.__eq__(otherpoints)
|
|
3143
3609
|
else:
|
|
3144
3610
|
return NotImplemented
|
|
3145
3611
|
|
|
@@ -3147,6 +3613,10 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
|
3147
3613
|
with NoTracing():
|
|
3148
3614
|
if not isinstance(i, (Integral, slice)):
|
|
3149
3615
|
raise TypeError(type(i))
|
|
3616
|
+
# This could/should? be symbolic by naming all the possibilities.
|
|
3617
|
+
# Note the slice case still must realize the return length.
|
|
3618
|
+
# Especially because we no longer explore realization trees except
|
|
3619
|
+
# as a last resort.
|
|
3150
3620
|
i = deep_realize(i)
|
|
3151
3621
|
with ResumedTracing():
|
|
3152
3622
|
newcontents = self._codepoints[i]
|
|
@@ -3227,11 +3697,15 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
|
3227
3697
|
return any(self.endswith(s, start, end) for s in substr)
|
|
3228
3698
|
if not isinstance(substr, str):
|
|
3229
3699
|
raise TypeError
|
|
3700
|
+
substrlen = len(substr)
|
|
3230
3701
|
if start is None and end is None:
|
|
3231
3702
|
matchable = self
|
|
3232
3703
|
else:
|
|
3233
3704
|
matchable = self[start:end]
|
|
3234
|
-
|
|
3705
|
+
if substrlen == 0:
|
|
3706
|
+
return not _unfindable_range(start, end, len(self))
|
|
3707
|
+
else:
|
|
3708
|
+
return matchable[-substrlen:] == substr
|
|
3235
3709
|
|
|
3236
3710
|
def startswith(self, substr, start=None, end=None):
|
|
3237
3711
|
if isinstance(substr, tuple):
|
|
@@ -3241,6 +3715,10 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
|
3241
3715
|
if start is None and end is None:
|
|
3242
3716
|
matchable = self
|
|
3243
3717
|
else:
|
|
3718
|
+
# Wacky special case: the empty string is findable off the left
|
|
3719
|
+
# side but not the right!
|
|
3720
|
+
if _unfindable_range(start, end, len(self)):
|
|
3721
|
+
return False
|
|
3244
3722
|
matchable = self[start:end]
|
|
3245
3723
|
return matchable[: len(substr)] == substr
|
|
3246
3724
|
|
|
@@ -3281,7 +3759,7 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
|
3281
3759
|
end += mylen
|
|
3282
3760
|
matchstr = self[start:end] if start != 0 or end is not mylen else self
|
|
3283
3761
|
if len(substr) == 0:
|
|
3284
|
-
#
|
|
3762
|
+
# An oddity of CPython. We can find the empty string when over-slicing
|
|
3285
3763
|
# off the left side of the string, but not off the right:
|
|
3286
3764
|
# ''.find('', 3, 4) == -1
|
|
3287
3765
|
# ''.find('', -4, -3) == 0
|
|
@@ -3308,257 +3786,6 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
|
3308
3786
|
return self._find(substr, start, end, from_right=True)
|
|
3309
3787
|
|
|
3310
3788
|
|
|
3311
|
-
class SeqBasedSymbolicStr(AtomicSymbolicValue, SymbolicSequence, AnySymbolicStr):
|
|
3312
|
-
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type = str):
|
|
3313
|
-
assert typ == str
|
|
3314
|
-
SymbolicValue.__init__(self, smtvar, typ)
|
|
3315
|
-
self.item_pytype = str
|
|
3316
|
-
if isinstance(smtvar, str):
|
|
3317
|
-
# Constrain fresh strings to valid codepoints
|
|
3318
|
-
space = context_statespace()
|
|
3319
|
-
idxvar = z3.Int("idxvar" + space.uniq())
|
|
3320
|
-
z3seq = self.var
|
|
3321
|
-
space.add(
|
|
3322
|
-
z3.ForAll(
|
|
3323
|
-
[idxvar], z3.And(0 <= z3seq[idxvar], z3seq[idxvar] <= maxunicode)
|
|
3324
|
-
)
|
|
3325
|
-
)
|
|
3326
|
-
|
|
3327
|
-
@classmethod
|
|
3328
|
-
def _ch_smt_sort(cls) -> z3.SortRef:
|
|
3329
|
-
return _SMTSTR_Z3_SORT
|
|
3330
|
-
|
|
3331
|
-
@classmethod
|
|
3332
|
-
def _pytype(cls) -> Type:
|
|
3333
|
-
return str
|
|
3334
|
-
|
|
3335
|
-
@classmethod
|
|
3336
|
-
def _smt_promote_literal(cls, literal) -> Optional[z3.SortRef]:
|
|
3337
|
-
if isinstance(literal, str):
|
|
3338
|
-
if len(literal) <= 1:
|
|
3339
|
-
if len(literal) == 0:
|
|
3340
|
-
return z3.Empty(_SMTSTR_Z3_SORT)
|
|
3341
|
-
return z3.Unit(z3IntVal(ord(literal)))
|
|
3342
|
-
return z3.Concat([z3.Unit(z3IntVal(ord(ch))) for ch in literal])
|
|
3343
|
-
return None
|
|
3344
|
-
|
|
3345
|
-
def __ch_realize__(self) -> object:
|
|
3346
|
-
codepoints = context_statespace().find_model_value(self.var)
|
|
3347
|
-
return "".join(chr(x) for x in codepoints)
|
|
3348
|
-
|
|
3349
|
-
def __copy__(self):
|
|
3350
|
-
return SeqBasedSymbolicStr(self.var)
|
|
3351
|
-
|
|
3352
|
-
def __hash__(self):
|
|
3353
|
-
return hash(self.__str__())
|
|
3354
|
-
|
|
3355
|
-
@staticmethod
|
|
3356
|
-
def _concat_strings(
|
|
3357
|
-
a: Union[str, "SeqBasedSymbolicStr"], b: Union[str, "SeqBasedSymbolicStr"]
|
|
3358
|
-
) -> Union[str, "SeqBasedSymbolicStr"]:
|
|
3359
|
-
assert not is_tracing()
|
|
3360
|
-
# Assumes at least one argument is symbolic and not tracing
|
|
3361
|
-
if isinstance(a, SeqBasedSymbolicStr) and isinstance(b, SeqBasedSymbolicStr):
|
|
3362
|
-
return SeqBasedSymbolicStr(a.var + b.var)
|
|
3363
|
-
elif isinstance(a, str) and isinstance(b, SeqBasedSymbolicStr):
|
|
3364
|
-
return SeqBasedSymbolicStr(
|
|
3365
|
-
SeqBasedSymbolicStr._coerce_to_smt_sort(a) + b.var
|
|
3366
|
-
)
|
|
3367
|
-
else:
|
|
3368
|
-
assert isinstance(a, SeqBasedSymbolicStr)
|
|
3369
|
-
assert isinstance(b, str)
|
|
3370
|
-
return SeqBasedSymbolicStr(
|
|
3371
|
-
a.var + SeqBasedSymbolicStr._coerce_to_smt_sort(b)
|
|
3372
|
-
)
|
|
3373
|
-
|
|
3374
|
-
def __add__(self, other):
|
|
3375
|
-
with NoTracing():
|
|
3376
|
-
if isinstance(other, (SeqBasedSymbolicStr, str)):
|
|
3377
|
-
return SeqBasedSymbolicStr._concat_strings(self, other)
|
|
3378
|
-
if isinstance(other, AnySymbolicStr):
|
|
3379
|
-
return NotImplemented
|
|
3380
|
-
raise TypeError
|
|
3381
|
-
|
|
3382
|
-
def __radd__(self, other):
|
|
3383
|
-
with NoTracing():
|
|
3384
|
-
if isinstance(other, (SeqBasedSymbolicStr, str)):
|
|
3385
|
-
return SeqBasedSymbolicStr._concat_strings(other, self)
|
|
3386
|
-
if isinstance(other, AnySymbolicStr):
|
|
3387
|
-
return NotImplemented
|
|
3388
|
-
raise TypeError
|
|
3389
|
-
|
|
3390
|
-
def __mul__(self, other):
|
|
3391
|
-
if isinstance(other, Integral):
|
|
3392
|
-
if other <= 1:
|
|
3393
|
-
return self if other == 1 else ""
|
|
3394
|
-
# Note that in SymbolicInt, we attempt string multiplication via regex.
|
|
3395
|
-
# Z3 cannot do much with a symbolic regex, so we case-split on
|
|
3396
|
-
# the repetition count.
|
|
3397
|
-
return SeqBasedSymbolicStr(z3.Concat(*[self.var for _ in range(other)]))
|
|
3398
|
-
return NotImplemented
|
|
3399
|
-
|
|
3400
|
-
__rmul__ = __mul__
|
|
3401
|
-
|
|
3402
|
-
def __mod__(self, other):
|
|
3403
|
-
return self.__str__() % realize(other)
|
|
3404
|
-
|
|
3405
|
-
def __contains__(self, other):
|
|
3406
|
-
with NoTracing():
|
|
3407
|
-
forced = force_to_smt_sort(other, SeqBasedSymbolicStr)
|
|
3408
|
-
return SymbolicBool(z3.Contains(self.var, forced))
|
|
3409
|
-
|
|
3410
|
-
def __getitem__(self, i: Union[int, slice]):
|
|
3411
|
-
with NoTracing():
|
|
3412
|
-
idx_or_pair = process_slice_vs_symbolic_len(
|
|
3413
|
-
context_statespace(), i, z3.Length(self.var)
|
|
3414
|
-
)
|
|
3415
|
-
if isinstance(idx_or_pair, tuple):
|
|
3416
|
-
(start, stop) = idx_or_pair
|
|
3417
|
-
smt_result = z3.Extract(self.var, start, stop - start)
|
|
3418
|
-
else:
|
|
3419
|
-
smt_result = z3.Unit(self.var[idx_or_pair])
|
|
3420
|
-
return SeqBasedSymbolicStr(smt_result)
|
|
3421
|
-
|
|
3422
|
-
def endswith(self, substr):
|
|
3423
|
-
with NoTracing():
|
|
3424
|
-
smt_substr = force_to_smt_sort(substr, SeqBasedSymbolicStr)
|
|
3425
|
-
return SymbolicBool(z3.SuffixOf(smt_substr, self.var))
|
|
3426
|
-
|
|
3427
|
-
def find(self, substr, start=None, end=None):
|
|
3428
|
-
if not isinstance(substr, str):
|
|
3429
|
-
raise TypeError
|
|
3430
|
-
with NoTracing():
|
|
3431
|
-
space = context_statespace()
|
|
3432
|
-
smt_my_len = z3.Length(self.var)
|
|
3433
|
-
if start is None and end is None:
|
|
3434
|
-
smt_start = z3IntVal(0)
|
|
3435
|
-
smt_end = smt_my_len
|
|
3436
|
-
smt_str = self.var
|
|
3437
|
-
if len(substr) == 0:
|
|
3438
|
-
return 0
|
|
3439
|
-
else:
|
|
3440
|
-
(smt_start, smt_end) = flip_slice_vs_symbolic_len(
|
|
3441
|
-
space, slice(start, end, None), smt_my_len
|
|
3442
|
-
)
|
|
3443
|
-
if len(substr) == 0:
|
|
3444
|
-
# Add oddity of CPython. We can find the empty string when over-slicing
|
|
3445
|
-
# off the left side of the string, but not off the right:
|
|
3446
|
-
# ''.find('', 3, 4) == -1
|
|
3447
|
-
# ''.find('', -4, -3) == 0
|
|
3448
|
-
if space.smt_fork(smt_start > smt_my_len):
|
|
3449
|
-
return -1
|
|
3450
|
-
elif space.smt_fork(smt_start > 0):
|
|
3451
|
-
return SymbolicInt(smt_start)
|
|
3452
|
-
else:
|
|
3453
|
-
return 0
|
|
3454
|
-
(smt_start, smt_end) = clip_range_to_symbolic_len(
|
|
3455
|
-
space, smt_start, smt_end, smt_my_len
|
|
3456
|
-
)
|
|
3457
|
-
smt_str = z3.SubString(self.var, smt_start, smt_end - smt_start)
|
|
3458
|
-
|
|
3459
|
-
smt_sub = force_to_smt_sort(substr, SeqBasedSymbolicStr)
|
|
3460
|
-
if space.smt_fork(z3.Contains(smt_str, smt_sub)):
|
|
3461
|
-
return SymbolicInt(z3.IndexOf(smt_str, smt_sub, 0) + smt_start)
|
|
3462
|
-
else:
|
|
3463
|
-
return -1
|
|
3464
|
-
|
|
3465
|
-
def partition(self, sep: str):
|
|
3466
|
-
if not isinstance(sep, str):
|
|
3467
|
-
raise TypeError
|
|
3468
|
-
if len(sep) == 0:
|
|
3469
|
-
raise ValueError
|
|
3470
|
-
with NoTracing():
|
|
3471
|
-
space = context_statespace()
|
|
3472
|
-
smt_str = self.var
|
|
3473
|
-
smt_sep = force_to_smt_sort(sep, SeqBasedSymbolicStr)
|
|
3474
|
-
if space.smt_fork(z3.Contains(smt_str, smt_sep)):
|
|
3475
|
-
uniq = space.uniq()
|
|
3476
|
-
# Divide my contents into 4 concatenated parts:
|
|
3477
|
-
prefix = SeqBasedSymbolicStr(f"prefix{uniq}")
|
|
3478
|
-
match1 = SeqBasedSymbolicStr(
|
|
3479
|
-
f"match1{uniq}"
|
|
3480
|
-
) # the first character of the match
|
|
3481
|
-
match_tail = SeqBasedSymbolicStr(f"match_tail{uniq}")
|
|
3482
|
-
suffix = SeqBasedSymbolicStr(f"suffix{uniq}")
|
|
3483
|
-
space.add(z3.Length(match1.var) == 1)
|
|
3484
|
-
space.add(smt_sep == z3.Concat(match1.var, match_tail.var))
|
|
3485
|
-
space.add(smt_str == z3.Concat(prefix.var, smt_sep, suffix.var))
|
|
3486
|
-
space.add(
|
|
3487
|
-
z3.Not(z3.Contains(z3.Concat(match_tail.var, suffix.var), smt_sep))
|
|
3488
|
-
)
|
|
3489
|
-
return (prefix, sep, suffix)
|
|
3490
|
-
else:
|
|
3491
|
-
return (self, "", "")
|
|
3492
|
-
|
|
3493
|
-
def rfind(self, substr, start=None, end=None) -> Union[int, SymbolicInt]:
|
|
3494
|
-
if not isinstance(substr, str):
|
|
3495
|
-
raise TypeError
|
|
3496
|
-
with NoTracing():
|
|
3497
|
-
space = context_statespace()
|
|
3498
|
-
smt_my_len = z3.Length(self.var)
|
|
3499
|
-
if start is None and end is None:
|
|
3500
|
-
smt_start = z3IntVal(0)
|
|
3501
|
-
smt_end = smt_my_len
|
|
3502
|
-
smt_str = self.var
|
|
3503
|
-
if len(substr) == 0:
|
|
3504
|
-
return SymbolicInt(smt_my_len)
|
|
3505
|
-
else:
|
|
3506
|
-
(smt_start, smt_end) = flip_slice_vs_symbolic_len(
|
|
3507
|
-
space, slice(start, end, None), smt_my_len
|
|
3508
|
-
)
|
|
3509
|
-
if len(substr) == 0:
|
|
3510
|
-
# Add oddity of CPython. We can find the empty string when over-slicing
|
|
3511
|
-
# off the left side of the string, but not off the right:
|
|
3512
|
-
# ''.find('', 3, 4) == -1
|
|
3513
|
-
# ''.find('', -4, -3) == 0
|
|
3514
|
-
if space.smt_fork(smt_start > smt_my_len):
|
|
3515
|
-
return -1
|
|
3516
|
-
elif space.smt_fork(smt_end < 0):
|
|
3517
|
-
return 0
|
|
3518
|
-
elif space.smt_fork(smt_end < smt_my_len):
|
|
3519
|
-
return SymbolicInt(smt_end)
|
|
3520
|
-
else:
|
|
3521
|
-
return SymbolicInt(smt_my_len)
|
|
3522
|
-
(smt_start, smt_end) = clip_range_to_symbolic_len(
|
|
3523
|
-
space, smt_start, smt_end, smt_my_len
|
|
3524
|
-
)
|
|
3525
|
-
smt_str = z3.SubString(self.var, smt_start, smt_end - smt_start)
|
|
3526
|
-
smt_sub = force_to_smt_sort(substr, SeqBasedSymbolicStr)
|
|
3527
|
-
if space.smt_fork(z3.Contains(smt_str, smt_sub)):
|
|
3528
|
-
uniq = space.uniq()
|
|
3529
|
-
# Divide my contents into 4 concatenated parts:
|
|
3530
|
-
prefix = SeqBasedSymbolicStr(f"prefix{uniq}")
|
|
3531
|
-
match1 = SeqBasedSymbolicStr(f"match1{uniq}")
|
|
3532
|
-
match_tail = SeqBasedSymbolicStr(f"match_tail{uniq}")
|
|
3533
|
-
suffix = SeqBasedSymbolicStr(f"suffix{uniq}")
|
|
3534
|
-
space.add(z3.Length(match1.var) == 1)
|
|
3535
|
-
space.add(smt_sub == z3.Concat(match1.var, match_tail.var))
|
|
3536
|
-
space.add(smt_str == z3.Concat(prefix.var, smt_sub, suffix.var))
|
|
3537
|
-
space.add(
|
|
3538
|
-
z3.Not(z3.Contains(z3.Concat(match_tail.var, suffix.var), smt_sub))
|
|
3539
|
-
)
|
|
3540
|
-
return SymbolicInt(smt_start + z3.Length(prefix.var))
|
|
3541
|
-
else:
|
|
3542
|
-
return -1
|
|
3543
|
-
|
|
3544
|
-
def rpartition(self, sep: str):
|
|
3545
|
-
result = self.rsplit(sep, maxsplit=1)
|
|
3546
|
-
if len(result) == 1:
|
|
3547
|
-
return ("", "", self)
|
|
3548
|
-
elif len(result) == 2:
|
|
3549
|
-
return (result[0], sep, result[1])
|
|
3550
|
-
|
|
3551
|
-
def startswith(self, substr, start=None, end=None):
|
|
3552
|
-
if isinstance(substr, tuple):
|
|
3553
|
-
return any(self.startswith(s, start, end) for s in substr)
|
|
3554
|
-
smt_substr = force_to_smt_sort(substr, SeqBasedSymbolicStr)
|
|
3555
|
-
if start is not None or end is not None:
|
|
3556
|
-
# TODO: "".startswith("", 1) should be False, not True
|
|
3557
|
-
return self[start:end].startswith(substr)
|
|
3558
|
-
with NoTracing():
|
|
3559
|
-
return SymbolicBool(z3.PrefixOf(smt_substr, self.var))
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
3789
|
def buffer_to_byte_seq(obj: object) -> Optional[Sequence[int]]:
|
|
3563
3790
|
if isinstance(obj, (bytes, bytearray)):
|
|
3564
3791
|
return list(obj)
|
|
@@ -3620,9 +3847,7 @@ def is_ascii_space_ord(char_ord: int):
|
|
|
3620
3847
|
)
|
|
3621
3848
|
|
|
3622
3849
|
|
|
3623
|
-
|
|
3624
|
-
# of collections.abc.ByteString (here and elsewhere)
|
|
3625
|
-
class BytesLike(collections.abc.ByteString, AbcString, CrossHairValue):
|
|
3850
|
+
class BytesLike(Buffer, AbcString, CrossHairValue):
|
|
3626
3851
|
def __eq__(self, other) -> bool:
|
|
3627
3852
|
if not isinstance(other, _ALL_BYTES_TYPES):
|
|
3628
3853
|
return False
|
|
@@ -3630,6 +3855,12 @@ class BytesLike(collections.abc.ByteString, AbcString, CrossHairValue):
|
|
|
3630
3855
|
return False
|
|
3631
3856
|
return list(self) == list(other)
|
|
3632
3857
|
|
|
3858
|
+
if version_info >= (3, 12):
|
|
3859
|
+
|
|
3860
|
+
def __buffer__(self, flags: int):
|
|
3861
|
+
with NoTracing():
|
|
3862
|
+
return memoryview(realize(self))
|
|
3863
|
+
|
|
3633
3864
|
def _cmp_op(self, other, op) -> bool:
|
|
3634
3865
|
# Surprisingly, none of (bytes, memoryview, array) are ordered-comparable with
|
|
3635
3866
|
# the other types.
|
|
@@ -3703,7 +3934,6 @@ class BytesLike(collections.abc.ByteString, AbcString, CrossHairValue):
|
|
|
3703
3934
|
chars.append(sep)
|
|
3704
3935
|
low = make_hex_digit(byt)
|
|
3705
3936
|
high = make_hex_digit(byt // 16)
|
|
3706
|
-
debug("loiw", low, "high", high)
|
|
3707
3937
|
chars.append(chr(high))
|
|
3708
3938
|
chars.append(chr(low))
|
|
3709
3939
|
# TODO: optimize by creating a LazyIntSymbolicStr directly
|
|
@@ -3732,6 +3962,9 @@ class SymbolicBytes(BytesLike):
|
|
|
3732
3962
|
def __ch_pytype__(self):
|
|
3733
3963
|
return bytes
|
|
3734
3964
|
|
|
3965
|
+
def __hash__(self):
|
|
3966
|
+
return deep_realize(self).__hash__()
|
|
3967
|
+
|
|
3735
3968
|
def __repr__(self):
|
|
3736
3969
|
# TODO: implement this preserving symbolics. These are the cases:
|
|
3737
3970
|
# [9]: "\t"
|
|
@@ -3782,7 +4015,10 @@ class SymbolicBytes(BytesLike):
|
|
|
3782
4015
|
accumulated = []
|
|
3783
4016
|
high = None
|
|
3784
4017
|
if not isinstance(hexstr, str):
|
|
3785
|
-
|
|
4018
|
+
if is_bytes_like(hexstr) and version_info >= (3, 14):
|
|
4019
|
+
hexstr = LazyIntSymbolicStr(tuple(hexstr))
|
|
4020
|
+
else:
|
|
4021
|
+
raise TypeError
|
|
3786
4022
|
for idx, ch in enumerate(hexstr):
|
|
3787
4023
|
if not ch.isascii():
|
|
3788
4024
|
raise ValueError(
|
|
@@ -3870,7 +4106,7 @@ class SymbolicByteArray(BytesLike, ShellMutableSequence): # type: ignore
|
|
|
3870
4106
|
|
|
3871
4107
|
|
|
3872
4108
|
class SymbolicMemoryView(BytesLike):
|
|
3873
|
-
format = "B"
|
|
4109
|
+
format = "B" # type: ignore
|
|
3874
4110
|
itemsize = 1
|
|
3875
4111
|
ndim = 1
|
|
3876
4112
|
strides = (1,)
|
|
@@ -3883,11 +4119,12 @@ class SymbolicMemoryView(BytesLike):
|
|
|
3883
4119
|
assert not is_tracing()
|
|
3884
4120
|
if not isinstance(obj, (_ALL_BYTES_TYPES, BytesLike)):
|
|
3885
4121
|
raise TypeError
|
|
3886
|
-
|
|
4122
|
+
with ResumedTracing():
|
|
4123
|
+
objlen = len(obj)
|
|
4124
|
+
self.readonly = isinstance(obj, bytes)
|
|
3887
4125
|
self.obj = obj
|
|
3888
4126
|
self.nbytes = objlen
|
|
3889
4127
|
self.shape = (objlen,)
|
|
3890
|
-
self.readonly = isinstance(obj, bytes)
|
|
3891
4128
|
self._sliced = SliceView(obj, 0, objlen)
|
|
3892
4129
|
|
|
3893
4130
|
def __ch_realize__(self):
|
|
@@ -3896,6 +4133,12 @@ class SymbolicMemoryView(BytesLike):
|
|
|
3896
4133
|
self.obj = obj
|
|
3897
4134
|
return memoryview(realize(obj))[realize(start) : realize(stop)]
|
|
3898
4135
|
|
|
4136
|
+
def __ch_deep_realize__(self, memo):
|
|
4137
|
+
sliced = self._sliced
|
|
4138
|
+
obj, start, stop = self.obj, sliced.start, sliced.stop
|
|
4139
|
+
self.obj = obj
|
|
4140
|
+
return memoryview(deep_realize(obj, memo))[realize(start) : realize(stop)]
|
|
4141
|
+
|
|
3899
4142
|
def __ch_pytype__(self):
|
|
3900
4143
|
return memoryview
|
|
3901
4144
|
|
|
@@ -3972,22 +4215,15 @@ class SymbolicMemoryView(BytesLike):
|
|
|
3972
4215
|
return realize(self).cast(*map(realize, a))
|
|
3973
4216
|
|
|
3974
4217
|
|
|
3975
|
-
_CACHED_TYPE_ENUMS: Dict[FrozenSet[type], z3.SortRef] = {}
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
4218
|
_PYTYPE_TO_WRAPPER_TYPE = {
|
|
3979
4219
|
# These are mappings for AtomicSymbolic values - values that we directly represent
|
|
3980
4220
|
# as single z3 values.
|
|
3981
|
-
bool: (SymbolicBool,),
|
|
3982
|
-
int: (SymbolicInt,),
|
|
3983
|
-
float: (
|
|
3984
|
-
type: (SymbolicType,),
|
|
4221
|
+
bool: ((SymbolicBool, 1.0),),
|
|
4222
|
+
int: ((SymbolicInt, 1.0),),
|
|
4223
|
+
float: ((RealBasedSymbolicFloat, 0.98), (PreciseIeeeSymbolicFloat, 0.02)),
|
|
4224
|
+
type: ((SymbolicType, 1.0),),
|
|
3985
4225
|
}
|
|
3986
4226
|
|
|
3987
|
-
_WRAPPER_TYPE_TO_PYTYPE = dict(
|
|
3988
|
-
(v, k) for (k, vs) in _PYTYPE_TO_WRAPPER_TYPE.items() for v in vs
|
|
3989
|
-
)
|
|
3990
|
-
|
|
3991
4227
|
|
|
3992
4228
|
#
|
|
3993
4229
|
# Symbolic-making helpers
|
|
@@ -4004,11 +4240,12 @@ def make_union_choice(creator: SymbolicFactory, *pytypes):
|
|
|
4004
4240
|
return creator(pytypes[-1])
|
|
4005
4241
|
|
|
4006
4242
|
|
|
4007
|
-
def
|
|
4243
|
+
def make_concrete_or_symbolic(typ: type):
|
|
4008
4244
|
def make(creator: SymbolicFactory, *type_args):
|
|
4245
|
+
nonlocal typ
|
|
4009
4246
|
space = context_statespace()
|
|
4010
4247
|
varname, pytype = creator.varname, creator.pytype
|
|
4011
|
-
ret =
|
|
4248
|
+
ret = typ(creator.varname, pytype)
|
|
4012
4249
|
|
|
4013
4250
|
premature_stats, symbolic_stats = space.stats_lookahead()
|
|
4014
4251
|
bad_iters = (
|
|
@@ -4052,11 +4289,36 @@ def make_dictionary(creator: SymbolicFactory, key_type=Any, value_type=Any):
|
|
|
4052
4289
|
return ShellMutableMap(SymbolicDict(varname, creator.pytype))
|
|
4053
4290
|
|
|
4054
4291
|
|
|
4292
|
+
@assert_tracing(False)
|
|
4293
|
+
def make_float(varname: str, pytype: Type):
|
|
4294
|
+
if os.environ.get("CROSSHAIR_ONLY_FINITE_FLOATS") == "1":
|
|
4295
|
+
warnings.warn(
|
|
4296
|
+
"Support for CROSSHAIR_ONLY_FINITE_FLOATS will be removed in CrossHair v0.0.75",
|
|
4297
|
+
FutureWarning,
|
|
4298
|
+
)
|
|
4299
|
+
return RealBasedSymbolicFloat(varname, pytype)
|
|
4300
|
+
space = context_statespace()
|
|
4301
|
+
chosen_typ = space.extra(ModelingDirector).choose(float)
|
|
4302
|
+
if chosen_typ is RealBasedSymbolicFloat:
|
|
4303
|
+
if space.smt_fork(desc=f"{varname}_isfinite", probability_true=0.8):
|
|
4304
|
+
return RealBasedSymbolicFloat(varname, pytype)
|
|
4305
|
+
if space.smt_fork(desc=f"{varname}_isnan", probability_true=0.5):
|
|
4306
|
+
return nan
|
|
4307
|
+
if space.smt_fork(desc=f"{varname}_neginf", probability_true=0.25):
|
|
4308
|
+
return -inf
|
|
4309
|
+
return inf
|
|
4310
|
+
else:
|
|
4311
|
+
return chosen_typ(varname, pytype)
|
|
4312
|
+
|
|
4313
|
+
|
|
4055
4314
|
def make_tuple(creator: SymbolicFactory, *type_args):
|
|
4056
4315
|
if not type_args:
|
|
4057
4316
|
type_args = (object, ...) # type: ignore
|
|
4058
4317
|
if len(type_args) == 2 and type_args[1] == ...:
|
|
4059
4318
|
return SymbolicUniformTuple(creator.varname, creator.pytype)
|
|
4319
|
+
elif len(type_args) == 1 and type_args[0] == ():
|
|
4320
|
+
# In python, the type for the empty tuple is written like Tuple[()]
|
|
4321
|
+
return ()
|
|
4060
4322
|
else:
|
|
4061
4323
|
return tuple(
|
|
4062
4324
|
proxy_for_type(t, creator.varname + "_at_" + str(idx), allow_subtypes=True)
|
|
@@ -4197,36 +4459,38 @@ def _chr(i: int) -> Union[str, LazyIntSymbolicStr]:
|
|
|
4197
4459
|
return chr(realize(i))
|
|
4198
4460
|
|
|
4199
4461
|
|
|
4200
|
-
def _dict(arg=_MISSING) -> Union[dict, ShellMutableMap]:
|
|
4201
|
-
if optional_context_statespace():
|
|
4202
|
-
if
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4462
|
+
def _dict(arg=_MISSING, **kwargs) -> Union[dict, ShellMutableMap]:
|
|
4463
|
+
if not optional_context_statespace():
|
|
4464
|
+
newdict: Union[dict, ShellMutableMap] = dict() if arg is _MISSING else dict(arg)
|
|
4465
|
+
if isinstance(arg, Mapping):
|
|
4466
|
+
newdict = ShellMutableMap(SimpleDict(list(arg.items())))
|
|
4467
|
+
elif arg is _MISSING:
|
|
4468
|
+
newdict = ShellMutableMap(SimpleDict([]))
|
|
4469
|
+
elif is_iterable(arg):
|
|
4470
|
+
keys: List = []
|
|
4471
|
+
key_compares: List = []
|
|
4472
|
+
all_items: List = []
|
|
4473
|
+
for pair in arg: # NOTE: `arg` can be an iterator; scan only once
|
|
4474
|
+
if len(pair) != 2:
|
|
4475
|
+
raise ValueError
|
|
4476
|
+
(key, val) = pair
|
|
4477
|
+
if not is_hashable(key):
|
|
4478
|
+
raise ValueError
|
|
4479
|
+
all_items.append(pair)
|
|
4480
|
+
key_compares.extend(key == k for k in keys)
|
|
4481
|
+
keys.append(key)
|
|
4482
|
+
if not any(key_compares):
|
|
4483
|
+
simpledict = SimpleDict(all_items)
|
|
4484
|
+
else: # we have one or more key conflicts:
|
|
4485
|
+
simpledict = SimpleDict([])
|
|
4486
|
+
for key, val in reversed(all_items):
|
|
4487
|
+
if key not in simpledict:
|
|
4488
|
+
simpledict[key] = val
|
|
4489
|
+
newdict = ShellMutableMap(simpledict)
|
|
4490
|
+
else:
|
|
4491
|
+
raise TypeError
|
|
4492
|
+
newdict.update(kwargs)
|
|
4493
|
+
return newdict
|
|
4230
4494
|
|
|
4231
4495
|
|
|
4232
4496
|
def _eval(expr: str, _globals=None, _locals=None) -> object:
|
|
@@ -4281,26 +4545,39 @@ def _hash(obj: Hashable) -> int:
|
|
|
4281
4545
|
return invoke_dunder(obj, "__hash__")
|
|
4282
4546
|
|
|
4283
4547
|
|
|
4284
|
-
def _int(val:
|
|
4548
|
+
def _int(val: Any = 0, base=_MISSING):
|
|
4285
4549
|
with NoTracing():
|
|
4286
4550
|
if isinstance(val, SymbolicInt):
|
|
4551
|
+
if base is not _MISSING:
|
|
4552
|
+
raise TypeError("int() can't convert non-string with explicit base")
|
|
4287
4553
|
return val
|
|
4288
|
-
if isinstance(val, AnySymbolicStr)
|
|
4554
|
+
if isinstance(val, AnySymbolicStr):
|
|
4289
4555
|
with ResumedTracing():
|
|
4290
|
-
if
|
|
4291
|
-
|
|
4556
|
+
if base is _MISSING:
|
|
4557
|
+
base = 10
|
|
4558
|
+
elif not hasattr(base, "__index__"):
|
|
4559
|
+
raise TypeError(
|
|
4560
|
+
f"{name_of_type(type(base))} object cannot be interpreted as an integer"
|
|
4561
|
+
)
|
|
4562
|
+
if any([base < 2, base > 10, not val]):
|
|
4563
|
+
# TODO: bses 11-36 are allowed, but require parsing the a-z and A-Z ranges.
|
|
4564
|
+
# TODO: base can be 0, which means to interpret the string as a literal e.g. '0b100'
|
|
4565
|
+
return int(realize(val), base=realize(base))
|
|
4292
4566
|
ret = 0
|
|
4293
4567
|
for ch in val:
|
|
4294
4568
|
ch_num = ord(ch) - _ORD_OF_ZERO
|
|
4295
4569
|
# Use `any()` to collapse symbolc conditions
|
|
4296
|
-
if any((ch_num < 0, ch_num
|
|
4570
|
+
if any((ch_num < 0, ch_num >= base)):
|
|
4297
4571
|
# TODO parse other digits with data from unicodedata.decimal()
|
|
4298
4572
|
return int(realize(val))
|
|
4299
4573
|
else:
|
|
4300
|
-
ret = (ret *
|
|
4574
|
+
ret = (ret * base) + ch_num
|
|
4301
4575
|
return ret
|
|
4302
|
-
|
|
4303
|
-
|
|
4576
|
+
elif isinstance(val, CrossHairValue):
|
|
4577
|
+
val = deep_realize(val)
|
|
4578
|
+
base = deep_realize(base)
|
|
4579
|
+
|
|
4580
|
+
return int(val) if base is _MISSING else int(val, base=base)
|
|
4304
4581
|
|
|
4305
4582
|
|
|
4306
4583
|
_FLOAT_REGEX = re.compile(
|
|
@@ -4347,11 +4624,17 @@ def _float(val=0.0):
|
|
|
4347
4624
|
ret = -ret
|
|
4348
4625
|
return ret
|
|
4349
4626
|
elif is_symbolic_int:
|
|
4350
|
-
|
|
4351
|
-
return SymbolicFloat(z3.ToReal(val.var))
|
|
4627
|
+
return val.__float__()
|
|
4352
4628
|
return float(realize(val))
|
|
4353
4629
|
|
|
4354
4630
|
|
|
4631
|
+
def _frozenset(itr=()) -> Union[set, LinearSet]:
|
|
4632
|
+
if isinstance(itr, set):
|
|
4633
|
+
return LinearSet(itr)
|
|
4634
|
+
else:
|
|
4635
|
+
return LinearSet.check_unique_and_create(itr)
|
|
4636
|
+
|
|
4637
|
+
|
|
4355
4638
|
# Trick the system into believing that symbolic values are
|
|
4356
4639
|
# native types.
|
|
4357
4640
|
def _issubclass(subclass, superclass):
|
|
@@ -4399,7 +4682,7 @@ def _len(ls):
|
|
|
4399
4682
|
def _map(fn, *iters):
|
|
4400
4683
|
# Wrap the `map` callback in a pure Python lambda.
|
|
4401
4684
|
# This de-optimization ensures that the callback can be intercepted.
|
|
4402
|
-
return map(lambda
|
|
4685
|
+
return map(lambda *a: fn(*a), *iters)
|
|
4403
4686
|
|
|
4404
4687
|
|
|
4405
4688
|
def _memoryview(source):
|
|
@@ -4415,15 +4698,12 @@ def _ord(c: str) -> int:
|
|
|
4415
4698
|
with NoTracing():
|
|
4416
4699
|
if isinstance(c, LazyIntSymbolicStr):
|
|
4417
4700
|
return c._codepoints[0]
|
|
4418
|
-
elif isinstance(c, SeqBasedSymbolicStr):
|
|
4419
|
-
space = context_statespace()
|
|
4420
|
-
ret = SymbolicInt("ord" + space.uniq())
|
|
4421
|
-
space.add(c.var == z3.Unit(ret.var))
|
|
4422
|
-
return ret
|
|
4423
4701
|
return ord(realize(c))
|
|
4424
4702
|
|
|
4425
4703
|
|
|
4426
4704
|
def _pow(base, exp, mod=None):
|
|
4705
|
+
# TODO: we should be able to loosen this up a little.
|
|
4706
|
+
# TODO: move this into the __pow__ definitions. (different smt vars will have different needs)
|
|
4427
4707
|
return pow(realize(base), realize(exp), realize(mod))
|
|
4428
4708
|
|
|
4429
4709
|
|
|
@@ -4445,9 +4725,8 @@ def _repr(obj: object) -> str:
|
|
|
4445
4725
|
|
|
4446
4726
|
|
|
4447
4727
|
def _set(itr=_MISSING) -> Union[set, ShellMutableSet]:
|
|
4448
|
-
|
|
4728
|
+
with NoTracing():
|
|
4449
4729
|
return ShellMutableSet() if itr is _MISSING else ShellMutableSet(itr)
|
|
4450
|
-
return set() if itr is _MISSING else set(itr)
|
|
4451
4730
|
|
|
4452
4731
|
|
|
4453
4732
|
def _setattr(obj: object, name: str, value: object) -> None:
|
|
@@ -4494,7 +4773,7 @@ def _int_from_bytes(
|
|
|
4494
4773
|
) -> int:
|
|
4495
4774
|
if byteorder is _MISSING:
|
|
4496
4775
|
# byteorder defaults to "big" as of 3.11
|
|
4497
|
-
if
|
|
4776
|
+
if version_info >= (3, 11):
|
|
4498
4777
|
byteorder = "big"
|
|
4499
4778
|
else:
|
|
4500
4779
|
raise TypeError
|
|
@@ -4506,8 +4785,12 @@ def _int_from_bytes(
|
|
|
4506
4785
|
little = True
|
|
4507
4786
|
else:
|
|
4508
4787
|
raise ValueError
|
|
4509
|
-
if not
|
|
4510
|
-
|
|
4788
|
+
if not isinstance(b, Sized):
|
|
4789
|
+
if is_iterable(b):
|
|
4790
|
+
b = list(b)
|
|
4791
|
+
else:
|
|
4792
|
+
raise TypeError
|
|
4793
|
+
|
|
4511
4794
|
byteitr: Iterable[int] = reversed(b) if little else b
|
|
4512
4795
|
val = 0
|
|
4513
4796
|
invert = None
|
|
@@ -4621,12 +4904,23 @@ def _str_join(self, itr) -> str:
|
|
|
4621
4904
|
return _join(self, itr, self_type=str, item_type=str)
|
|
4622
4905
|
|
|
4623
4906
|
|
|
4624
|
-
def
|
|
4625
|
-
|
|
4907
|
+
def _str_percent_format(self, other):
|
|
4908
|
+
# Almost nobody uses percent formatting anymore, so it's
|
|
4909
|
+
# probably OK to realize here.
|
|
4910
|
+
# TODO: However, collections.namedtuple still uses percent formatting to
|
|
4911
|
+
# do reprs, so we should consider handling the special case when there
|
|
4912
|
+
# are only "%r" substitutions.
|
|
4913
|
+
if not isinstance(self, str):
|
|
4914
|
+
raise TypeError
|
|
4915
|
+
return self.__mod__(deep_realize(other))
|
|
4916
|
+
|
|
4917
|
+
|
|
4918
|
+
def _bytes_join(self, itr) -> bytes:
|
|
4919
|
+
return _join(self, itr, self_type=bytes, item_type=Buffer)
|
|
4626
4920
|
|
|
4627
4921
|
|
|
4628
|
-
def _bytearray_join(self, itr) ->
|
|
4629
|
-
return _join(self, itr, self_type=bytearray, item_type=
|
|
4922
|
+
def _bytearray_join(self, itr) -> bytes:
|
|
4923
|
+
return _join(self, itr, self_type=bytearray, item_type=Buffer)
|
|
4630
4924
|
|
|
4631
4925
|
|
|
4632
4926
|
def _str_format(self, *a, **kw) -> Union[AnySymbolicStr, str]:
|
|
@@ -4640,14 +4934,17 @@ def _str_format_map(self, map) -> Union[AnySymbolicStr, str]:
|
|
|
4640
4934
|
|
|
4641
4935
|
|
|
4642
4936
|
def _str_startswith(self, substr, start=None, end=None) -> bool:
|
|
4643
|
-
if not isinstance(self, str):
|
|
4644
|
-
raise TypeError
|
|
4645
4937
|
with NoTracing():
|
|
4938
|
+
if isinstance(self, LazyIntSymbolicStr):
|
|
4939
|
+
with ResumedTracing():
|
|
4940
|
+
return self.startswith(substr, start, end)
|
|
4941
|
+
elif not isinstance(self, str):
|
|
4942
|
+
raise TypeError
|
|
4646
4943
|
# Handle native values with native implementation:
|
|
4647
4944
|
if type(substr) is str:
|
|
4648
4945
|
return self.startswith(substr, start, end)
|
|
4649
4946
|
if type(substr) is tuple:
|
|
4650
|
-
if all(type(
|
|
4947
|
+
if all(type(s) is str for s in substr):
|
|
4651
4948
|
return self.startswith(substr, start, end)
|
|
4652
4949
|
symbolic_self = LazyIntSymbolicStr([ord(c) for c in self])
|
|
4653
4950
|
return symbolic_self.startswith(substr, start, end)
|
|
@@ -4693,7 +4990,7 @@ def make_registrations():
|
|
|
4693
4990
|
|
|
4694
4991
|
register_type(Union, make_union_choice)
|
|
4695
4992
|
|
|
4696
|
-
if
|
|
4993
|
+
if version_info >= (3, 8):
|
|
4697
4994
|
from typing import Final
|
|
4698
4995
|
|
|
4699
4996
|
register_type(Final, lambda p, t: p(t))
|
|
@@ -4701,17 +4998,18 @@ def make_registrations():
|
|
|
4701
4998
|
# Types modeled in the SMT solver:
|
|
4702
4999
|
|
|
4703
5000
|
register_type(NoneType, lambda *a: None)
|
|
4704
|
-
register_type(bool,
|
|
4705
|
-
register_type(int,
|
|
4706
|
-
register_type(
|
|
4707
|
-
register_type(
|
|
4708
|
-
register_type(
|
|
5001
|
+
register_type(bool, make_concrete_or_symbolic(SymbolicBool))
|
|
5002
|
+
# register_type(int, make_concrete_or_symbolic(SymbolicInt))
|
|
5003
|
+
register_type(int, make_concrete_or_symbolic(SymbolicBoundedInt))
|
|
5004
|
+
register_type(float, make_concrete_or_symbolic(make_float))
|
|
5005
|
+
register_type(str, make_concrete_or_symbolic(LazyIntSymbolicStr))
|
|
5006
|
+
register_type(list, make_concrete_or_symbolic(SymbolicList))
|
|
4709
5007
|
register_type(dict, make_dictionary)
|
|
4710
5008
|
register_type(range, make_range)
|
|
4711
5009
|
register_type(tuple, make_tuple)
|
|
4712
5010
|
register_type(set, make_set)
|
|
4713
|
-
register_type(frozenset,
|
|
4714
|
-
register_type(type,
|
|
5011
|
+
register_type(frozenset, make_concrete_or_symbolic(SymbolicFrozenSet))
|
|
5012
|
+
register_type(type, make_concrete_or_symbolic(SymbolicType))
|
|
4715
5013
|
register_type(
|
|
4716
5014
|
collections.abc.Callable,
|
|
4717
5015
|
lambda p, *t: SymbolicCallable(p(List.__getitem__(t[1] if t else object))),
|
|
@@ -4743,8 +5041,8 @@ def make_registrations():
|
|
|
4743
5041
|
|
|
4744
5042
|
register_type(NamedTuple, lambda p, *t: p(Tuple.__getitem__(tuple(t))))
|
|
4745
5043
|
|
|
4746
|
-
register_type(re.Pattern, lambda p, t=None:
|
|
4747
|
-
register_type(re.Match,
|
|
5044
|
+
register_type(re.Pattern, lambda p, t=None: re.compile(realize(p(str))))
|
|
5045
|
+
register_type(re.Match, make_raiser(CrosshairUnsupported))
|
|
4748
5046
|
|
|
4749
5047
|
# Text: (elsewhere - identical to str)
|
|
4750
5048
|
register_type(bytes, make_byte_string)
|
|
@@ -4762,7 +5060,7 @@ def make_registrations():
|
|
|
4762
5060
|
register_type(SupportsFloat, lambda p: p(float))
|
|
4763
5061
|
register_type(SupportsInt, lambda p: p(int))
|
|
4764
5062
|
register_type(SupportsRound, lambda p: p(float))
|
|
4765
|
-
register_type(SupportsBytes, lambda p: p(
|
|
5063
|
+
register_type(SupportsBytes, lambda p: p(Buffer))
|
|
4766
5064
|
register_type(SupportsComplex, lambda p: p(complex))
|
|
4767
5065
|
|
|
4768
5066
|
# Patches
|
|
@@ -4797,6 +5095,7 @@ def make_registrations():
|
|
|
4797
5095
|
register_patch(bytes, _bytes)
|
|
4798
5096
|
register_patch(dict, _dict)
|
|
4799
5097
|
register_patch(float, _float)
|
|
5098
|
+
register_patch(frozenset, _frozenset)
|
|
4800
5099
|
register_patch(int, _int)
|
|
4801
5100
|
register_patch(memoryview, _memoryview)
|
|
4802
5101
|
register_patch(range, _range)
|
|
@@ -4850,7 +5149,7 @@ def make_registrations():
|
|
|
4850
5149
|
"upper",
|
|
4851
5150
|
"zfill",
|
|
4852
5151
|
]
|
|
4853
|
-
if
|
|
5152
|
+
if version_info >= (3, 9):
|
|
4854
5153
|
names_to_str_patch.append("removeprefix")
|
|
4855
5154
|
names_to_str_patch.append("removesuffix")
|
|
4856
5155
|
for name in names_to_str_patch:
|
|
@@ -4869,6 +5168,7 @@ def make_registrations():
|
|
|
4869
5168
|
register_patch(str.__contains__, _str_contains)
|
|
4870
5169
|
register_patch(str.join, _str_join)
|
|
4871
5170
|
register_patch(str.__repr__, with_symbolic_self(LazyIntSymbolicStr, str.__repr__))
|
|
5171
|
+
register_patch(str.__mod__, _str_percent_format)
|
|
4872
5172
|
|
|
4873
5173
|
# Patches on bytes
|
|
4874
5174
|
register_patch(bytes.join, _bytes_join)
|
|
@@ -4879,28 +5179,48 @@ def make_registrations():
|
|
|
4879
5179
|
register_patch(bytearray.fromhex, SymbolicByteArray.fromhex)
|
|
4880
5180
|
|
|
4881
5181
|
# Patches on list
|
|
4882
|
-
register_patch(list.
|
|
5182
|
+
register_patch(list.__len__, with_checked_self(list, "__len__"))
|
|
4883
5183
|
register_patch(list.__repr__, _list_repr)
|
|
5184
|
+
register_patch(list.copy, with_checked_self(list, "copy"))
|
|
5185
|
+
register_patch(list.index, _list_index)
|
|
5186
|
+
register_patch(list.pop, with_checked_self(list, "pop"))
|
|
4884
5187
|
|
|
4885
5188
|
# Patches on dict
|
|
4886
|
-
register_patch(dict.
|
|
5189
|
+
register_patch(dict.__len__, with_checked_self(dict, "__len__"))
|
|
4887
5190
|
register_patch(dict.__repr__, _dict_repr)
|
|
5191
|
+
register_patch(dict.copy, with_checked_self(dict, "copy"))
|
|
5192
|
+
register_patch(dict.items, with_checked_self(dict, "items"))
|
|
5193
|
+
register_patch(dict.keys, with_checked_self(dict, "keys"))
|
|
4888
5194
|
# TODO: dict.update (concrete w/ symbolic argument), __getitem__, & more?
|
|
5195
|
+
register_patch(dict.get, _dict_get)
|
|
5196
|
+
register_patch(dict.values, with_checked_self(dict, "values"))
|
|
4889
5197
|
|
|
4890
5198
|
# Patches on set/frozenset
|
|
4891
5199
|
register_patch(set.__repr__, _set_repr)
|
|
4892
5200
|
register_patch(frozenset.__repr__, _frozenset_repr)
|
|
5201
|
+
register_patch(set.copy, with_checked_self(set, "copy"))
|
|
5202
|
+
register_patch(frozenset.copy, with_checked_self(frozenset, "copy"))
|
|
5203
|
+
register_patch(set.pop, with_checked_self(set, "pop"))
|
|
4893
5204
|
|
|
4894
5205
|
# Patches on int
|
|
5206
|
+
register_patch(int.__repr__, with_checked_self(int, "__repr__"))
|
|
5207
|
+
register_patch(int.as_integer_ratio, with_checked_self(int, "as_integer_ratio"))
|
|
5208
|
+
if version_info >= (3, 10):
|
|
5209
|
+
register_patch(int.bit_count, with_checked_self(int, "bit_count"))
|
|
5210
|
+
register_patch(int.bit_length, with_checked_self(int, "bit_length"))
|
|
5211
|
+
register_patch(int.conjugate, with_checked_self(int, "conjugate"))
|
|
4895
5212
|
register_patch(int.from_bytes, _int_from_bytes)
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
register_patch(int.__repr__, with_symbolic_self(SymbolicInt, int.__repr__))
|
|
5213
|
+
if version_info >= (3, 12):
|
|
5214
|
+
register_patch(int.is_integer, with_checked_self(int, "is_integer"))
|
|
5215
|
+
register_patch(int.to_bytes, with_checked_self(int, "to_bytes"))
|
|
4900
5216
|
|
|
4901
5217
|
# Patches on float
|
|
5218
|
+
register_patch(float.__repr__, with_checked_self(float, "__repr__"))
|
|
4902
5219
|
register_patch(float.fromhex, with_realized_args(float.fromhex))
|
|
4903
|
-
register_patch(float.
|
|
5220
|
+
register_patch(float.as_integer_ratio, with_checked_self(float, "as_integer_ratio"))
|
|
5221
|
+
register_patch(float.conjugate, with_checked_self(float, "conjugate"))
|
|
5222
|
+
register_patch(float.hex, with_checked_self(float, "hex"))
|
|
5223
|
+
register_patch(float.is_integer, with_checked_self(float, "is_integer"))
|
|
4904
5224
|
|
|
4905
5225
|
# Patches on tuples
|
|
4906
5226
|
register_patch(tuple.__repr__, _tuple_repr)
|