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.
Files changed (123) hide show
  1. _crosshair_tracers.cpython-39-darwin.so +0 -0
  2. crosshair/__init__.py +1 -1
  3. crosshair/_mark_stacks.h +51 -24
  4. crosshair/_tracers.h +9 -5
  5. crosshair/_tracers_test.py +19 -9
  6. crosshair/auditwall.py +9 -8
  7. crosshair/auditwall_test.py +31 -19
  8. crosshair/codeconfig.py +3 -2
  9. crosshair/condition_parser.py +17 -133
  10. crosshair/condition_parser_test.py +54 -96
  11. crosshair/conftest.py +1 -1
  12. crosshair/copyext.py +91 -22
  13. crosshair/copyext_test.py +33 -0
  14. crosshair/core.py +259 -203
  15. crosshair/core_and_libs.py +20 -0
  16. crosshair/core_regestered_types_test.py +82 -0
  17. crosshair/core_test.py +693 -664
  18. crosshair/diff_behavior.py +76 -21
  19. crosshair/diff_behavior_test.py +132 -23
  20. crosshair/dynamic_typing.py +128 -18
  21. crosshair/dynamic_typing_test.py +91 -4
  22. crosshair/enforce.py +1 -6
  23. crosshair/enforce_test.py +15 -23
  24. crosshair/examples/check_examples_test.py +2 -1
  25. crosshair/fnutil.py +2 -3
  26. crosshair/fnutil_test.py +0 -7
  27. crosshair/fuzz_core_test.py +70 -83
  28. crosshair/libimpl/arraylib.py +10 -7
  29. crosshair/libimpl/binascii_ch_test.py +30 -0
  30. crosshair/libimpl/binascii_test.py +67 -0
  31. crosshair/libimpl/binasciilib.py +150 -0
  32. crosshair/libimpl/bisectlib_test.py +5 -5
  33. crosshair/libimpl/builtinslib.py +1002 -682
  34. crosshair/libimpl/builtinslib_ch_test.py +108 -30
  35. crosshair/libimpl/builtinslib_test.py +431 -143
  36. crosshair/libimpl/codecslib.py +22 -2
  37. crosshair/libimpl/codecslib_test.py +41 -9
  38. crosshair/libimpl/collectionslib.py +44 -8
  39. crosshair/libimpl/collectionslib_test.py +108 -20
  40. crosshair/libimpl/copylib.py +1 -1
  41. crosshair/libimpl/copylib_test.py +18 -0
  42. crosshair/libimpl/datetimelib.py +84 -67
  43. crosshair/libimpl/datetimelib_ch_test.py +12 -7
  44. crosshair/libimpl/datetimelib_test.py +5 -6
  45. crosshair/libimpl/decimallib.py +5257 -0
  46. crosshair/libimpl/decimallib_ch_test.py +78 -0
  47. crosshair/libimpl/decimallib_test.py +76 -0
  48. crosshair/libimpl/encodings/_encutil.py +21 -11
  49. crosshair/libimpl/fractionlib.py +16 -0
  50. crosshair/libimpl/fractionlib_test.py +80 -0
  51. crosshair/libimpl/functoolslib.py +19 -7
  52. crosshair/libimpl/functoolslib_test.py +22 -6
  53. crosshair/libimpl/hashliblib.py +30 -0
  54. crosshair/libimpl/hashliblib_test.py +18 -0
  55. crosshair/libimpl/heapqlib.py +32 -5
  56. crosshair/libimpl/heapqlib_test.py +15 -12
  57. crosshair/libimpl/iolib.py +7 -4
  58. crosshair/libimpl/ipaddresslib.py +8 -0
  59. crosshair/libimpl/itertoolslib_test.py +1 -1
  60. crosshair/libimpl/mathlib.py +165 -2
  61. crosshair/libimpl/mathlib_ch_test.py +44 -0
  62. crosshair/libimpl/mathlib_test.py +59 -16
  63. crosshair/libimpl/oslib.py +7 -0
  64. crosshair/libimpl/pathliblib_test.py +10 -0
  65. crosshair/libimpl/randomlib.py +1 -0
  66. crosshair/libimpl/randomlib_test.py +6 -4
  67. crosshair/libimpl/relib.py +180 -59
  68. crosshair/libimpl/relib_ch_test.py +26 -2
  69. crosshair/libimpl/relib_test.py +77 -14
  70. crosshair/libimpl/timelib.py +35 -13
  71. crosshair/libimpl/timelib_test.py +13 -3
  72. crosshair/libimpl/typeslib.py +15 -0
  73. crosshair/libimpl/typeslib_test.py +36 -0
  74. crosshair/libimpl/unicodedatalib_test.py +3 -3
  75. crosshair/libimpl/weakreflib.py +13 -0
  76. crosshair/libimpl/weakreflib_test.py +69 -0
  77. crosshair/libimpl/zliblib.py +15 -0
  78. crosshair/libimpl/zliblib_test.py +13 -0
  79. crosshair/lsp_server.py +21 -10
  80. crosshair/main.py +48 -28
  81. crosshair/main_test.py +59 -14
  82. crosshair/objectproxy.py +39 -14
  83. crosshair/objectproxy_test.py +27 -13
  84. crosshair/opcode_intercept.py +212 -24
  85. crosshair/opcode_intercept_test.py +172 -18
  86. crosshair/options.py +0 -1
  87. crosshair/patch_equivalence_test.py +5 -21
  88. crosshair/path_cover.py +7 -5
  89. crosshair/path_search.py +6 -4
  90. crosshair/path_search_test.py +1 -2
  91. crosshair/pathing_oracle.py +53 -10
  92. crosshair/pathing_oracle_test.py +21 -0
  93. crosshair/pure_importer_test.py +5 -21
  94. crosshair/register_contract.py +16 -6
  95. crosshair/register_contract_test.py +2 -14
  96. crosshair/simplestructs.py +154 -85
  97. crosshair/simplestructs_test.py +16 -2
  98. crosshair/smtlib.py +24 -0
  99. crosshair/smtlib_test.py +14 -0
  100. crosshair/statespace.py +319 -196
  101. crosshair/statespace_test.py +45 -0
  102. crosshair/stubs_parser.py +0 -2
  103. crosshair/test_util.py +87 -25
  104. crosshair/test_util_test.py +26 -0
  105. crosshair/tools/check_init_and_setup_coincide.py +0 -3
  106. crosshair/tools/generate_demo_table.py +2 -2
  107. crosshair/tracers.py +141 -49
  108. crosshair/type_repo.py +11 -4
  109. crosshair/unicode_categories.py +1 -0
  110. crosshair/util.py +158 -76
  111. crosshair/util_test.py +13 -20
  112. crosshair/watcher.py +4 -4
  113. crosshair/z3util.py +1 -1
  114. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/METADATA +45 -36
  115. crosshair_tool-0.0.100.dist-info/RECORD +176 -0
  116. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/WHEEL +2 -1
  117. crosshair/examples/hypothesis/__init__.py +0 -2
  118. crosshair/examples/hypothesis/bugs_detected/simple_strategies.py +0 -74
  119. crosshair_tool-0.0.56.dist-info/RECORD +0 -152
  120. /crosshair/{examples/hypothesis/bugs_detected/__init__.py → py.typed} +0 -0
  121. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/entry_points.txt +0 -0
  122. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info/licenses}/LICENSE +0 -0
  123. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/top_level.txt +0 -0
@@ -4,14 +4,15 @@ from typing import Optional
4
4
 
5
5
  import pytest
6
6
 
7
- from crosshair.core import deep_realize
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 _BACKREF_RE, _match_pattern
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 CrosshairInternal, set_debug
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 _BACKREF_RE.fullmatch(r"\1").group("num") == "1"
378
- assert _BACKREF_RE.fullmatch(r"ab\1cd").group("num") == "1"
379
- assert _BACKREF_RE.fullmatch(r"$%^ \g<_cat> &*").group("named") == "_cat"
380
- assert _BACKREF_RE.fullmatch(r"\g< cat>").group("namedother") == " cat"
381
- assert _BACKREF_RE.fullmatch(r"\g<0>").group("namednum") == "0"
382
- assert _BACKREF_RE.fullmatch(r"\g<+100>").group("namednum") == "+100"
383
- assert _BACKREF_RE.fullmatch(r"\1 foo \2").group("num") == "1"
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 _BACKREF_RE.fullmatch(r"\g<0>")
387
- assert not _BACKREF_RE.fullmatch(r"\0")
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 CrosshairInternal
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)]
@@ -1,26 +1,45 @@
1
1
  import time as real_time
2
2
  from inspect import Signature
3
- from typing import Any, Callable
3
+ from math import isfinite
4
+ from typing import Any, Literal
4
5
 
5
- from crosshair.core import FunctionInterps
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
- def _gte_last(fn: Callable, value: Any) -> bool:
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
- interps = context_statespace().extra(FunctionInterps)
14
- previous = interps._interpretations[fn]
15
- if len(previous) < 2:
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(real_time.monotonic, __return__),
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(real_time.monotonic_ns, __return__),
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(real_time.process_time, __return__),
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(real_time.process_time_ns, __return__),
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, MessageType
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, CANNOT_CONFIRM)
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, CONFIRMED)
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, "\u00BD"))) # (1/2 character)
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, "\u0E54"))) # (Thai numerial 4)
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, "\u0E54"))) # (Thai numerial 4)
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,13 @@
1
+ from weakref import ref
2
+
3
+ from crosshair.core import register_patch
4
+
5
+
6
+ def _ref_call(r):
7
+ if not isinstance(r, ref):
8
+ raise TypeError
9
+ return None
10
+
11
+
12
+ def make_registrations():
13
+ register_patch(ref.__call__, _ref_call)
@@ -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
- from pygls.server import LanguageServer
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.show_message_log()ing the long description
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.show_message_log(*a)
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.show_message_log(
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.show_message_log(status)
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.show_message_log(env_info())
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.show_message_log("New path set: " + repr(paths))
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.show_message_log("did_change")
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.show_message_log("did_close")
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, [])