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/relib_test.py
CHANGED
|
@@ -4,14 +4,15 @@ from typing import Optional
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from crosshair
|
|
7
|
+
from crosshair import ResumedTracing
|
|
8
|
+
from crosshair.core import deep_realize, proxy_for_type
|
|
8
9
|
from crosshair.core_and_libs import NoTracing, standalone_statespace
|
|
9
|
-
from crosshair.libimpl.builtinslib import LazyIntSymbolicStr
|
|
10
|
-
from crosshair.libimpl.relib import
|
|
10
|
+
from crosshair.libimpl.builtinslib import LazyIntSymbolicStr, SymbolicBytes
|
|
11
|
+
from crosshair.libimpl.relib import _BACKREF_STR_RE, _match_pattern
|
|
11
12
|
from crosshair.options import AnalysisOptionSet
|
|
12
13
|
from crosshair.statespace import CANNOT_CONFIRM, CONFIRMED, POST_FAIL, MessageType
|
|
13
14
|
from crosshair.test_util import check_states
|
|
14
|
-
from crosshair.util import
|
|
15
|
+
from crosshair.util import CrossHairInternal
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def eval_regex(re_string, flags, test_string, offset, endpos=None):
|
|
@@ -374,17 +375,17 @@ def test_lookbehind() -> None:
|
|
|
374
375
|
|
|
375
376
|
|
|
376
377
|
def test_backref_re():
|
|
377
|
-
assert
|
|
378
|
-
assert
|
|
379
|
-
assert
|
|
380
|
-
assert
|
|
381
|
-
assert
|
|
382
|
-
assert
|
|
383
|
-
assert
|
|
378
|
+
assert _BACKREF_STR_RE.fullmatch(r"\1").group("num") == "1"
|
|
379
|
+
assert _BACKREF_STR_RE.fullmatch(r"ab\1cd").group("num") == "1"
|
|
380
|
+
assert _BACKREF_STR_RE.fullmatch(r"$%^ \g<_cat> &*").group("named") == "_cat"
|
|
381
|
+
assert _BACKREF_STR_RE.fullmatch(r"\g< cat>").group("namedother") == " cat"
|
|
382
|
+
assert _BACKREF_STR_RE.fullmatch(r"\g<0>").group("namednum") == "0"
|
|
383
|
+
assert _BACKREF_STR_RE.fullmatch(r"\g<+100>").group("namednum") == "+100"
|
|
384
|
+
assert _BACKREF_STR_RE.fullmatch(r"\1 foo \2").group("num") == "1"
|
|
384
385
|
|
|
385
386
|
# "\g<0>" is OK; "\0" is not:
|
|
386
|
-
assert
|
|
387
|
-
assert not
|
|
387
|
+
assert _BACKREF_STR_RE.fullmatch(r"\g<0>")
|
|
388
|
+
assert not _BACKREF_STR_RE.fullmatch(r"\0")
|
|
388
389
|
|
|
389
390
|
|
|
390
391
|
def test_template_expansion():
|
|
@@ -422,9 +423,71 @@ def test_charmatch_literal_does_not_fork():
|
|
|
422
423
|
s = LazyIntSymbolicStr(list(map(ord, "abaa")))
|
|
423
424
|
|
|
424
425
|
def explode(*a, **kw):
|
|
425
|
-
raise
|
|
426
|
+
raise CrossHairInternal
|
|
426
427
|
|
|
427
428
|
space.smt_fork = explode
|
|
428
429
|
match = letters.match(s)
|
|
429
430
|
assert match
|
|
430
431
|
assert match.group(0) == "a"
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def test_symbolic_offset():
|
|
435
|
+
_all_zeros = re.compile("0*$")
|
|
436
|
+
with standalone_statespace as space:
|
|
437
|
+
with NoTracing():
|
|
438
|
+
string = LazyIntSymbolicStr(list(map(ord, "21000")))
|
|
439
|
+
offset = proxy_for_type(int, "offset")
|
|
440
|
+
endpos = proxy_for_type(int, "endpos")
|
|
441
|
+
space.add(offset == 2)
|
|
442
|
+
space.add(endpos == 5)
|
|
443
|
+
assert _all_zeros.match(string, offset)
|
|
444
|
+
assert not _all_zeros.match(string, offset - 1)
|
|
445
|
+
assert not _all_zeros.match(string + "1", offset)
|
|
446
|
+
assert _all_zeros.match(string + "1", offset, endpos)
|
|
447
|
+
assert not _all_zeros.match(string + "1", offset, endpos + 1)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
@pytest.mark.parametrize(
|
|
451
|
+
"patt_char,match_char",
|
|
452
|
+
[
|
|
453
|
+
("ß", "ẞ"),
|
|
454
|
+
("ẞ", "ß"),
|
|
455
|
+
("İ", "i"),
|
|
456
|
+
("i", "İ"),
|
|
457
|
+
("Ⓐ", "ⓐ"),
|
|
458
|
+
("ⓐ", "Ⓐ"),
|
|
459
|
+
],
|
|
460
|
+
)
|
|
461
|
+
def test_ignorecase_matches(space, patt_char, match_char):
|
|
462
|
+
pattern = re.compile(patt_char, re.IGNORECASE)
|
|
463
|
+
# sanity check that regular python does what we expect:
|
|
464
|
+
assert pattern.fullmatch(match_char)
|
|
465
|
+
symbolic_match_char = LazyIntSymbolicStr(list(map(ord, match_char)))
|
|
466
|
+
with ResumedTracing():
|
|
467
|
+
assert pattern.fullmatch(symbolic_match_char)
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
@pytest.mark.parametrize(
|
|
471
|
+
"patt_char,match_char",
|
|
472
|
+
[
|
|
473
|
+
("a", "ⓐ"),
|
|
474
|
+
("ß".upper(), "ß"),
|
|
475
|
+
("ß", "ß".upper()),
|
|
476
|
+
("İ".lower(), "İ"),
|
|
477
|
+
("İ", "İ".lower()),
|
|
478
|
+
],
|
|
479
|
+
)
|
|
480
|
+
def test_ignorecase_nonmatches(space, patt_char, match_char):
|
|
481
|
+
pattern = re.compile(patt_char, re.IGNORECASE)
|
|
482
|
+
# sanity check that regular python does what we expect:
|
|
483
|
+
assert not pattern.fullmatch(match_char)
|
|
484
|
+
symbolic_match_char = LazyIntSymbolicStr(list(map(ord, match_char)))
|
|
485
|
+
with ResumedTracing():
|
|
486
|
+
assert not pattern.fullmatch(symbolic_match_char)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def test_bytes_based_pattern(space):
|
|
490
|
+
string = SymbolicBytes(b"abbc")
|
|
491
|
+
with ResumedTracing():
|
|
492
|
+
assert re.fullmatch(b"ab+c", string)
|
|
493
|
+
assert [m.span() for m in re.finditer(b"b", string)] == [(1, 2), (2, 3)]
|
crosshair/libimpl/timelib.py
CHANGED
|
@@ -1,26 +1,45 @@
|
|
|
1
1
|
import time as real_time
|
|
2
2
|
from inspect import Signature
|
|
3
|
-
from
|
|
3
|
+
from math import isfinite
|
|
4
|
+
from typing import Any, Literal
|
|
4
5
|
|
|
5
|
-
from crosshair.core import
|
|
6
|
+
from crosshair.core import register_patch
|
|
6
7
|
from crosshair.register_contract import register_contract
|
|
7
8
|
from crosshair.statespace import context_statespace
|
|
8
9
|
from crosshair.tracers import NoTracing
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
class EarliestPossibleTime:
|
|
13
|
+
monotonic: float = 0.0
|
|
14
|
+
process_time: float = 0.0
|
|
15
|
+
|
|
16
|
+
def __init__(self, *a):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Imprecision at high values becomes a sort of artificial problem
|
|
21
|
+
_UNREALISTICALLY_LARGE_TIME_FLOAT = float(60 * 60 * 24 * 365 * 100_000)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _gte_last(kind: Literal["monotonic", "process_time"], value: Any) -> bool:
|
|
25
|
+
with NoTracing():
|
|
26
|
+
earliest_times = context_statespace().extra(EarliestPossibleTime)
|
|
27
|
+
threshold = getattr(earliest_times, kind)
|
|
28
|
+
setattr(earliest_times, kind, value)
|
|
29
|
+
return all([threshold <= value, value < _UNREALISTICALLY_LARGE_TIME_FLOAT])
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _sleep(value: float) -> None:
|
|
12
33
|
with NoTracing():
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return True
|
|
17
|
-
return value >= previous[-2]
|
|
34
|
+
earliest_times = context_statespace().extra(EarliestPossibleTime)
|
|
35
|
+
earliest_times.monotonic += value
|
|
36
|
+
return None
|
|
18
37
|
|
|
19
38
|
|
|
20
39
|
def make_registrations():
|
|
21
40
|
register_contract(
|
|
22
41
|
real_time.time,
|
|
23
|
-
post=lambda __return__: __return__ > 0.0,
|
|
42
|
+
post=lambda __return__: __return__ > 0.0 and isfinite(__return__),
|
|
24
43
|
sig=Signature(parameters=[], return_annotation=float),
|
|
25
44
|
)
|
|
26
45
|
register_contract(
|
|
@@ -30,21 +49,24 @@ def make_registrations():
|
|
|
30
49
|
)
|
|
31
50
|
register_contract(
|
|
32
51
|
real_time.monotonic,
|
|
33
|
-
post=lambda __return__: _gte_last(
|
|
52
|
+
post=lambda __return__: _gte_last("monotonic", __return__)
|
|
53
|
+
and isfinite(__return__),
|
|
34
54
|
sig=Signature(parameters=[], return_annotation=float),
|
|
35
55
|
)
|
|
36
56
|
register_contract(
|
|
37
57
|
real_time.monotonic_ns,
|
|
38
|
-
post=lambda __return__: _gte_last(
|
|
58
|
+
post=lambda __return__: _gte_last("monotonic", __return__ / 1_000_000_000),
|
|
39
59
|
sig=Signature(parameters=[], return_annotation=int),
|
|
40
60
|
)
|
|
41
61
|
register_contract(
|
|
42
62
|
real_time.process_time,
|
|
43
|
-
post=lambda __return__: _gte_last(
|
|
63
|
+
post=lambda __return__: _gte_last("process_time", __return__)
|
|
64
|
+
and isfinite(__return__),
|
|
44
65
|
sig=Signature(parameters=[], return_annotation=float),
|
|
45
66
|
)
|
|
46
67
|
register_contract(
|
|
47
68
|
real_time.process_time_ns,
|
|
48
|
-
post=lambda __return__: _gte_last(
|
|
69
|
+
post=lambda __return__: _gte_last("process_time", __return__ / 1_000_000_000),
|
|
49
70
|
sig=Signature(parameters=[], return_annotation=int),
|
|
50
71
|
)
|
|
72
|
+
register_patch(real_time.sleep, _sleep)
|
|
@@ -2,7 +2,7 @@ import time
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
-
from crosshair.statespace import CANNOT_CONFIRM, CONFIRMED, POST_FAIL
|
|
5
|
+
from crosshair.statespace import CANNOT_CONFIRM, CONFIRMED, POST_FAIL
|
|
6
6
|
from crosshair.test_util import check_states
|
|
7
7
|
|
|
8
8
|
|
|
@@ -60,7 +60,7 @@ def test_monotonic_confirm():
|
|
|
60
60
|
start = time.monotonic()
|
|
61
61
|
return time.monotonic() - start
|
|
62
62
|
|
|
63
|
-
check_states(f,
|
|
63
|
+
check_states(f, CONFIRMED)
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
def test_monotonic_ns():
|
|
@@ -69,4 +69,14 @@ def test_monotonic_ns():
|
|
|
69
69
|
start = time.monotonic_ns()
|
|
70
70
|
return time.monotonic_ns() - start
|
|
71
71
|
|
|
72
|
-
check_states(f,
|
|
72
|
+
check_states(f, CANNOT_CONFIRM)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_sleep():
|
|
76
|
+
def f():
|
|
77
|
+
"""post: _ >= 60.0"""
|
|
78
|
+
start = time.monotonic()
|
|
79
|
+
time.sleep(60.01)
|
|
80
|
+
return time.monotonic() - start
|
|
81
|
+
|
|
82
|
+
check_states(f, CANNOT_CONFIRM)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from types import MappingProxyType
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
|
|
4
|
+
from crosshair.core import register_patch, register_type
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _repr(map: MappingProxyType):
|
|
8
|
+
if not isinstance(map, MappingProxyType):
|
|
9
|
+
raise TypeError
|
|
10
|
+
return f"mappingproxy({repr(dict(map))})"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def make_registrations():
|
|
14
|
+
register_type(MappingProxyType, lambda p, kt=Any, vt=Any: MappingProxyType(p(Dict[kt, vt]))) # type: ignore
|
|
15
|
+
register_patch(MappingProxyType.__repr__, _repr)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from types import MappingProxyType
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from crosshair import ResumedTracing
|
|
8
|
+
from crosshair.core import deep_realize, proxy_for_type
|
|
9
|
+
from crosshair.libimpl.builtinslib import SymbolicInt
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.skipif(
|
|
13
|
+
sys.version_info < (3, 9), reason="MappingProxyType not subscriptable in 3.8"
|
|
14
|
+
)
|
|
15
|
+
def test_mappingproxy_repr(space):
|
|
16
|
+
d = proxy_for_type(MappingProxyType[int, int], "d")
|
|
17
|
+
with ResumedTracing():
|
|
18
|
+
assert repr(d).startswith("mappingproxy(")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_mappingproxy_deep_realize(space):
|
|
22
|
+
inner = proxy_for_type(Dict[int, int], "inner")
|
|
23
|
+
with ResumedTracing():
|
|
24
|
+
space.add(inner._inner.__len__() == 1)
|
|
25
|
+
key = next(iter(inner.keys()))
|
|
26
|
+
assert type(key) is SymbolicInt
|
|
27
|
+
orig = MappingProxyType(inner)
|
|
28
|
+
assert type(orig) is MappingProxyType
|
|
29
|
+
copy = deep_realize(orig)
|
|
30
|
+
assert type(copy) is MappingProxyType
|
|
31
|
+
with ResumedTracing():
|
|
32
|
+
val_from_orig = orig[key]
|
|
33
|
+
realized_key = deep_realize(key)
|
|
34
|
+
val_from_copy = copy[realized_key]
|
|
35
|
+
assert type(val_from_orig) is SymbolicInt
|
|
36
|
+
assert type(val_from_copy) is int
|
|
@@ -11,7 +11,7 @@ def test_numeric():
|
|
|
11
11
|
with standalone_statespace as space:
|
|
12
12
|
with NoTracing():
|
|
13
13
|
fourstr = LazyIntSymbolicStr(list(map(ord, "4")))
|
|
14
|
-
halfstr = LazyIntSymbolicStr(list(map(ord, "\
|
|
14
|
+
halfstr = LazyIntSymbolicStr(list(map(ord, "\u00bd"))) # (1/2 character)
|
|
15
15
|
four = unicodedata.numeric(fourstr)
|
|
16
16
|
half = unicodedata.numeric(halfstr)
|
|
17
17
|
assert type(four) is float
|
|
@@ -22,7 +22,7 @@ def test_numeric():
|
|
|
22
22
|
def test_decimal():
|
|
23
23
|
with standalone_statespace as space:
|
|
24
24
|
with NoTracing():
|
|
25
|
-
thai4 = LazyIntSymbolicStr(list(map(ord, "\
|
|
25
|
+
thai4 = LazyIntSymbolicStr(list(map(ord, "\u0e54"))) # (Thai numerial 4)
|
|
26
26
|
super4 = LazyIntSymbolicStr(list(map(ord, "\u2074"))) # (superscript 4)
|
|
27
27
|
four = unicodedata.decimal(thai4)
|
|
28
28
|
assert type(four) is int
|
|
@@ -34,7 +34,7 @@ def test_decimal():
|
|
|
34
34
|
def test_digit():
|
|
35
35
|
with standalone_statespace as space:
|
|
36
36
|
with NoTracing():
|
|
37
|
-
thai4 = LazyIntSymbolicStr(list(map(ord, "\
|
|
37
|
+
thai4 = LazyIntSymbolicStr(list(map(ord, "\u0e54"))) # (Thai numerial 4)
|
|
38
38
|
super4 = LazyIntSymbolicStr(list(map(ord, "\u2074"))) # (superscript 4)
|
|
39
39
|
four = unicodedata.digit(thai4)
|
|
40
40
|
assert type(four) is int
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from weakref import WeakKeyDictionary, WeakSet, WeakValueDictionary, proxy, ref
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from crosshair.tracers import ResumedTracing
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Thing:
|
|
9
|
+
x: int = 0
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_weakref(space):
|
|
13
|
+
thing1 = Thing()
|
|
14
|
+
assert ref(thing1)() is thing1
|
|
15
|
+
with ResumedTracing():
|
|
16
|
+
assert ref(thing1)() is None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_weakref_WeakKeyDictionary(space):
|
|
20
|
+
d = WeakKeyDictionary()
|
|
21
|
+
thing1 = Thing()
|
|
22
|
+
thing2 = Thing()
|
|
23
|
+
d[thing1] = 1
|
|
24
|
+
d[thing2] = 2
|
|
25
|
+
assert len(d) == 2
|
|
26
|
+
assert thing1 in d
|
|
27
|
+
assert set(d.keys()) == {thing1, thing2}
|
|
28
|
+
with ResumedTracing():
|
|
29
|
+
assert set(d.keys()) == set()
|
|
30
|
+
# You would expect the following assertions to work too
|
|
31
|
+
# However, they don't require getting the referred object, so they appear to still exist:
|
|
32
|
+
# assert thing1 not in d
|
|
33
|
+
# assert len(d) == 0
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_weakref_WeakValueDictionary(space):
|
|
37
|
+
d = WeakValueDictionary()
|
|
38
|
+
thing1 = Thing()
|
|
39
|
+
thing2 = Thing()
|
|
40
|
+
d[1] = thing1
|
|
41
|
+
d[2] = thing2
|
|
42
|
+
assert len(d) == 2
|
|
43
|
+
assert 1 in d
|
|
44
|
+
assert set(d.keys()) == {1, 2}
|
|
45
|
+
with ResumedTracing():
|
|
46
|
+
assert set(d.keys()) == set()
|
|
47
|
+
assert 1 not in d
|
|
48
|
+
# You would expect the length to update too.
|
|
49
|
+
# However, it doesn't require getting the referred object, so it appears to still exist:
|
|
50
|
+
# assert len(d) == 0
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.mark.xfail(reason="weakref.WeakSet is not yet supported")
|
|
54
|
+
def test_weakref_WeakSet(space):
|
|
55
|
+
s = WeakSet()
|
|
56
|
+
thing1 = Thing()
|
|
57
|
+
s.add(thing1)
|
|
58
|
+
assert thing1 in s
|
|
59
|
+
with ResumedTracing():
|
|
60
|
+
assert thing1 not in s
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@pytest.mark.xfail(reason="weakref.proxy is not yet supported")
|
|
64
|
+
def test_weakref_proxy(space):
|
|
65
|
+
thing1 = Thing()
|
|
66
|
+
thing1.x
|
|
67
|
+
p = proxy(thing1)
|
|
68
|
+
with pytest.raises(ReferenceError), ResumedTracing():
|
|
69
|
+
p.x
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import zlib
|
|
2
|
+
|
|
3
|
+
from crosshair.core import register_patch, with_realized_args
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def make_registrations():
|
|
7
|
+
for fn in (
|
|
8
|
+
zlib.adler32,
|
|
9
|
+
zlib.compress,
|
|
10
|
+
zlib.crc32,
|
|
11
|
+
zlib.decompress,
|
|
12
|
+
type(zlib.compressobj()).compress,
|
|
13
|
+
type(zlib.decompressobj()).decompress,
|
|
14
|
+
):
|
|
15
|
+
register_patch(fn, with_realized_args(fn))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import zlib
|
|
2
|
+
|
|
3
|
+
from crosshair.core import proxy_for_type, realize
|
|
4
|
+
from crosshair.tracers import ResumedTracing
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_compress_on_symbolic(space):
|
|
8
|
+
buffer = proxy_for_type(bytes, "buffer")
|
|
9
|
+
with ResumedTracing():
|
|
10
|
+
space.add(len(buffer) == 1)
|
|
11
|
+
result = zlib.compress(buffer)
|
|
12
|
+
realized_buffer = realize(buffer)
|
|
13
|
+
assert zlib.decompress(result) == realized_buffer
|
crosshair/lsp_server.py
CHANGED
|
@@ -25,7 +25,11 @@ from lsprotocol.types import (
|
|
|
25
25
|
Position,
|
|
26
26
|
Range,
|
|
27
27
|
)
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
from pygls.lsp.server import LanguageServer # (v2.x)
|
|
31
|
+
except ImportError:
|
|
32
|
+
from pygls.server import LanguageServer # (v1.x)
|
|
29
33
|
|
|
30
34
|
from crosshair import __version__, env_info
|
|
31
35
|
from crosshair.options import DEFAULT_OPTIONS, AnalysisOptionSet
|
|
@@ -42,6 +46,13 @@ class CrossHairLanguageServer(LanguageServer):
|
|
|
42
46
|
self.options = options
|
|
43
47
|
super().__init__("CrossHairServer", __version__)
|
|
44
48
|
|
|
49
|
+
def window_log_message(self, message: str):
|
|
50
|
+
# Just for version 1 & 2 compatibility
|
|
51
|
+
if hasattr(self, "show_message_log"):
|
|
52
|
+
self.show_message_log(message)
|
|
53
|
+
else:
|
|
54
|
+
super().window_log_message(message)
|
|
55
|
+
|
|
45
56
|
CMD_REGISTER_COMPLETIONS = "registerCompletions"
|
|
46
57
|
CMD_UNREGISTER_COMPLETIONS = "unregisterCompletions"
|
|
47
58
|
|
|
@@ -85,8 +96,8 @@ def publish_messages(
|
|
|
85
96
|
for message in file_messages.values():
|
|
86
97
|
if message.state < MessageType.PRE_UNSAT:
|
|
87
98
|
continue
|
|
88
|
-
# TODO: consider server.
|
|
89
|
-
diagnostics.append(get_diagnostic(message, doc.lines if doc else
|
|
99
|
+
# TODO: consider server.window_log_message()ing the long description
|
|
100
|
+
diagnostics.append(get_diagnostic(message, doc.lines if doc else []))
|
|
90
101
|
server.publish_diagnostics(uri, diagnostics)
|
|
91
102
|
if not diagnostics:
|
|
92
103
|
# After we publish an empty set, it's safe to forget about the file:
|
|
@@ -116,7 +127,7 @@ class LocalState:
|
|
|
116
127
|
max_watch_iterations: int = sys.maxsize,
|
|
117
128
|
) -> None:
|
|
118
129
|
def log(*a):
|
|
119
|
-
pass # self.server.
|
|
130
|
+
pass # self.server.window_log_message(*a)
|
|
120
131
|
|
|
121
132
|
log("loop thread started")
|
|
122
133
|
watcher = self.watcher
|
|
@@ -129,7 +140,7 @@ class LocalState:
|
|
|
129
140
|
return
|
|
130
141
|
if restart:
|
|
131
142
|
numfiles = len(watcher._modtimes)
|
|
132
|
-
server.
|
|
143
|
+
server.window_log_message(
|
|
133
144
|
f"Scanning {numfiles} file(s) for properties to check."
|
|
134
145
|
)
|
|
135
146
|
max_uninteresting_iterations = (
|
|
@@ -163,7 +174,7 @@ class LocalState:
|
|
|
163
174
|
return
|
|
164
175
|
if numpaths > 0:
|
|
165
176
|
status = f"Analyzed {numpaths} paths in {len(watcher._modtimes)} files."
|
|
166
|
-
server.
|
|
177
|
+
server.window_log_message(status)
|
|
167
178
|
publish_messages(active_messages, server)
|
|
168
179
|
if watcher._change_flag:
|
|
169
180
|
watcher._change_flag = False
|
|
@@ -177,7 +188,7 @@ _LS: Optional[LocalState] = None
|
|
|
177
188
|
def getlocalstate(server: CrossHairLanguageServer) -> LocalState:
|
|
178
189
|
global _LS
|
|
179
190
|
if _LS is None:
|
|
180
|
-
server.
|
|
191
|
+
server.window_log_message(env_info())
|
|
181
192
|
watcher = Watcher([], server.options)
|
|
182
193
|
watcher.startpool()
|
|
183
194
|
_LS = LocalState(watcher, server)
|
|
@@ -202,7 +213,7 @@ def update_paths(server: CrossHairLanguageServer):
|
|
|
202
213
|
path = path.replace("/", "\\")
|
|
203
214
|
paths.append(pathlib.Path(path))
|
|
204
215
|
watcher = getlocalstate(server).watcher
|
|
205
|
-
server.
|
|
216
|
+
server.window_log_message("New path set: " + repr(paths))
|
|
206
217
|
watcher.update_paths(paths)
|
|
207
218
|
|
|
208
219
|
|
|
@@ -214,14 +225,14 @@ def create_lsp_server(options: AnalysisOptionSet) -> CrossHairLanguageServer:
|
|
|
214
225
|
def did_change(
|
|
215
226
|
server: CrossHairLanguageServer, params: DidChangeTextDocumentParams
|
|
216
227
|
):
|
|
217
|
-
# server.
|
|
228
|
+
# server.window_log_message("did_change")
|
|
218
229
|
uri = params.text_document.uri
|
|
219
230
|
getlocalstate(server).active_messages[uri] = None
|
|
220
231
|
server.publish_diagnostics(uri, [])
|
|
221
232
|
|
|
222
233
|
@crosshair_lsp_server.feature(TEXT_DOCUMENT_DID_CLOSE)
|
|
223
234
|
def did_close(server: CrossHairLanguageServer, params: DidCloseTextDocumentParams):
|
|
224
|
-
# server.
|
|
235
|
+
# server.window_log_message("did_close")
|
|
225
236
|
uri = params.text_document.uri
|
|
226
237
|
getlocalstate(server).active_messages[uri] = None
|
|
227
238
|
server.publish_diagnostics(uri, [])
|