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/tracers.py
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
import ctypes
|
|
4
4
|
import dataclasses
|
|
5
5
|
import dis
|
|
6
|
+
import os
|
|
6
7
|
import sys
|
|
8
|
+
import types
|
|
7
9
|
from collections import defaultdict
|
|
8
10
|
from sys import _getframe
|
|
9
11
|
from types import CodeType
|
|
@@ -12,6 +14,7 @@ from typing import (
|
|
|
12
14
|
Callable,
|
|
13
15
|
DefaultDict,
|
|
14
16
|
Dict,
|
|
17
|
+
FrozenSet,
|
|
15
18
|
Iterable,
|
|
16
19
|
List,
|
|
17
20
|
Optional,
|
|
@@ -20,8 +23,11 @@ from typing import (
|
|
|
20
23
|
TypeVar,
|
|
21
24
|
)
|
|
22
25
|
|
|
23
|
-
from _crosshair_tracers import CTracer, TraceSwap
|
|
26
|
+
from _crosshair_tracers import CTracer, TraceSwap, supported_opcodes # type: ignore
|
|
24
27
|
|
|
28
|
+
CROSSHAIR_EXTRA_ASSERTS = os.environ.get("CROSSHAIR_EXTRA_ASSERTS", "0") == "1"
|
|
29
|
+
|
|
30
|
+
SYS_MONITORING_TOOL_ID = 4
|
|
25
31
|
USE_C_TRACER = True
|
|
26
32
|
|
|
27
33
|
PyObjPtr = ctypes.POINTER(ctypes.py_object)
|
|
@@ -42,12 +48,12 @@ _debug_header: Tuple[Tuple[str, type], ...] = (
|
|
|
42
48
|
from _crosshair_tracers import frame_stack_read, frame_stack_write
|
|
43
49
|
|
|
44
50
|
CALL_FUNCTION = dis.opmap.get("CALL_FUNCTION", 256)
|
|
45
|
-
CALL_FUNCTION_KW = dis.opmap.get("CALL_FUNCTION_KW", 256)
|
|
51
|
+
CALL_FUNCTION_KW = dis.opmap.get("CALL_FUNCTION_KW", 256) # Removed as of 3.11
|
|
46
52
|
CALL_FUNCTION_EX = dis.opmap.get("CALL_FUNCTION_EX", 256)
|
|
47
53
|
CALL_METHOD = dis.opmap.get("CALL_METHOD", 256)
|
|
48
54
|
BUILD_TUPLE_UNPACK_WITH_CALL = dis.opmap.get("BUILD_TUPLE_UNPACK_WITH_CALL", 256)
|
|
49
55
|
CALL = dis.opmap.get("CALL", 256)
|
|
50
|
-
CALL_KW = dis.opmap.get("CALL_KW", 256)
|
|
56
|
+
CALL_KW = dis.opmap.get("CALL_KW", 256) # New in 3.13
|
|
51
57
|
|
|
52
58
|
|
|
53
59
|
class RawNullPointer:
|
|
@@ -55,89 +61,113 @@ class RawNullPointer:
|
|
|
55
61
|
|
|
56
62
|
|
|
57
63
|
NULL_POINTER = RawNullPointer()
|
|
64
|
+
CallStackInfo = (
|
|
65
|
+
Tuple[ # Information about the interpreter stack just before calling a function
|
|
66
|
+
int, # stack index of the callable
|
|
67
|
+
Callable, # the callable object itself
|
|
68
|
+
Optional[int], # index of kwargs dict (if used in this call)
|
|
69
|
+
]
|
|
70
|
+
)
|
|
58
71
|
|
|
59
72
|
|
|
60
|
-
def handle_build_tuple_unpack_with_call(frame) ->
|
|
73
|
+
def handle_build_tuple_unpack_with_call(frame) -> CallStackInfo:
|
|
61
74
|
idx = -(
|
|
62
75
|
frame.f_code.co_code[frame.f_lasti + 1] + 1
|
|
63
76
|
) # TODO: account for EXTENDED_ARG, here and elsewhere
|
|
64
77
|
try:
|
|
65
|
-
return (idx, frame_stack_read(frame, idx))
|
|
78
|
+
return (idx, frame_stack_read(frame, idx), None)
|
|
66
79
|
except ValueError:
|
|
67
80
|
return (idx, NULL_POINTER) # type: ignore
|
|
68
81
|
|
|
69
82
|
|
|
70
|
-
def handle_call_3_11(frame) ->
|
|
83
|
+
def handle_call_3_11(frame) -> CallStackInfo:
|
|
71
84
|
idx = -(frame.f_code.co_code[frame.f_lasti + 1] + 1)
|
|
72
85
|
try:
|
|
73
|
-
ret = (idx - 1, frame_stack_read(frame, idx - 1))
|
|
86
|
+
ret = (idx - 1, frame_stack_read(frame, idx - 1), None)
|
|
74
87
|
except ValueError:
|
|
75
|
-
ret = (idx, frame_stack_read(frame, idx))
|
|
88
|
+
ret = (idx, frame_stack_read(frame, idx), None)
|
|
76
89
|
return ret
|
|
77
90
|
|
|
78
91
|
|
|
79
|
-
def handle_call_3_13(frame) ->
|
|
92
|
+
def handle_call_3_13(frame) -> CallStackInfo:
|
|
80
93
|
idx = -(frame.f_code.co_code[frame.f_lasti + 1] + 2)
|
|
81
|
-
return (idx, frame_stack_read(frame, idx))
|
|
94
|
+
return (idx, frame_stack_read(frame, idx), None)
|
|
82
95
|
|
|
83
96
|
|
|
84
|
-
def handle_call_function(frame) ->
|
|
97
|
+
def handle_call_function(frame) -> CallStackInfo:
|
|
85
98
|
idx = -(frame.f_code.co_code[frame.f_lasti + 1] + 1)
|
|
86
99
|
try:
|
|
87
|
-
return (idx, frame_stack_read(frame, idx))
|
|
100
|
+
return (idx, frame_stack_read(frame, idx), None)
|
|
88
101
|
except ValueError:
|
|
89
102
|
return (idx, NULL_POINTER) # type: ignore
|
|
90
103
|
|
|
91
104
|
|
|
92
|
-
def handle_call_function_kw(frame) ->
|
|
105
|
+
def handle_call_function_kw(frame) -> CallStackInfo:
|
|
93
106
|
idx = -(frame.f_code.co_code[frame.f_lasti + 1] + 2)
|
|
94
107
|
try:
|
|
95
|
-
return (idx, frame_stack_read(frame, idx))
|
|
108
|
+
return (idx, frame_stack_read(frame, idx), None)
|
|
96
109
|
except ValueError:
|
|
97
110
|
return (idx, NULL_POINTER) # type: ignore
|
|
98
111
|
|
|
99
112
|
|
|
100
|
-
def handle_call_kw(frame) ->
|
|
113
|
+
def handle_call_kw(frame) -> CallStackInfo:
|
|
101
114
|
idx = -(frame.f_code.co_code[frame.f_lasti + 1] + 3)
|
|
102
|
-
return (idx, frame_stack_read(frame, idx))
|
|
115
|
+
return (idx, frame_stack_read(frame, idx), None)
|
|
103
116
|
|
|
104
117
|
|
|
105
|
-
def handle_call_function_ex_3_6(frame) ->
|
|
106
|
-
|
|
118
|
+
def handle_call_function_ex_3_6(frame) -> CallStackInfo:
|
|
119
|
+
has_kwargs = frame.f_code.co_code[frame.f_lasti + 1] & 1
|
|
120
|
+
idx = -(has_kwargs + 2)
|
|
121
|
+
kwargs_idx = -1 if has_kwargs else None
|
|
107
122
|
try:
|
|
108
|
-
|
|
109
|
-
return (idx, frame_stack_read(frame, idx))
|
|
123
|
+
return (idx, frame_stack_read(frame, idx), kwargs_idx)
|
|
110
124
|
except ValueError:
|
|
111
|
-
return (idx, NULL_POINTER) # type: ignore
|
|
125
|
+
return (idx, NULL_POINTER, kwargs_idx) # type: ignore
|
|
112
126
|
|
|
113
127
|
|
|
114
|
-
def handle_call_function_ex_3_13(frame) ->
|
|
115
|
-
|
|
128
|
+
def handle_call_function_ex_3_13(frame) -> CallStackInfo:
|
|
129
|
+
has_kwargs = frame.f_code.co_code[frame.f_lasti + 1] & 1
|
|
130
|
+
idx = -(has_kwargs + 3)
|
|
131
|
+
kwargs_idx = -1 if has_kwargs else None
|
|
116
132
|
try:
|
|
117
|
-
return (idx, frame_stack_read(frame, idx))
|
|
133
|
+
return (idx, frame_stack_read(frame, idx), kwargs_idx)
|
|
118
134
|
except ValueError:
|
|
119
|
-
return (idx, NULL_POINTER) # type: ignore
|
|
135
|
+
return (idx, NULL_POINTER, kwargs_idx) # type: ignore
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def handle_call_function_ex_3_14(frame) -> CallStackInfo:
|
|
139
|
+
callable_idx, kwargs_idx = -4, -1
|
|
140
|
+
try:
|
|
141
|
+
return (callable_idx, frame_stack_read(frame, callable_idx), kwargs_idx)
|
|
142
|
+
except ValueError:
|
|
143
|
+
return (callable_idx, NULL_POINTER, kwargs_idx) # type: ignore
|
|
120
144
|
|
|
121
145
|
|
|
122
|
-
def handle_call_method(frame) ->
|
|
146
|
+
def handle_call_method(frame) -> CallStackInfo:
|
|
123
147
|
idx = -(frame.f_code.co_code[frame.f_lasti + 1] + 2)
|
|
124
148
|
try:
|
|
125
|
-
return (idx, frame_stack_read(frame, idx))
|
|
149
|
+
return (idx, frame_stack_read(frame, idx), None)
|
|
126
150
|
except ValueError:
|
|
127
151
|
# not a sucessful method lookup; no call happens here
|
|
128
152
|
idx += 1
|
|
129
|
-
return (idx, frame_stack_read(frame, idx))
|
|
153
|
+
return (idx, frame_stack_read(frame, idx), None)
|
|
130
154
|
|
|
131
155
|
|
|
132
|
-
_CALL_HANDLERS: Dict[int, Callable[[object],
|
|
156
|
+
_CALL_HANDLERS: Dict[int, Callable[[object], CallStackInfo]] = {
|
|
133
157
|
BUILD_TUPLE_UNPACK_WITH_CALL: handle_build_tuple_unpack_with_call,
|
|
134
158
|
CALL: handle_call_3_13 if sys.version_info >= (3, 13) else handle_call_3_11,
|
|
135
159
|
CALL_KW: handle_call_kw,
|
|
136
160
|
CALL_FUNCTION: handle_call_function,
|
|
137
161
|
CALL_FUNCTION_KW: handle_call_function_kw,
|
|
138
|
-
CALL_FUNCTION_EX:
|
|
139
|
-
|
|
140
|
-
|
|
162
|
+
CALL_FUNCTION_EX: (
|
|
163
|
+
handle_call_function_ex_3_14
|
|
164
|
+
if sys.version_info >= (3, 14)
|
|
165
|
+
else (
|
|
166
|
+
handle_call_function_ex_3_13
|
|
167
|
+
if sys.version_info >= (3, 13)
|
|
168
|
+
else handle_call_function_ex_3_6
|
|
169
|
+
)
|
|
170
|
+
),
|
|
141
171
|
CALL_METHOD: handle_call_method,
|
|
142
172
|
}
|
|
143
173
|
|
|
@@ -152,6 +182,35 @@ class TraceException(BaseException):
|
|
|
152
182
|
pass
|
|
153
183
|
|
|
154
184
|
|
|
185
|
+
def check_opcode_support(opcodes: FrozenSet[int]):
|
|
186
|
+
if sys.version_info < (3, 12):
|
|
187
|
+
return
|
|
188
|
+
missing_opcodes = opcodes - set(supported_opcodes())
|
|
189
|
+
if missing_opcodes:
|
|
190
|
+
raise TraceException(
|
|
191
|
+
f"The C-level tracer does not support these opcodes: {','.join(map(dis.opname.__getitem__, missing_opcodes))}"
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
check_opcode_support(frozenset(_CALL_HANDLERS.keys()))
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
wrapper_descriptor_type = type(int.__bool__)
|
|
199
|
+
assert str(wrapper_descriptor_type) == "<class 'wrapper_descriptor'>"
|
|
200
|
+
|
|
201
|
+
_NORMAL_CALLABLE_TYPES = (
|
|
202
|
+
type,
|
|
203
|
+
types.FunctionType, #': <class 'function'>,
|
|
204
|
+
types.MethodDescriptorType, #': <class 'method_descriptor'>,
|
|
205
|
+
types.MethodType, #': <class 'method'>,
|
|
206
|
+
types.MethodWrapperType, #': <class 'method-wrapper'>}
|
|
207
|
+
types.BuiltinFunctionType, #': <class 'builtin_function_or_method'>,
|
|
208
|
+
types.BuiltinMethodType, #: <class 'builtin_function_or_method'>,
|
|
209
|
+
types.ClassMethodDescriptorType, #': <class 'classmethod_descriptor'>,
|
|
210
|
+
wrapper_descriptor_type,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
155
214
|
class TracingModule:
|
|
156
215
|
# override these!:
|
|
157
216
|
opcodes_wanted = frozenset(_CALL_HANDLERS.keys())
|
|
@@ -165,18 +224,21 @@ class TracingModule:
|
|
|
165
224
|
call_handler = _CALL_HANDLERS.get(opcodenum)
|
|
166
225
|
if not call_handler:
|
|
167
226
|
return None
|
|
168
|
-
|
|
169
|
-
if maybe_call_info is None:
|
|
170
|
-
# TODO: this cannot happen?
|
|
171
|
-
return None
|
|
172
|
-
(fn_idx, target) = maybe_call_info
|
|
227
|
+
(fn_idx, target, kwargs_idx) = call_handler(frame)
|
|
173
228
|
binding_target = None
|
|
174
229
|
|
|
230
|
+
__self = None
|
|
175
231
|
try:
|
|
176
232
|
__self = object.__getattribute__(target, "__self__")
|
|
177
233
|
except AttributeError:
|
|
178
234
|
pass
|
|
179
|
-
|
|
235
|
+
if (__self is None) and (not isinstance(target, _NORMAL_CALLABLE_TYPES)):
|
|
236
|
+
try:
|
|
237
|
+
target = object.__getattribute__(target, "__call__")
|
|
238
|
+
__self = object.__getattribute__(target, "__self__")
|
|
239
|
+
except AttributeError:
|
|
240
|
+
pass
|
|
241
|
+
if __self is not None:
|
|
180
242
|
try:
|
|
181
243
|
__func = object.__getattribute__(target, "__func__")
|
|
182
244
|
except AttributeError:
|
|
@@ -190,6 +252,27 @@ class TracingModule:
|
|
|
190
252
|
binding_target = __self
|
|
191
253
|
target = __func
|
|
192
254
|
|
|
255
|
+
if kwargs_idx is not None:
|
|
256
|
+
try:
|
|
257
|
+
kwargs_dict = frame_stack_read(frame, kwargs_idx)
|
|
258
|
+
except ValueError:
|
|
259
|
+
pass
|
|
260
|
+
else:
|
|
261
|
+
replacement_kwargs = {}
|
|
262
|
+
for key, val in kwargs_dict.items():
|
|
263
|
+
if isinstance(key, str):
|
|
264
|
+
replacement_kwargs[key] = val
|
|
265
|
+
continue
|
|
266
|
+
# circular import:
|
|
267
|
+
from crosshair.libimpl.builtinslib import AnySymbolicStr
|
|
268
|
+
|
|
269
|
+
if isinstance(key, AnySymbolicStr):
|
|
270
|
+
# NOTE: We need to ensure symbolic strings don't need tracing for realization
|
|
271
|
+
replacement_kwargs[key.__ch_realize__()] = val
|
|
272
|
+
else:
|
|
273
|
+
raise TypeError("keywords must be strings")
|
|
274
|
+
frame_stack_write(frame, kwargs_idx, replacement_kwargs)
|
|
275
|
+
|
|
193
276
|
if isinstance(target, Untracable):
|
|
194
277
|
return None
|
|
195
278
|
replacement = self.trace_call(frame, target, binding_target)
|
|
@@ -222,7 +305,6 @@ class PatchingModule(TracingModule):
|
|
|
222
305
|
def __init__(
|
|
223
306
|
self,
|
|
224
307
|
overrides: Optional[Dict[Callable, Callable]] = None,
|
|
225
|
-
fn_type_overrides: Optional[Dict[type, Callable]] = None,
|
|
226
308
|
):
|
|
227
309
|
# NOTE: you might imagine that we should use an IdKeyedDict for self.overrides
|
|
228
310
|
# However, some builtin bound methods have no way to get identity for their code:
|
|
@@ -234,11 +316,13 @@ class PatchingModule(TracingModule):
|
|
|
234
316
|
self.nextfn: Dict[object, Callable] = {} # code object to next, lower layer
|
|
235
317
|
if overrides:
|
|
236
318
|
self.add(overrides)
|
|
237
|
-
self.fn_type_overrides = fn_type_overrides or {}
|
|
238
319
|
|
|
239
320
|
def add(self, new_overrides: Dict[Callable, Callable]):
|
|
240
321
|
for orig, new_override in new_overrides.items():
|
|
241
322
|
prev_override = self.overrides.get(orig, orig)
|
|
323
|
+
assert (
|
|
324
|
+
prev_override is not new_override
|
|
325
|
+
), f"Function patch {new_override} has already been applied"
|
|
242
326
|
self.nextfn[(new_override.__code__, orig)] = prev_override
|
|
243
327
|
self.overrides[orig] = new_override
|
|
244
328
|
|
|
@@ -265,11 +349,7 @@ class PatchingModule(TracingModule):
|
|
|
265
349
|
# not properly unbound by `TracingModule.trace_op`.
|
|
266
350
|
return None
|
|
267
351
|
if target is None:
|
|
268
|
-
|
|
269
|
-
if fn_type_override is None:
|
|
270
|
-
return None
|
|
271
|
-
else:
|
|
272
|
-
target = fn_type_override(fn)
|
|
352
|
+
return None
|
|
273
353
|
caller_code = frame.f_code
|
|
274
354
|
if caller_code.co_name == "_crosshair_wrapper":
|
|
275
355
|
return None
|
|
@@ -306,7 +386,7 @@ class CompositeTracer:
|
|
|
306
386
|
|
|
307
387
|
def __enter__(self) -> object:
|
|
308
388
|
self.ctracer.push_module(self.patching_module)
|
|
309
|
-
tool_id =
|
|
389
|
+
tool_id = SYS_MONITORING_TOOL_ID
|
|
310
390
|
sys.monitoring.use_tool_id(tool_id, "CrossHair")
|
|
311
391
|
sys.monitoring.register_callback(
|
|
312
392
|
tool_id,
|
|
@@ -321,7 +401,7 @@ class CompositeTracer:
|
|
|
321
401
|
return self
|
|
322
402
|
|
|
323
403
|
def __exit__(self, _etype, exc, _etb):
|
|
324
|
-
tool_id =
|
|
404
|
+
tool_id = SYS_MONITORING_TOOL_ID
|
|
325
405
|
sys.monitoring.register_callback(
|
|
326
406
|
tool_id, sys.monitoring.events.INSTRUCTION, None
|
|
327
407
|
)
|
|
@@ -380,6 +460,9 @@ class CoverageResult:
|
|
|
380
460
|
class CoverageTracingModule(TracingModule):
|
|
381
461
|
opcodes_wanted = frozenset(i for i in range(256))
|
|
382
462
|
|
|
463
|
+
# TODO: this needs to be moved into a separate kind of monitor to
|
|
464
|
+
# support threading (sys.monitoring probes are global)
|
|
465
|
+
|
|
383
466
|
def __init__(self, *fns: Callable):
|
|
384
467
|
assert not is_tracing()
|
|
385
468
|
self.fns = fns
|
|
@@ -431,8 +514,17 @@ def NoTracing():
|
|
|
431
514
|
return TraceSwap(COMPOSITE_TRACER.ctracer, True)
|
|
432
515
|
|
|
433
516
|
|
|
434
|
-
|
|
435
|
-
|
|
517
|
+
if CROSSHAIR_EXTRA_ASSERTS:
|
|
518
|
+
|
|
519
|
+
def ResumedTracing():
|
|
520
|
+
if COMPOSITE_TRACER.ctracer.is_handling():
|
|
521
|
+
raise TraceException("Cannot resume tracing while opcode handling")
|
|
522
|
+
return TraceSwap(COMPOSITE_TRACER.ctracer, False)
|
|
523
|
+
|
|
524
|
+
else:
|
|
525
|
+
|
|
526
|
+
def ResumedTracing():
|
|
527
|
+
return TraceSwap(COMPOSITE_TRACER.ctracer, False)
|
|
436
528
|
|
|
437
529
|
|
|
438
530
|
_T = TypeVar("_T")
|
crosshair/type_repo.py
CHANGED
|
@@ -5,6 +5,8 @@ from typing import Dict, List, Optional, Type
|
|
|
5
5
|
|
|
6
6
|
import z3 # type: ignore
|
|
7
7
|
|
|
8
|
+
from crosshair.tracers import NoTracing
|
|
9
|
+
from crosshair.util import CrossHairInternal, CrossHairValue, is_hashable
|
|
8
10
|
from crosshair.z3util import z3Eq, z3Not
|
|
9
11
|
|
|
10
12
|
_MAP: Optional[Dict[type, List[type]]] = None
|
|
@@ -18,6 +20,8 @@ _IGNORED_MODULE_ROOTS = {
|
|
|
18
20
|
"pkg_resources",
|
|
19
21
|
"pytest",
|
|
20
22
|
"py", # (part of pytest)
|
|
23
|
+
# Disabled because the import attempt can cause problems:
|
|
24
|
+
"numpy", # importing numpy/testing/_private/utils.py attempts a subprocess call
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
|
|
@@ -48,7 +52,7 @@ def get_subclass_map() -> Dict[type, List[type]]:
|
|
|
48
52
|
Crawl all types presently in memory and makes a map from parent to child classes.
|
|
49
53
|
|
|
50
54
|
Only direct children are included.
|
|
51
|
-
Does not yet handle "protocol" subclassing (eg "Iterator", "Mapping", etc).
|
|
55
|
+
TODO: Does not yet handle "protocol" subclassing (eg "Iterator", "Mapping", etc).
|
|
52
56
|
"""
|
|
53
57
|
global _MAP
|
|
54
58
|
if _MAP is None:
|
|
@@ -57,15 +61,13 @@ def get_subclass_map() -> Dict[type, List[type]]:
|
|
|
57
61
|
if module_name.split(".", 1)[0] in _IGNORED_MODULE_ROOTS:
|
|
58
62
|
continue
|
|
59
63
|
if module is None:
|
|
60
|
-
# We set the internal _datetime module to None, ensuring that
|
|
61
|
-
# we don't load the C implementation.
|
|
62
64
|
continue
|
|
63
65
|
try:
|
|
64
66
|
module_classes = inspect.getmembers(module, inspect.isclass)
|
|
65
67
|
except ImportError:
|
|
66
68
|
continue
|
|
67
69
|
for _, cls in module_classes:
|
|
68
|
-
if _class_known_to_be_copyable(cls):
|
|
70
|
+
if _class_known_to_be_copyable(cls) and is_hashable(cls):
|
|
69
71
|
classes.add(cls)
|
|
70
72
|
subclass = collections.defaultdict(list)
|
|
71
73
|
for cls in classes:
|
|
@@ -120,6 +122,11 @@ class SymbolicTypeRepository:
|
|
|
120
122
|
return SMT_SUBTYPE_FN(typ1, typ2) == MAYBE_SORT.yes
|
|
121
123
|
|
|
122
124
|
def get_type(self, typ: Type) -> z3.ExprRef:
|
|
125
|
+
with NoTracing():
|
|
126
|
+
if issubclass(typ, CrossHairValue):
|
|
127
|
+
raise CrossHairInternal(
|
|
128
|
+
"Type repo should not be queried with a symbolic"
|
|
129
|
+
)
|
|
123
130
|
pytype_to_smt = self.pytype_to_smt
|
|
124
131
|
if typ not in pytype_to_smt:
|
|
125
132
|
stmts = []
|