crosshair-tool 0.0.84__cp312-cp312-macosx_11_0_arm64.whl → 0.0.86__cp312-cp312-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.
Potentially problematic release.
This version of crosshair-tool might be problematic. Click here for more details.
- _crosshair_tracers.cpython-312-darwin.so +0 -0
- crosshair/__init__.py +1 -1
- crosshair/_mark_stacks.h +0 -25
- crosshair/_tracers.h +2 -0
- crosshair/_tracers_test.py +8 -2
- crosshair/condition_parser.py +5 -5
- crosshair/condition_parser_test.py +1 -1
- crosshair/copyext.py +23 -7
- crosshair/copyext_test.py +11 -1
- crosshair/dynamic_typing.py +1 -1
- crosshair/fnutil_test.py +4 -1
- crosshair/libimpl/arraylib.py +0 -13
- crosshair/libimpl/builtinslib.py +26 -274
- crosshair/libimpl/builtinslib_test.py +1 -1
- crosshair/libimpl/collectionslib.py +13 -2
- crosshair/libimpl/collectionslib_test.py +10 -2
- crosshair/libimpl/timelib.py +34 -15
- crosshair/libimpl/timelib_test.py +12 -2
- crosshair/libimpl/typeslib_test.py +2 -1
- crosshair/lsp_server.py +1 -1
- crosshair/opcode_intercept.py +131 -47
- crosshair/opcode_intercept_test.py +97 -6
- crosshair/statespace.py +9 -4
- crosshair/tracers.py +27 -9
- crosshair/type_repo.py +2 -2
- crosshair/unicode_categories.py +1 -0
- crosshair/util.py +48 -17
- {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/METADATA +1 -1
- {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/RECORD +33 -33
- {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/WHEEL +2 -1
- {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/entry_points.txt +0 -0
- {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/licenses/LICENSE +0 -0
- {crosshair_tool-0.0.84.dist-info → crosshair_tool-0.0.86.dist-info}/top_level.txt +0 -0
|
Binary file
|
crosshair/__init__.py
CHANGED
|
@@ -15,7 +15,7 @@ from crosshair.statespace import StateSpace
|
|
|
15
15
|
from crosshair.tracers import NoTracing, ResumedTracing
|
|
16
16
|
from crosshair.util import IgnoreAttempt, debug
|
|
17
17
|
|
|
18
|
-
__version__ = "0.0.
|
|
18
|
+
__version__ = "0.0.86" # Do not forget to update in setup.py!
|
|
19
19
|
__author__ = "Phillip Schanely"
|
|
20
20
|
__license__ = "MIT"
|
|
21
21
|
__status__ = "Alpha"
|
crosshair/_mark_stacks.h
CHANGED
|
@@ -538,31 +538,6 @@ static const uint8_t _ch_DE_INSTRUMENT[256] = {
|
|
|
538
538
|
#endif
|
|
539
539
|
#endif
|
|
540
540
|
|
|
541
|
-
static const uint8_t _ch_TRACABLE_INSTRUCTIONS[256] = {
|
|
542
|
-
// This must be manually kept in sync the the various
|
|
543
|
-
// instructions that we care about on the python side.
|
|
544
|
-
[MAP_ADD] = 1,
|
|
545
|
-
[BINARY_SUBSCR] = 1,
|
|
546
|
-
[BINARY_SLICE] = 1,
|
|
547
|
-
[CONTAINS_OP] = 1,
|
|
548
|
-
[BUILD_STRING] = 1,
|
|
549
|
-
#if PY_VERSION_HEX < 0x030D0000
|
|
550
|
-
// <= 3.12
|
|
551
|
-
[FORMAT_VALUE] = 1,
|
|
552
|
-
#elif PY_VERSION_HEX < 0x030E0000
|
|
553
|
-
// 3.13
|
|
554
|
-
[CALL_KW] = 1,
|
|
555
|
-
[CONVERT_VALUE] = 1,
|
|
556
|
-
#endif
|
|
557
|
-
[UNARY_NOT] = 1,
|
|
558
|
-
[SET_ADD] = 1,
|
|
559
|
-
[IS_OP] = 1,
|
|
560
|
-
[BINARY_OP] = 1,
|
|
561
|
-
[CALL] = 1,
|
|
562
|
-
[CALL_FUNCTION_EX] = 1,
|
|
563
|
-
};
|
|
564
|
-
|
|
565
|
-
|
|
566
541
|
/* Get the underlying opcode, stripping instrumentation */
|
|
567
542
|
int _ch_Py_GetBaseOpcode(PyCodeObject *code, int i)
|
|
568
543
|
{
|
crosshair/_tracers.h
CHANGED
crosshair/_tracers_test.py
CHANGED
|
@@ -89,7 +89,10 @@ def _log_execution_stacks(fn, *a, **kw):
|
|
|
89
89
|
return stacks
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
@pytest.mark.skipif(
|
|
92
|
+
@pytest.mark.skipif(
|
|
93
|
+
sys.version_info < (3, 12) or sys.version_info >= (3, 14),
|
|
94
|
+
reason="stack depths only in 3.12 & 3.13",
|
|
95
|
+
)
|
|
93
96
|
def test_one_function_stack_depth():
|
|
94
97
|
_E = (TypeError, KeyboardInterrupt)
|
|
95
98
|
|
|
@@ -100,7 +103,10 @@ def test_one_function_stack_depth():
|
|
|
100
103
|
_log_execution_stacks(a, 4)
|
|
101
104
|
|
|
102
105
|
|
|
103
|
-
@pytest.mark.skipif(
|
|
106
|
+
@pytest.mark.skipif(
|
|
107
|
+
sys.version_info < (3, 12) or sys.version_info >= (3, 14),
|
|
108
|
+
reason="stack depths only in 3.12 & 3.13",
|
|
109
|
+
)
|
|
104
110
|
def test_stack_get():
|
|
105
111
|
def to_be_traced(x):
|
|
106
112
|
r = 8 - x
|
crosshair/condition_parser.py
CHANGED
|
@@ -47,6 +47,7 @@ from crosshair.options import AnalysisKind
|
|
|
47
47
|
from crosshair.register_contract import get_contract
|
|
48
48
|
from crosshair.tracers import NoTracing
|
|
49
49
|
from crosshair.util import (
|
|
50
|
+
CrossHairInternal,
|
|
50
51
|
DynamicScopeVar,
|
|
51
52
|
EvalFriendlyReprContext,
|
|
52
53
|
IdKeyedDict,
|
|
@@ -485,6 +486,7 @@ class ConcreteConditionParser(ConditionParser):
|
|
|
485
486
|
method = cls.__dict__.get(method_name, None)
|
|
486
487
|
super_method_conditions = super_methods.get(method_name)
|
|
487
488
|
if super_method_conditions is not None:
|
|
489
|
+
# Re-type the super's `self` argument to be this class:
|
|
488
490
|
revised_sig = set_first_arg_type(super_method_conditions.sig, cls)
|
|
489
491
|
super_method_conditions = replace(
|
|
490
492
|
super_method_conditions, sig=revised_sig
|
|
@@ -511,17 +513,15 @@ class ConcreteConditionParser(ConditionParser):
|
|
|
511
513
|
final_pre = list(conditions.pre)
|
|
512
514
|
final_post = list(conditions.post)
|
|
513
515
|
if method_name in (
|
|
514
|
-
"__new__", #
|
|
516
|
+
"__new__", # a staticmethod, but not isinstance(staticmethod)
|
|
515
517
|
"__repr__", # is itself required for reporting problems with invariants.
|
|
516
518
|
# [set/del]attr can do anything; we can't resonably enforce invariants:
|
|
517
519
|
"__setattr__",
|
|
518
520
|
"__delattr__",
|
|
521
|
+
"__replace__", # Will raise an exception with most arbitrary **kwargs.
|
|
522
|
+
"__annotate__", # a staticmethod, but not isinstance(staticmethod)
|
|
519
523
|
):
|
|
520
524
|
pass
|
|
521
|
-
elif method_name == "__replace__":
|
|
522
|
-
# TODO: remove this case when fixed in 3.13
|
|
523
|
-
# see https://github.com/python/cpython/issues/114198
|
|
524
|
-
pass
|
|
525
525
|
elif method_name == "__del__":
|
|
526
526
|
final_pre.extend(inv)
|
|
527
527
|
elif method_name == "__init__":
|
|
@@ -140,7 +140,7 @@ class TestPep316Parser:
|
|
|
140
140
|
"self.x >= 0",
|
|
141
141
|
"self.y >= 0",
|
|
142
142
|
}
|
|
143
|
-
assert set(class_conditions.methods.keys())
|
|
143
|
+
assert {"isready", "__init__"} <= set(class_conditions.methods.keys())
|
|
144
144
|
method = class_conditions.methods["isready"]
|
|
145
145
|
assert set([c.expr_source for c in method.pre]) == {
|
|
146
146
|
"self.x >= 0",
|
crosshair/copyext.py
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
if sys.version_info >= (3, 14):
|
|
4
|
+
from copy import _atomic_types
|
|
5
|
+
else:
|
|
6
|
+
from copy import _deepcopy_atomic # type: ignore
|
|
2
7
|
from copy import _deepcopy_dict # type: ignore
|
|
3
8
|
from copy import _deepcopy_dispatch # type: ignore
|
|
4
9
|
from copy import _deepcopy_list # type: ignore
|
|
@@ -9,7 +14,7 @@ from copy import Error
|
|
|
9
14
|
from copyreg import dispatch_table # type: ignore
|
|
10
15
|
from enum import Enum
|
|
11
16
|
from types import MappingProxyType
|
|
12
|
-
from typing import Any, Dict, Tuple
|
|
17
|
+
from typing import Any, Callable, Dict, Tuple
|
|
13
18
|
|
|
14
19
|
from crosshair.tracers import ResumedTracing
|
|
15
20
|
from crosshair.util import (
|
|
@@ -85,17 +90,28 @@ def deepcopyext(obj: object, mode: CopyMode, memo: Dict) -> Any:
|
|
|
85
90
|
return cpy
|
|
86
91
|
|
|
87
92
|
|
|
93
|
+
if sys.version_info >= (3, 14):
|
|
94
|
+
|
|
95
|
+
def lookup_dispatch(cls: type) -> Callable:
|
|
96
|
+
if cls in _atomic_types:
|
|
97
|
+
return lambda obj, memo: obj
|
|
98
|
+
return _deepcopy_dispatch.get(cls)
|
|
99
|
+
|
|
100
|
+
else:
|
|
101
|
+
|
|
102
|
+
def lookup_dispatch(cls: type) -> Callable:
|
|
103
|
+
return _deepcopy_dispatch.get(cls)
|
|
104
|
+
|
|
105
|
+
|
|
88
106
|
def _deepconstruct(obj: object, mode: CopyMode, memo: Dict):
|
|
89
107
|
cls = type(obj)
|
|
90
108
|
|
|
91
109
|
def subdeepcopy(obj: object, memo: Dict):
|
|
92
110
|
return deepcopyext(obj, mode, memo)
|
|
93
111
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if creator
|
|
97
|
-
return obj
|
|
98
|
-
elif creator in (_deepcopy_dict, _deepcopy_list, _deepcopy_tuple):
|
|
112
|
+
creator = lookup_dispatch(cls)
|
|
113
|
+
if creator is not None:
|
|
114
|
+
if creator in (_deepcopy_dict, _deepcopy_list, _deepcopy_tuple):
|
|
99
115
|
return creator(obj, memo, deepcopy=subdeepcopy)
|
|
100
116
|
else:
|
|
101
117
|
# TODO: We loose subdeepcopy in this case - won't
|
crosshair/copyext_test.py
CHANGED
|
@@ -7,7 +7,7 @@ import pytest
|
|
|
7
7
|
from crosshair.copyext import CopyMode, deepcopyext
|
|
8
8
|
from crosshair.core_and_libs import proxy_for_type, standalone_statespace
|
|
9
9
|
from crosshair.libimpl.builtinslib import SymbolicInt
|
|
10
|
-
from crosshair.tracers import NoTracing
|
|
10
|
+
from crosshair.tracers import NoTracing
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def test_deepcopyext_best_effort():
|
|
@@ -33,6 +33,16 @@ def test_deepcopyext_symbolic_set():
|
|
|
33
33
|
deepcopyext(s, CopyMode.REALIZE, {})
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def test_deepcopyext_realize_simple(space):
|
|
37
|
+
x = SymbolicInt("x")
|
|
38
|
+
input = (x,)
|
|
39
|
+
output = deepcopyext(input, CopyMode.REALIZE, {})
|
|
40
|
+
assert input is not output
|
|
41
|
+
assert input[0] is not output[0]
|
|
42
|
+
assert type(input[0]) is SymbolicInt
|
|
43
|
+
assert type(output[0]) is int
|
|
44
|
+
|
|
45
|
+
|
|
36
46
|
def test_deepcopyext_realize(space):
|
|
37
47
|
x = SymbolicInt("x")
|
|
38
48
|
lock = RLock()
|
crosshair/dynamic_typing.py
CHANGED
|
@@ -4,7 +4,7 @@ from inspect import Parameter, Signature
|
|
|
4
4
|
from itertools import zip_longest
|
|
5
5
|
from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, Type
|
|
6
6
|
|
|
7
|
-
import typing_inspect
|
|
7
|
+
import typing_inspect # type: ignore
|
|
8
8
|
|
|
9
9
|
from crosshair.util import debug # type: ignore
|
|
10
10
|
|
crosshair/fnutil_test.py
CHANGED
|
@@ -26,7 +26,10 @@ def test_fn_globals_on_builtin() -> None:
|
|
|
26
26
|
|
|
27
27
|
def test_resolve_signature_invalid_annotations() -> None:
|
|
28
28
|
sig = resolve_signature(with_invalid_type_annotation)
|
|
29
|
-
|
|
29
|
+
if sys.version_info >= (3, 14):
|
|
30
|
+
assert sig == "TypeThatIsNotDefined"
|
|
31
|
+
else:
|
|
32
|
+
assert sig == "name 'TypeThatIsNotDefined' is not defined"
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
@pytest.mark.skipif(
|
crosshair/libimpl/arraylib.py
CHANGED
|
@@ -30,19 +30,6 @@ INT_TYPE_BOUNDS: Dict[str, Tuple[int, int]] = {
|
|
|
30
30
|
INT_TYPE_SIZE = {c: array(c).itemsize for c in INT_TYPE_BOUNDS.keys()}
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
if sys.version_info >= (3, 12):
|
|
34
|
-
from collections.abc import Buffer
|
|
35
|
-
|
|
36
|
-
def is_bytes_like(obj: object) -> bool:
|
|
37
|
-
return isinstance(obj, Buffer)
|
|
38
|
-
|
|
39
|
-
else:
|
|
40
|
-
from collections.abc import ByteString
|
|
41
|
-
|
|
42
|
-
def is_bytes_like(obj: object) -> bool:
|
|
43
|
-
return isinstance(obj, (ByteString, array))
|
|
44
|
-
|
|
45
|
-
|
|
46
33
|
def pick_code(space: StateSpace) -> Tuple[str, int, int]:
|
|
47
34
|
last_idx = len(INT_TYPE_BOUNDS) - 1
|
|
48
35
|
for (idx, (code, rng)) in enumerate(INT_TYPE_BOUNDS.items()):
|
crosshair/libimpl/builtinslib.py
CHANGED
|
@@ -17,7 +17,7 @@ from dataclasses import dataclass
|
|
|
17
17
|
from itertools import zip_longest
|
|
18
18
|
from math import inf, isfinite, isinf, isnan, nan
|
|
19
19
|
from numbers import Integral, Number, Real
|
|
20
|
-
from sys import maxunicode
|
|
20
|
+
from sys import maxunicode, version_info
|
|
21
21
|
from typing import (
|
|
22
22
|
Any,
|
|
23
23
|
BinaryIO,
|
|
@@ -58,6 +58,8 @@ try:
|
|
|
58
58
|
except ImportError:
|
|
59
59
|
from z3 import FfpEQ as fpEQ
|
|
60
60
|
|
|
61
|
+
import sys
|
|
62
|
+
|
|
61
63
|
from crosshair.abcstring import AbcString
|
|
62
64
|
from crosshair.core import (
|
|
63
65
|
SymbolicFactory,
|
|
@@ -116,6 +118,7 @@ from crosshair.util import (
|
|
|
116
118
|
assert_tracing,
|
|
117
119
|
ch_stack,
|
|
118
120
|
debug,
|
|
121
|
+
is_bytes_like,
|
|
119
122
|
is_hashable,
|
|
120
123
|
is_iterable,
|
|
121
124
|
memo,
|
|
@@ -399,6 +402,10 @@ def crosshair_types_for_python_type(
|
|
|
399
402
|
return _PYTYPE_TO_WRAPPER_TYPE.get(origin, ())
|
|
400
403
|
|
|
401
404
|
|
|
405
|
+
def python_types_using_atomic_symbolics() -> Iterable[Type[AtomicSymbolicValue]]:
|
|
406
|
+
return _PYTYPE_TO_WRAPPER_TYPE.keys()
|
|
407
|
+
|
|
408
|
+
|
|
402
409
|
class ModelingDirector:
|
|
403
410
|
def __init__(self, *a) -> None:
|
|
404
411
|
# Maps python type to the symbolic type we've chosen to represent it (on this iteration)
|
|
@@ -1213,10 +1220,11 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
|
|
|
1213
1220
|
cur_divisor = 10
|
|
1214
1221
|
while True:
|
|
1215
1222
|
leftover = self // cur_divisor
|
|
1216
|
-
if leftover
|
|
1223
|
+
if leftover != 0:
|
|
1224
|
+
codepoints.append(48 + (leftover % 10))
|
|
1225
|
+
cur_divisor *= 10
|
|
1226
|
+
else:
|
|
1217
1227
|
break
|
|
1218
|
-
codepoints.append(48 + (leftover % 10))
|
|
1219
|
-
cur_divisor *= 10
|
|
1220
1228
|
with NoTracing():
|
|
1221
1229
|
codepoints.reverse()
|
|
1222
1230
|
return LazyIntSymbolicStr(codepoints)
|
|
@@ -1316,7 +1324,7 @@ class SymbolicInt(SymbolicIntable, AtomicSymbolicValue):
|
|
|
1316
1324
|
z3.If(val < 128, 7, 8)))))))))
|
|
1317
1325
|
# fmt: on
|
|
1318
1326
|
|
|
1319
|
-
if
|
|
1327
|
+
if version_info >= (3, 12):
|
|
1320
1328
|
|
|
1321
1329
|
def is_integer(self):
|
|
1322
1330
|
return True
|
|
@@ -2934,7 +2942,7 @@ class AnySymbolicStr(AbcString):
|
|
|
2934
2942
|
def capitalize(self):
|
|
2935
2943
|
if self.__len__() == 0:
|
|
2936
2944
|
return ""
|
|
2937
|
-
if
|
|
2945
|
+
if version_info >= (3, 8):
|
|
2938
2946
|
firstchar = self[0].title()
|
|
2939
2947
|
else:
|
|
2940
2948
|
firstchar = self[0].upper()
|
|
@@ -3172,7 +3180,7 @@ class AnySymbolicStr(AbcString):
|
|
|
3172
3180
|
return ""
|
|
3173
3181
|
|
|
3174
3182
|
def splitlines(self, keepends=False):
|
|
3175
|
-
if
|
|
3183
|
+
if version_info < (3, 12):
|
|
3176
3184
|
if not isinstance(keepends, int):
|
|
3177
3185
|
raise TypeError
|
|
3178
3186
|
mylen = self.__len__()
|
|
@@ -3438,6 +3446,7 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
|
3438
3446
|
SliceView,
|
|
3439
3447
|
SequenceConcatenation,
|
|
3440
3448
|
list, # TODO: are we sharing mutable state here?
|
|
3449
|
+
tuple,
|
|
3441
3450
|
),
|
|
3442
3451
|
):
|
|
3443
3452
|
self._codepoints = smtvar
|
|
@@ -3483,10 +3492,6 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
|
3483
3492
|
otherpoints = [ord(ch) for ch in other]
|
|
3484
3493
|
with ResumedTracing():
|
|
3485
3494
|
return mypoints.__eq__(otherpoints)
|
|
3486
|
-
elif isinstance(other, SeqBasedSymbolicStr):
|
|
3487
|
-
with ResumedTracing():
|
|
3488
|
-
otherpoints = [ord(ch) for ch in other]
|
|
3489
|
-
return mypoints.__eq__(otherpoints)
|
|
3490
3495
|
else:
|
|
3491
3496
|
return NotImplemented
|
|
3492
3497
|
|
|
@@ -3655,257 +3660,6 @@ class LazyIntSymbolicStr(AnySymbolicStr, CrossHairValue):
|
|
|
3655
3660
|
return self._find(substr, start, end, from_right=True)
|
|
3656
3661
|
|
|
3657
3662
|
|
|
3658
|
-
class SeqBasedSymbolicStr(AtomicSymbolicValue, SymbolicSequence, AnySymbolicStr):
|
|
3659
|
-
def __init__(self, smtvar: Union[str, z3.ExprRef], typ: Type = str):
|
|
3660
|
-
assert typ == str
|
|
3661
|
-
SymbolicValue.__init__(self, smtvar, typ)
|
|
3662
|
-
self.item_pytype = str
|
|
3663
|
-
if isinstance(smtvar, str):
|
|
3664
|
-
# Constrain fresh strings to valid codepoints
|
|
3665
|
-
space = context_statespace()
|
|
3666
|
-
idxvar = z3.Int("idxvar" + space.uniq())
|
|
3667
|
-
z3seq = self.var
|
|
3668
|
-
space.add(
|
|
3669
|
-
z3.ForAll(
|
|
3670
|
-
[idxvar], z3.And(0 <= z3seq[idxvar], z3seq[idxvar] <= maxunicode)
|
|
3671
|
-
)
|
|
3672
|
-
)
|
|
3673
|
-
|
|
3674
|
-
@classmethod
|
|
3675
|
-
def _ch_smt_sort(cls) -> z3.SortRef:
|
|
3676
|
-
return z3.SeqSort(z3.IntSort())
|
|
3677
|
-
|
|
3678
|
-
@classmethod
|
|
3679
|
-
def _pytype(cls) -> Type:
|
|
3680
|
-
return str
|
|
3681
|
-
|
|
3682
|
-
@classmethod
|
|
3683
|
-
def _smt_promote_literal(cls, literal) -> Optional[z3.SortRef]:
|
|
3684
|
-
if isinstance(literal, str):
|
|
3685
|
-
if len(literal) <= 1:
|
|
3686
|
-
if len(literal) == 0:
|
|
3687
|
-
return z3.Empty(z3.SeqSort(z3.IntSort()))
|
|
3688
|
-
return z3.Unit(z3IntVal(ord(literal)))
|
|
3689
|
-
return z3.Concat([z3.Unit(z3IntVal(ord(ch))) for ch in literal])
|
|
3690
|
-
return None
|
|
3691
|
-
|
|
3692
|
-
def __ch_realize__(self) -> object:
|
|
3693
|
-
codepoints = context_statespace().find_model_value(self.var)
|
|
3694
|
-
return "".join(chr(x) for x in codepoints)
|
|
3695
|
-
|
|
3696
|
-
def __copy__(self):
|
|
3697
|
-
return SeqBasedSymbolicStr(self.var)
|
|
3698
|
-
|
|
3699
|
-
def __hash__(self):
|
|
3700
|
-
return hash(self.__str__())
|
|
3701
|
-
|
|
3702
|
-
@staticmethod
|
|
3703
|
-
def _concat_strings(
|
|
3704
|
-
a: Union[str, "SeqBasedSymbolicStr"], b: Union[str, "SeqBasedSymbolicStr"]
|
|
3705
|
-
) -> Union[str, "SeqBasedSymbolicStr"]:
|
|
3706
|
-
assert not is_tracing()
|
|
3707
|
-
# Assumes at least one argument is symbolic and not tracing
|
|
3708
|
-
if isinstance(a, SeqBasedSymbolicStr) and isinstance(b, SeqBasedSymbolicStr):
|
|
3709
|
-
return SeqBasedSymbolicStr(a.var + b.var)
|
|
3710
|
-
elif isinstance(a, str) and isinstance(b, SeqBasedSymbolicStr):
|
|
3711
|
-
return SeqBasedSymbolicStr(
|
|
3712
|
-
SeqBasedSymbolicStr._coerce_to_smt_sort(a) + b.var
|
|
3713
|
-
)
|
|
3714
|
-
else:
|
|
3715
|
-
assert isinstance(a, SeqBasedSymbolicStr)
|
|
3716
|
-
assert isinstance(b, str)
|
|
3717
|
-
return SeqBasedSymbolicStr(
|
|
3718
|
-
a.var + SeqBasedSymbolicStr._coerce_to_smt_sort(b)
|
|
3719
|
-
)
|
|
3720
|
-
|
|
3721
|
-
def __add__(self, other):
|
|
3722
|
-
with NoTracing():
|
|
3723
|
-
if isinstance(other, (SeqBasedSymbolicStr, str)):
|
|
3724
|
-
return SeqBasedSymbolicStr._concat_strings(self, other)
|
|
3725
|
-
if isinstance(other, AnySymbolicStr):
|
|
3726
|
-
return NotImplemented
|
|
3727
|
-
raise TypeError
|
|
3728
|
-
|
|
3729
|
-
def __radd__(self, other):
|
|
3730
|
-
with NoTracing():
|
|
3731
|
-
if isinstance(other, (SeqBasedSymbolicStr, str)):
|
|
3732
|
-
return SeqBasedSymbolicStr._concat_strings(other, self)
|
|
3733
|
-
if isinstance(other, AnySymbolicStr):
|
|
3734
|
-
return NotImplemented
|
|
3735
|
-
raise TypeError
|
|
3736
|
-
|
|
3737
|
-
def __mul__(self, other):
|
|
3738
|
-
if isinstance(other, Integral):
|
|
3739
|
-
if other <= 1:
|
|
3740
|
-
return self if other == 1 else ""
|
|
3741
|
-
# Note that in SymbolicInt, we attempt string multiplication via regex.
|
|
3742
|
-
# Z3 cannot do much with a symbolic regex, so we case-split on
|
|
3743
|
-
# the repetition count.
|
|
3744
|
-
return SeqBasedSymbolicStr(z3.Concat(*[self.var for _ in range(other)]))
|
|
3745
|
-
return NotImplemented
|
|
3746
|
-
|
|
3747
|
-
__rmul__ = __mul__
|
|
3748
|
-
|
|
3749
|
-
def __mod__(self, other):
|
|
3750
|
-
return self.__str__() % realize(other)
|
|
3751
|
-
|
|
3752
|
-
def __contains__(self, other):
|
|
3753
|
-
with NoTracing():
|
|
3754
|
-
forced = force_to_smt_sort(other, SeqBasedSymbolicStr)
|
|
3755
|
-
return SymbolicBool(z3.Contains(self.var, forced))
|
|
3756
|
-
|
|
3757
|
-
def __getitem__(self, i: Union[int, slice]):
|
|
3758
|
-
with NoTracing():
|
|
3759
|
-
idx_or_pair = process_slice_vs_symbolic_len(
|
|
3760
|
-
context_statespace(), i, z3.Length(self.var)
|
|
3761
|
-
)
|
|
3762
|
-
if isinstance(idx_or_pair, tuple):
|
|
3763
|
-
(start, stop) = idx_or_pair
|
|
3764
|
-
smt_result = z3.Extract(self.var, start, stop - start)
|
|
3765
|
-
else:
|
|
3766
|
-
smt_result = z3.Unit(self.var[idx_or_pair])
|
|
3767
|
-
return SeqBasedSymbolicStr(smt_result)
|
|
3768
|
-
|
|
3769
|
-
def endswith(self, substr):
|
|
3770
|
-
with NoTracing():
|
|
3771
|
-
smt_substr = force_to_smt_sort(substr, SeqBasedSymbolicStr)
|
|
3772
|
-
return SymbolicBool(z3.SuffixOf(smt_substr, self.var))
|
|
3773
|
-
|
|
3774
|
-
def find(self, substr, start=None, end=None):
|
|
3775
|
-
if not isinstance(substr, str):
|
|
3776
|
-
raise TypeError
|
|
3777
|
-
with NoTracing():
|
|
3778
|
-
space = context_statespace()
|
|
3779
|
-
smt_my_len = z3.Length(self.var)
|
|
3780
|
-
if start is None and end is None:
|
|
3781
|
-
smt_start = z3IntVal(0)
|
|
3782
|
-
smt_end = smt_my_len
|
|
3783
|
-
smt_str = self.var
|
|
3784
|
-
if len(substr) == 0:
|
|
3785
|
-
return 0
|
|
3786
|
-
else:
|
|
3787
|
-
(smt_start, smt_end) = flip_slice_vs_symbolic_len(
|
|
3788
|
-
space, slice(start, end, None), smt_my_len
|
|
3789
|
-
)
|
|
3790
|
-
if len(substr) == 0:
|
|
3791
|
-
# Add oddity of CPython. We can find the empty string when over-slicing
|
|
3792
|
-
# off the left side of the string, but not off the right:
|
|
3793
|
-
# ''.find('', 3, 4) == -1
|
|
3794
|
-
# ''.find('', -4, -3) == 0
|
|
3795
|
-
if space.smt_fork(smt_start > smt_my_len):
|
|
3796
|
-
return -1
|
|
3797
|
-
elif space.smt_fork(smt_start > 0):
|
|
3798
|
-
return SymbolicInt(smt_start)
|
|
3799
|
-
else:
|
|
3800
|
-
return 0
|
|
3801
|
-
(smt_start, smt_end) = clip_range_to_symbolic_len(
|
|
3802
|
-
space, smt_start, smt_end, smt_my_len
|
|
3803
|
-
)
|
|
3804
|
-
smt_str = z3.SubString(self.var, smt_start, smt_end - smt_start)
|
|
3805
|
-
|
|
3806
|
-
smt_sub = force_to_smt_sort(substr, SeqBasedSymbolicStr)
|
|
3807
|
-
if space.smt_fork(z3.Contains(smt_str, smt_sub)):
|
|
3808
|
-
return SymbolicInt(z3.IndexOf(smt_str, smt_sub, 0) + smt_start)
|
|
3809
|
-
else:
|
|
3810
|
-
return -1
|
|
3811
|
-
|
|
3812
|
-
def partition(self, sep: str):
|
|
3813
|
-
if not isinstance(sep, str):
|
|
3814
|
-
raise TypeError
|
|
3815
|
-
if len(sep) == 0:
|
|
3816
|
-
raise ValueError
|
|
3817
|
-
with NoTracing():
|
|
3818
|
-
space = context_statespace()
|
|
3819
|
-
smt_str = self.var
|
|
3820
|
-
smt_sep = force_to_smt_sort(sep, SeqBasedSymbolicStr)
|
|
3821
|
-
if space.smt_fork(z3.Contains(smt_str, smt_sep)):
|
|
3822
|
-
uniq = space.uniq()
|
|
3823
|
-
# Divide my contents into 4 concatenated parts:
|
|
3824
|
-
prefix = SeqBasedSymbolicStr(f"prefix{uniq}")
|
|
3825
|
-
match1 = SeqBasedSymbolicStr(
|
|
3826
|
-
f"match1{uniq}"
|
|
3827
|
-
) # the first character of the match
|
|
3828
|
-
match_tail = SeqBasedSymbolicStr(f"match_tail{uniq}")
|
|
3829
|
-
suffix = SeqBasedSymbolicStr(f"suffix{uniq}")
|
|
3830
|
-
space.add(z3.Length(match1.var) == 1)
|
|
3831
|
-
space.add(smt_sep == z3.Concat(match1.var, match_tail.var))
|
|
3832
|
-
space.add(smt_str == z3.Concat(prefix.var, smt_sep, suffix.var))
|
|
3833
|
-
space.add(
|
|
3834
|
-
z3.Not(z3.Contains(z3.Concat(match_tail.var, suffix.var), smt_sep))
|
|
3835
|
-
)
|
|
3836
|
-
return (prefix, sep, suffix)
|
|
3837
|
-
else:
|
|
3838
|
-
return (self, "", "")
|
|
3839
|
-
|
|
3840
|
-
def rfind(self, substr, start=None, end=None) -> Union[int, SymbolicInt]:
|
|
3841
|
-
if not isinstance(substr, str):
|
|
3842
|
-
raise TypeError
|
|
3843
|
-
with NoTracing():
|
|
3844
|
-
space = context_statespace()
|
|
3845
|
-
smt_my_len = z3.Length(self.var)
|
|
3846
|
-
if start is None and end is None:
|
|
3847
|
-
smt_start = z3IntVal(0)
|
|
3848
|
-
smt_end = smt_my_len
|
|
3849
|
-
smt_str = self.var
|
|
3850
|
-
if len(substr) == 0:
|
|
3851
|
-
return SymbolicInt(smt_my_len)
|
|
3852
|
-
else:
|
|
3853
|
-
(smt_start, smt_end) = flip_slice_vs_symbolic_len(
|
|
3854
|
-
space, slice(start, end, None), smt_my_len
|
|
3855
|
-
)
|
|
3856
|
-
if len(substr) == 0:
|
|
3857
|
-
# Add oddity of CPython. We can find the empty string when over-slicing
|
|
3858
|
-
# off the left side of the string, but not off the right:
|
|
3859
|
-
# ''.find('', 3, 4) == -1
|
|
3860
|
-
# ''.find('', -4, -3) == 0
|
|
3861
|
-
if space.smt_fork(smt_start > smt_my_len):
|
|
3862
|
-
return -1
|
|
3863
|
-
elif space.smt_fork(smt_end < 0):
|
|
3864
|
-
return 0
|
|
3865
|
-
elif space.smt_fork(smt_end < smt_my_len):
|
|
3866
|
-
return SymbolicInt(smt_end)
|
|
3867
|
-
else:
|
|
3868
|
-
return SymbolicInt(smt_my_len)
|
|
3869
|
-
(smt_start, smt_end) = clip_range_to_symbolic_len(
|
|
3870
|
-
space, smt_start, smt_end, smt_my_len
|
|
3871
|
-
)
|
|
3872
|
-
smt_str = z3.SubString(self.var, smt_start, smt_end - smt_start)
|
|
3873
|
-
smt_sub = force_to_smt_sort(substr, SeqBasedSymbolicStr)
|
|
3874
|
-
if space.smt_fork(z3.Contains(smt_str, smt_sub)):
|
|
3875
|
-
uniq = space.uniq()
|
|
3876
|
-
# Divide my contents into 4 concatenated parts:
|
|
3877
|
-
prefix = SeqBasedSymbolicStr(f"prefix{uniq}")
|
|
3878
|
-
match1 = SeqBasedSymbolicStr(f"match1{uniq}")
|
|
3879
|
-
match_tail = SeqBasedSymbolicStr(f"match_tail{uniq}")
|
|
3880
|
-
suffix = SeqBasedSymbolicStr(f"suffix{uniq}")
|
|
3881
|
-
space.add(z3.Length(match1.var) == 1)
|
|
3882
|
-
space.add(smt_sub == z3.Concat(match1.var, match_tail.var))
|
|
3883
|
-
space.add(smt_str == z3.Concat(prefix.var, smt_sub, suffix.var))
|
|
3884
|
-
space.add(
|
|
3885
|
-
z3.Not(z3.Contains(z3.Concat(match_tail.var, suffix.var), smt_sub))
|
|
3886
|
-
)
|
|
3887
|
-
return SymbolicInt(smt_start + z3.Length(prefix.var))
|
|
3888
|
-
else:
|
|
3889
|
-
return -1
|
|
3890
|
-
|
|
3891
|
-
def rpartition(self, sep: str):
|
|
3892
|
-
result = self.rsplit(sep, maxsplit=1)
|
|
3893
|
-
if len(result) == 1:
|
|
3894
|
-
return ("", "", self)
|
|
3895
|
-
elif len(result) == 2:
|
|
3896
|
-
return (result[0], sep, result[1])
|
|
3897
|
-
|
|
3898
|
-
def startswith(self, substr, start=None, end=None):
|
|
3899
|
-
if isinstance(substr, tuple):
|
|
3900
|
-
return any(self.startswith(s, start, end) for s in substr)
|
|
3901
|
-
smt_substr = force_to_smt_sort(substr, SeqBasedSymbolicStr)
|
|
3902
|
-
if start is not None or end is not None:
|
|
3903
|
-
# TODO: "".startswith("", 1) should be False, not True
|
|
3904
|
-
return self[start:end].startswith(substr)
|
|
3905
|
-
with NoTracing():
|
|
3906
|
-
return SymbolicBool(z3.PrefixOf(smt_substr, self.var))
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
3663
|
def buffer_to_byte_seq(obj: object) -> Optional[Sequence[int]]:
|
|
3910
3664
|
if isinstance(obj, (bytes, bytearray)):
|
|
3911
3665
|
return list(obj)
|
|
@@ -3975,7 +3729,7 @@ class BytesLike(Buffer, AbcString, CrossHairValue):
|
|
|
3975
3729
|
return False
|
|
3976
3730
|
return list(self) == list(other)
|
|
3977
3731
|
|
|
3978
|
-
if
|
|
3732
|
+
if version_info >= (3, 12):
|
|
3979
3733
|
|
|
3980
3734
|
def __buffer__(self, flags: int):
|
|
3981
3735
|
with NoTracing():
|
|
@@ -4135,7 +3889,10 @@ class SymbolicBytes(BytesLike):
|
|
|
4135
3889
|
accumulated = []
|
|
4136
3890
|
high = None
|
|
4137
3891
|
if not isinstance(hexstr, str):
|
|
4138
|
-
|
|
3892
|
+
if is_bytes_like(hexstr) and version_info >= (3, 14):
|
|
3893
|
+
hexstr = LazyIntSymbolicStr(tuple(hexstr))
|
|
3894
|
+
else:
|
|
3895
|
+
raise TypeError
|
|
4139
3896
|
for idx, ch in enumerate(hexstr):
|
|
4140
3897
|
if not ch.isascii():
|
|
4141
3898
|
raise ValueError(
|
|
@@ -4814,11 +4571,6 @@ def _ord(c: str) -> int:
|
|
|
4814
4571
|
with NoTracing():
|
|
4815
4572
|
if isinstance(c, LazyIntSymbolicStr):
|
|
4816
4573
|
return c._codepoints[0]
|
|
4817
|
-
elif isinstance(c, SeqBasedSymbolicStr):
|
|
4818
|
-
space = context_statespace()
|
|
4819
|
-
ret = SymbolicInt("ord" + space.uniq())
|
|
4820
|
-
space.add(c.var == z3.Unit(ret.var))
|
|
4821
|
-
return ret
|
|
4822
4574
|
return ord(realize(c))
|
|
4823
4575
|
|
|
4824
4576
|
|
|
@@ -4894,7 +4646,7 @@ def _int_from_bytes(
|
|
|
4894
4646
|
) -> int:
|
|
4895
4647
|
if byteorder is _MISSING:
|
|
4896
4648
|
# byteorder defaults to "big" as of 3.11
|
|
4897
|
-
if
|
|
4649
|
+
if version_info >= (3, 11):
|
|
4898
4650
|
byteorder = "big"
|
|
4899
4651
|
else:
|
|
4900
4652
|
raise TypeError
|
|
@@ -5108,7 +4860,7 @@ def make_registrations():
|
|
|
5108
4860
|
|
|
5109
4861
|
register_type(Union, make_union_choice)
|
|
5110
4862
|
|
|
5111
|
-
if
|
|
4863
|
+
if version_info >= (3, 8):
|
|
5112
4864
|
from typing import Final
|
|
5113
4865
|
|
|
5114
4866
|
register_type(Final, lambda p, t: p(t))
|
|
@@ -5266,7 +5018,7 @@ def make_registrations():
|
|
|
5266
5018
|
"upper",
|
|
5267
5019
|
"zfill",
|
|
5268
5020
|
]
|
|
5269
|
-
if
|
|
5021
|
+
if version_info >= (3, 9):
|
|
5270
5022
|
names_to_str_patch.append("removeprefix")
|
|
5271
5023
|
names_to_str_patch.append("removesuffix")
|
|
5272
5024
|
for name in names_to_str_patch:
|
|
@@ -5322,12 +5074,12 @@ def make_registrations():
|
|
|
5322
5074
|
# Patches on int
|
|
5323
5075
|
register_patch(int.__repr__, with_checked_self(int, "__repr__"))
|
|
5324
5076
|
register_patch(int.as_integer_ratio, with_checked_self(int, "as_integer_ratio"))
|
|
5325
|
-
if
|
|
5077
|
+
if version_info >= (3, 10):
|
|
5326
5078
|
register_patch(int.bit_count, with_checked_self(int, "bit_count"))
|
|
5327
5079
|
register_patch(int.bit_length, with_checked_self(int, "bit_length"))
|
|
5328
5080
|
register_patch(int.conjugate, with_checked_self(int, "conjugate"))
|
|
5329
5081
|
register_patch(int.from_bytes, _int_from_bytes)
|
|
5330
|
-
if
|
|
5082
|
+
if version_info >= (3, 12):
|
|
5331
5083
|
register_patch(int.is_integer, with_checked_self(int, "is_integer"))
|
|
5332
5084
|
register_patch(int.to_bytes, with_checked_self(int, "to_bytes"))
|
|
5333
5085
|
|