sflkit 0.5.0__tar.gz → 0.5.2__tar.gz

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 (86) hide show
  1. {sflkit-0.5.0/src/sflkit.egg-info → sflkit-0.5.2}/PKG-INFO +2 -2
  2. {sflkit-0.5.0 → sflkit-0.5.2}/pyproject.toml +1 -1
  3. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/__init__.py +1 -1
  4. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/analysis/analysis_type.py +13 -13
  5. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/analysis/analyzer.py +14 -4
  6. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/analysis/factory.py +180 -95
  7. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/analysis/predicate.py +86 -63
  8. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/analysis/similarity.py +1 -1
  9. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/analysis/spectra.py +100 -49
  10. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/events/event_file.py +8 -0
  11. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/features/handler.py +67 -33
  12. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/features/value.py +26 -5
  13. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/features/vector.py +27 -14
  14. sflkit-0.5.2/src/sflkit/model/model.py +150 -0
  15. sflkit-0.5.2/src/sflkit/model/parallel.py +97 -0
  16. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/runners/run.py +49 -50
  17. sflkit-0.5.2/src/sflkit/weights/models.py +329 -0
  18. {sflkit-0.5.0 → sflkit-0.5.2/src/sflkit.egg-info}/PKG-INFO +2 -2
  19. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit.egg-info/SOURCES.txt +1 -0
  20. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit.egg-info/requires.txt +1 -1
  21. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_cli.py +2 -1
  22. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_config.py +2 -2
  23. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_dependency_weights.py +4 -2
  24. sflkit-0.5.2/tests/test_features.py +451 -0
  25. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_runner.py +1 -0
  26. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_runner_parallel.py +3 -1
  27. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_suggestions.py +2 -0
  28. sflkit-0.5.0/src/sflkit/model/model.py +0 -114
  29. sflkit-0.5.0/src/sflkit/model/parallel.py +0 -127
  30. sflkit-0.5.0/src/sflkit/weights/models.py +0 -300
  31. {sflkit-0.5.0 → sflkit-0.5.2}/LICENSE +0 -0
  32. {sflkit-0.5.0 → sflkit-0.5.2}/README.md +0 -0
  33. {sflkit-0.5.0 → sflkit-0.5.2}/setup.cfg +0 -0
  34. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/analysis/__init__.py +0 -0
  35. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/analysis/mapping.py +0 -0
  36. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/analysis/suggestion.py +0 -0
  37. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/cli.py +0 -0
  38. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/color.py +0 -0
  39. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/config.py +0 -0
  40. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/evaluation.py +0 -0
  41. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/events/__init__.py +0 -0
  42. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/events/mapping.py +0 -0
  43. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/features/__init__.py +0 -0
  44. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/instrumentation/__init__.py +0 -0
  45. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/instrumentation/dir_instrumentation.py +0 -0
  46. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/instrumentation/file_instrumentation.py +0 -0
  47. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/__init__.py +0 -0
  48. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/extract.py +0 -0
  49. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/finder.py +0 -0
  50. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/java/__init__.py +0 -0
  51. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/java/extract.py +0 -0
  52. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/java/factory.py +0 -0
  53. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/java/finder.py +0 -0
  54. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/java/visitor.py +0 -0
  55. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/language.py +0 -0
  56. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/meta.py +0 -0
  57. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/python/__init__.py +0 -0
  58. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/python/extract.py +0 -0
  59. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/python/factory.py +0 -0
  60. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/python/finder.py +0 -0
  61. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/python/visitor.py +0 -0
  62. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/language/visitor.py +0 -0
  63. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/logger.py +0 -0
  64. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/model/__init__.py +0 -0
  65. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/model/scope.py +0 -0
  66. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/runners/__init__.py +0 -0
  67. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/weights/__init__.py +0 -0
  68. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit/weights/analyzer.py +0 -0
  69. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit.egg-info/dependency_links.txt +0 -0
  70. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit.egg-info/entry_points.txt +0 -0
  71. {sflkit-0.5.0 → sflkit-0.5.2}/src/sflkit.egg-info/top_level.txt +0 -0
  72. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_analysis_objects.py +0 -0
  73. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_analyzer.py +0 -0
  74. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_color.py +0 -0
  75. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_coverage.py +0 -0
  76. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_evaluation.py +0 -0
  77. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_events.py +0 -0
  78. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_execution.py +0 -0
  79. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_instrumentation.py +0 -0
  80. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_language.py +0 -0
  81. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_parallelization.py +0 -0
  82. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_predicates.py +0 -0
  83. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_runner_pytest_versions.py +0 -0
  84. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_scope.py +0 -0
  85. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_spectra.py +0 -0
  86. {sflkit-0.5.0 → sflkit-0.5.2}/tests/test_visitors.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sflkit
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: SFLKit: : A Workbench for Statistical Fault Localization
5
5
  Author-email: Marius Smytzek <marius.smytzek@cispa.de>
6
6
  Project-URL: Homepage, https://github.com/smythi93/sflkit/
@@ -15,7 +15,7 @@ Classifier: Topic :: Software Development :: Testing
15
15
  Requires-Python: >=3.10
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
- Requires-Dist: sflkitlib>=0.2.1
18
+ Requires-Dist: sflkitlib>=0.2.2
19
19
  Requires-Dist: numpy>=2.1.3
20
20
  Requires-Dist: matplotlib>=3.10.0
21
21
  Requires-Dist: pandas>=2.2.3
@@ -25,7 +25,7 @@ classifiers = [
25
25
  "Topic :: Software Development :: Testing"
26
26
  ]
27
27
  dependencies = [
28
- "sflkitlib>=0.2.1",
28
+ "sflkitlib>=0.2.2",
29
29
  "numpy>=2.1.3",
30
30
  "matplotlib>=3.10.0",
31
31
  "pandas>=2.2.3",
@@ -6,7 +6,7 @@ from sflkit.config import Config, parse_config
6
6
  from sflkit.instrumentation.dir_instrumentation import DirInstrumentation
7
7
 
8
8
 
9
- __version__ = "0.5.0"
9
+ __version__ = "0.5.2"
10
10
 
11
11
 
12
12
  def instrument_config(conf: Config):
@@ -1,6 +1,6 @@
1
+ import abc
1
2
  import enum
2
- from abc import abstractmethod
3
- from typing import List, Type, Optional
3
+ from typing import List, Type, Optional, Any
4
4
 
5
5
  from sflkit.events.event_file import EventFile
6
6
  from sflkit.model.scope import Scope
@@ -43,7 +43,7 @@ If you want to add new spectra or predicates, please register them here and in s
43
43
  """
44
44
 
45
45
 
46
- class AnalysisObject:
46
+ class AnalysisObject(abc.ABC):
47
47
  function_finder = None
48
48
  loop_finder = None
49
49
  branch_finder = None
@@ -58,17 +58,17 @@ class AnalysisObject:
58
58
  def __init__(self):
59
59
  self.suspiciousness: float = 0
60
60
  self.last_evaluation: EvaluationResult = EvaluationResult.UNOBSERVED
61
- self.hits = dict()
61
+ self.hits: dict[EventFile, dict[Optional[int], Any]] = dict()
62
62
 
63
63
  def adjust_weight(self, event_file: EventFile, weight: float):
64
64
  pass
65
65
 
66
- @abstractmethod
66
+ @abc.abstractmethod
67
67
  def serialize(self) -> dict:
68
68
  pass
69
69
 
70
70
  @staticmethod
71
- @abstractmethod
71
+ @abc.abstractmethod
72
72
  def deserialize(s: dict) -> "AnalysisObject":
73
73
  pass
74
74
 
@@ -82,7 +82,7 @@ class AnalysisObject:
82
82
  return f"{self.analysis_type()}"
83
83
 
84
84
  @staticmethod
85
- @abstractmethod
85
+ @abc.abstractmethod
86
86
  def analysis_type() -> AnalysisType:
87
87
  raise NotImplementedError()
88
88
 
@@ -93,19 +93,19 @@ class AnalysisObject:
93
93
  def calculate(self):
94
94
  pass
95
95
 
96
- @abstractmethod
96
+ @abc.abstractmethod
97
97
  def finalize(self, passed: List, failed: List):
98
98
  raise NotImplementedError()
99
99
 
100
- @abstractmethod
101
- def hit(self, id_, event, scope_: Scope = None):
100
+ @abc.abstractmethod
101
+ def hit(self, id_: EventFile, event, scope: Scope = None):
102
102
  raise NotImplementedError()
103
103
 
104
- @abstractmethod
104
+ @abc.abstractmethod
105
105
  def get_suggestion(self):
106
106
  raise NotImplementedError()
107
107
 
108
- @abstractmethod
108
+ @abc.abstractmethod
109
109
  def assign_suspiciousness(self):
110
110
  raise NotImplementedError()
111
111
 
@@ -114,7 +114,7 @@ class AnalysisObject:
114
114
  return any(map(lambda e: isinstance(event, e), events))
115
115
 
116
116
  @staticmethod
117
- @abstractmethod
117
+ @abc.abstractmethod
118
118
  def events() -> List[Type]:
119
119
  raise NotImplementedError()
120
120
 
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import os
3
+ from concurrent.futures import ThreadPoolExecutor
3
4
  from typing import List, Callable, Set, Dict, Optional, Any, Type
4
5
 
5
6
  from sflkit.analysis.analysis_type import AnalysisType, AnalysisObject
@@ -42,6 +43,7 @@ class Analyzer:
42
43
  meta_model: Optional[MetaModel] = None,
43
44
  model_class: Type[Model] = None,
44
45
  parallel: bool = False,
46
+ workers: int = 4,
45
47
  ):
46
48
  if (
47
49
  relevant_event_files is None
@@ -64,6 +66,7 @@ class Analyzer:
64
66
  else:
65
67
  model_class = Model
66
68
  self.model = model_class(factory)
69
+ self.workers = workers
67
70
  self.paths: Dict[int, os.PathLike] = dict()
68
71
  self.max_suspiciousness = 0
69
72
  self.min_suspiciousness = 0
@@ -76,7 +79,7 @@ class Analyzer:
76
79
  self.model.prepare(event_file)
77
80
  with event_file:
78
81
  for event in event_file.load():
79
- event.handle(self.model)
82
+ event.handle(self.model, event_file)
80
83
  self.model.follow_up(event_file)
81
84
 
82
85
  def _finalize(self):
@@ -87,9 +90,16 @@ class Analyzer:
87
90
  def analyze(self):
88
91
  if self.meta:
89
92
  raise NotImplementedError("Not implemented for meta/loaded analyzer")
90
- for event_file in self.relevant_event_files + self.irrelevant_event_files:
91
- self.paths[event_file.run_id] = event_file.path
92
- self._analyze(event_file)
93
+ if self.workers > 1:
94
+ event_files = self.relevant_event_files + self.irrelevant_event_files
95
+ for event_file in event_files:
96
+ self.paths[event_file.run_id] = event_file.path
97
+ with ThreadPoolExecutor(max_workers=self.workers) as executor:
98
+ list(executor.map(self._analyze, event_files))
99
+ else:
100
+ for event_file in self.relevant_event_files + self.irrelevant_event_files:
101
+ self.paths[event_file.run_id] = event_file.path
102
+ self._analyze(event_file)
93
103
  self._finalize()
94
104
 
95
105
  def dump(self, path: os.PathLike, indent: Optional[int] = None):
@@ -1,8 +1,9 @@
1
1
  import abc
2
- from abc import abstractmethod
2
+ from threading import Lock
3
3
  from typing import List, Type, Set
4
4
 
5
5
  from sflkitlib.events import EventType
6
+ from sflkitlib.events.event import DefEvent
6
7
 
7
8
  from sflkit.analysis.analysis_type import AnalysisObject, AnalysisType
8
9
  from sflkit.analysis.predicate import (
@@ -21,25 +22,29 @@ from sflkit.analysis.predicate import (
21
22
  FunctionErrorPredicate,
22
23
  )
23
24
  from sflkit.analysis.spectra import Line, Function, Loop, DefUse, Length
25
+ from sflkit.events.event_file import EventFile
24
26
  from sflkit.model.scope import Scope
25
27
 
26
28
 
27
- class AnalysisFactory:
29
+ class AnalysisFactory(abc.ABC):
28
30
  def __init__(self):
29
31
  self.objects = dict()
32
+ self._lock = Lock()
30
33
 
31
- @abstractmethod
32
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
34
+ @abc.abstractmethod
35
+ def get_analysis(
36
+ self, event, event_file: EventFile, scope: Scope = None
37
+ ) -> List[AnalysisObject]:
33
38
  raise NotImplementedError()
34
39
 
35
- def handle(self, event, scope: Scope = None):
36
- analysis = self.get_analysis(event, scope=scope)
40
+ def handle(self, event, event_file: EventFile, scope: Scope = None):
41
+ analysis = self.get_analysis(event, event_file, scope=scope)
37
42
  if analysis:
38
43
  return analysis
39
44
  else:
40
45
  return self.default()
41
46
 
42
- def reset(self):
47
+ def reset(self, event_file: EventFile):
43
48
  pass
44
49
 
45
50
  @staticmethod
@@ -55,25 +60,32 @@ class CombinationFactory(AnalysisFactory):
55
60
  super().__init__()
56
61
  self.factories = factories
57
62
 
58
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
63
+ def get_analysis(
64
+ self, event, event_file: EventFile, scope: Scope = None
65
+ ) -> List[AnalysisObject]:
59
66
  return sum(
60
- [factory.handle(event, scope) for factory in self.factories], start=list()
67
+ [factory.handle(event, event_file, scope) for factory in self.factories],
68
+ start=list(),
61
69
  )
62
70
 
63
- def reset(self):
64
- [f.reset() for f in self.factories]
71
+ def reset(self, event_file: EventFile):
72
+ [f.reset(event_file) for f in self.factories]
65
73
 
66
74
  def get_all(self) -> Set[AnalysisObject]:
67
75
  return set().union(*map(lambda f: f.get_all(), self.factories))
68
76
 
69
77
 
70
78
  class LineFactory(AnalysisFactory):
71
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
79
+ def get_analysis(
80
+ self, event, event_file: EventFile, scope: Scope = None
81
+ ) -> List[AnalysisObject]:
72
82
  if event.event_type == EventType.LINE:
73
83
  key = (Line.analysis_type(), event.file, event.line)
74
- if key not in self.objects:
75
- self.objects[key] = Line(event)
84
+ with self._lock:
85
+ if key not in self.objects:
86
+ self.objects[key] = Line(event)
76
87
  return [self.objects[key]]
88
+ return None
77
89
 
78
90
 
79
91
  class BranchFactory(AnalysisFactory):
@@ -81,12 +93,15 @@ class BranchFactory(AnalysisFactory):
81
93
  super().__init__()
82
94
  self.else_ = else_
83
95
 
84
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
96
+ def get_analysis(
97
+ self, event, event_file: EventFile, scope: Scope = None
98
+ ) -> List[AnalysisObject]:
85
99
  if event.event_type == EventType.BRANCH:
86
100
  key = (Branch.analysis_type(), event.file, event.line, event.then_id)
87
101
  then = event.then_id < event.else_id
88
- if key not in self.objects:
89
- self.objects[key] = Branch(event, then=then)
102
+ with self._lock:
103
+ if key not in self.objects:
104
+ self.objects[key] = Branch(event, then=then, then_id=event.then_id)
90
105
  if self.else_ and event.else_id >= 0:
91
106
  else_key = (
92
107
  Branch.analysis_type(),
@@ -94,19 +109,28 @@ class BranchFactory(AnalysisFactory):
94
109
  event.line,
95
110
  event.else_id,
96
111
  )
97
- if else_key not in self.objects:
98
- self.objects[else_key] = Branch(event, then=not then)
112
+ with self._lock:
113
+ if else_key not in self.objects:
114
+ self.objects[else_key] = Branch(
115
+ event, then=not then, then_id=event.else_id
116
+ )
99
117
  return [self.objects[key], self.objects[else_key]]
100
118
  return [self.objects[key]]
101
119
 
120
+ return None
121
+
102
122
 
103
123
  class FunctionFactory(AnalysisFactory):
104
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
124
+ def get_analysis(
125
+ self, event, event_file: EventFile, scope: Scope = None
126
+ ) -> List[AnalysisObject]:
105
127
  if event.event_type == EventType.FUNCTION_ENTER:
106
128
  key = (Function.analysis_type(), event.file, event.line, event.function_id)
107
- if key not in self.objects:
108
- self.objects[key] = Function(event)
129
+ with self._lock:
130
+ if key not in self.objects:
131
+ self.objects[key] = Function(event)
109
132
  return [self.objects[key]]
133
+ return None
110
134
 
111
135
 
112
136
  class LoopFactory(AnalysisFactory):
@@ -119,60 +143,74 @@ class LoopFactory(AnalysisFactory):
119
143
  def get_all(self) -> Set[AnalysisObject]:
120
144
  return set(obj for value in self.objects.values() for obj in value)
121
145
 
122
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
146
+ def get_analysis(
147
+ self, event, event_file: EventFile, scope: Scope = None
148
+ ) -> List[AnalysisObject]:
123
149
  if event.event_type in (
124
150
  EventType.LOOP_BEGIN,
125
151
  EventType.LOOP_HIT,
126
152
  EventType.LOOP_END,
127
153
  ):
128
154
  key = (Loop.analysis_type(), event.file, event.line, event.loop_id)
129
- if key not in self.objects:
130
- self.objects[key] = []
131
- if self.hit_0:
132
- self.objects[key].append(Loop(event, Loop.evaluate_hit_0)),
133
- if self.hit_1:
134
- self.objects[key].append(Loop(event, Loop.evaluate_hit_1)),
135
- if self.hit_more:
136
- self.objects[key].append(Loop(event, Loop.evaluate_hit_more)),
155
+ with self._lock:
156
+ if key not in self.objects:
157
+ self.objects[key] = []
158
+ if self.hit_0:
159
+ self.objects[key].append(Loop(event, Loop.evaluate_hit_0)),
160
+ if self.hit_1:
161
+ self.objects[key].append(Loop(event, Loop.evaluate_hit_1)),
162
+ if self.hit_more:
163
+ self.objects[key].append(Loop(event, Loop.evaluate_hit_more)),
137
164
  if event.event_type == EventType.LOOP_BEGIN:
138
- list(map(Loop.start_loop, self.objects[key]))
165
+ for obj in self.objects[key]:
166
+ obj.start_loop(thread_id=event.thread_id)
139
167
  elif event.event_type == EventType.LOOP_HIT:
140
- list(map(Loop.hit_loop, self.objects[key]))
168
+ for obj in self.objects[key]:
169
+ obj.hit_loop(thread_id=event.thread_id)
141
170
  elif event.event_type == EventType.LOOP_END:
142
171
  return self.objects[key][:]
143
172
  return list()
173
+ return None
144
174
 
145
175
 
146
176
  class DefUseFactory(AnalysisFactory):
147
177
  def __init__(self):
148
178
  super().__init__()
149
- self.id_to_def = dict()
179
+ self.id_to_def: dict[EventFile, dict[tuple[str, int], DefEvent]] = dict()
150
180
 
151
- def reset(self):
152
- self.id_to_def = dict()
181
+ def reset(self, event_file: EventFile):
182
+ with self._lock:
183
+ self.id_to_def[event_file] = dict()
153
184
 
154
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
185
+ def get_analysis(
186
+ self, event, event_file: EventFile, scope: Scope = None
187
+ ) -> List[AnalysisObject]:
155
188
  if event.event_type == EventType.DEF:
156
- self.id_to_def[(event.var, event.var_id)] = event
189
+ with self._lock:
190
+ self.id_to_def[event_file][(event.var, event.var_id)] = event
157
191
  elif event.event_type == EventType.USE:
158
- if (event.var, event.var_id) in self.id_to_def:
192
+ with self._lock:
193
+ def_event = self.id_to_def[event_file].get((event.var, event.var_id))
194
+ if def_event:
159
195
  key = (
160
196
  DefUse.analysis_type(),
161
- self.id_to_def[(event.var, event.var_id)].file,
162
- self.id_to_def[(event.var, event.var_id)].line,
197
+ def_event.file,
198
+ def_event.line,
163
199
  event.file,
164
200
  event.line,
165
201
  event.var,
166
202
  )
167
- if key not in self.objects:
168
- self.objects[key] = DefUse(
169
- self.id_to_def[(event.var, event.var_id)], event
170
- )
203
+ with self._lock:
204
+ if key not in self.objects:
205
+ self.objects[key] = DefUse(def_event, event)
171
206
  return [self.objects[key]]
207
+ return None
172
208
 
173
209
 
174
210
  class ConditionFactory(AnalysisFactory):
175
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
211
+ def get_analysis(
212
+ self, event, event_file: EventFile, scope: Scope = None
213
+ ) -> List[AnalysisObject]:
176
214
  objects = list()
177
215
  if event.event_type == EventType.CONDITION:
178
216
  for negate in (True, False):
@@ -183,10 +221,11 @@ class ConditionFactory(AnalysisFactory):
183
221
  event.condition,
184
222
  negate,
185
223
  )
186
- if key not in self.objects:
187
- self.objects[key] = Condition(
188
- event.file, event.line, event.condition, negate=negate
189
- )
224
+ with self._lock:
225
+ if key not in self.objects:
226
+ self.objects[key] = Condition(
227
+ event.file, event.line, event.condition, negate=negate
228
+ )
190
229
  objects.append(self.objects[key])
191
230
  return objects
192
231
 
@@ -218,7 +257,9 @@ class ComparisonFactory(AnalysisFactory, abc.ABC):
218
257
 
219
258
 
220
259
  class ScalarPairFactory(ComparisonFactory):
221
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
260
+ def get_analysis(
261
+ self, event, event_file: EventFile, scope: Scope = None
262
+ ) -> List[AnalysisObject]:
222
263
  if event.event_type == EventType.DEF:
223
264
  variables = scope.get_all_vars()
224
265
  objects = list()
@@ -238,10 +279,11 @@ class ScalarPairFactory(ComparisonFactory):
238
279
  comp,
239
280
  types[0],
240
281
  )
241
- if key not in self.objects:
242
- self.objects[key] = ScalarPair(
243
- event, comp, variable.var
244
- )
282
+ with self._lock:
283
+ if key not in self.objects:
284
+ self.objects[key] = ScalarPair(
285
+ event, comp, variable.var
286
+ )
245
287
  objects.append(self.objects[key])
246
288
  else:
247
289
  for variable in variables:
@@ -257,16 +299,20 @@ class ScalarPairFactory(ComparisonFactory):
257
299
  comp,
258
300
  event.type_,
259
301
  )
260
- if key not in self.objects:
261
- self.objects[key] = ScalarPair(
262
- event, comp, variable.var
263
- )
302
+ with self._lock:
303
+ if key not in self.objects:
304
+ self.objects[key] = ScalarPair(
305
+ event, comp, variable.var
306
+ )
264
307
  objects.append(self.objects[key])
265
308
  return objects
309
+ return None
266
310
 
267
311
 
268
312
  class VariableFactory(ComparisonFactory):
269
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
313
+ def get_analysis(
314
+ self, event, event_file: EventFile, scope: Scope = None
315
+ ) -> List[AnalysisObject]:
270
316
  if event.event_type == EventType.DEF and event.type_ in [
271
317
  "int",
272
318
  "float",
@@ -282,14 +328,18 @@ class VariableFactory(ComparisonFactory):
282
328
  comp,
283
329
  "int",
284
330
  )
285
- if key not in self.objects:
286
- self.objects[key] = VariablePredicate(event, comp)
331
+ with self._lock:
332
+ if key not in self.objects:
333
+ self.objects[key] = VariablePredicate(event, comp)
287
334
  objects.append(self.objects[key])
288
335
  return objects
336
+ return None
289
337
 
290
338
 
291
339
  class ReturnFactory(ComparisonFactory):
292
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
340
+ def get_analysis(
341
+ self, event, event_file: EventFile, scope: Scope = None
342
+ ) -> List[AnalysisObject]:
293
343
  if event.event_type == EventType.FUNCTION_EXIT:
294
344
  objects = list()
295
345
  if event.type_ in ("int", "float", "bool", "str", "bytes"):
@@ -312,10 +362,13 @@ class ReturnFactory(ComparisonFactory):
312
362
  comp,
313
363
  type_,
314
364
  )
315
- if key not in self.objects:
316
- self.objects[key] = ReturnPredicate(event, comp, value=tr)
317
- objects.append(self.objects[key])
318
- if event.type_ == "NoneType":
365
+ with self._lock:
366
+ if key not in self.objects:
367
+ self.objects[key] = ReturnPredicate(
368
+ event, comp, value=tr
369
+ )
370
+ objects.append(self.objects[key])
371
+ elif event.type_ == "NoneType":
319
372
  for comp in Comp.EQ, Comp.NE:
320
373
  if comp in self.comparators:
321
374
  key = (
@@ -326,8 +379,11 @@ class ReturnFactory(ComparisonFactory):
326
379
  comp,
327
380
  event.type_,
328
381
  )
329
- if key not in self.objects:
330
- self.objects[key] = ReturnPredicate(event, comp, value=None)
382
+ with self._lock:
383
+ if key not in self.objects:
384
+ self.objects[key] = ReturnPredicate(
385
+ event, comp, value=None
386
+ )
331
387
  objects.append(self.objects[key])
332
388
  else:
333
389
  for comp in Comp.EQ, Comp.NE:
@@ -340,9 +396,14 @@ class ReturnFactory(ComparisonFactory):
340
396
  comp,
341
397
  "NoneType",
342
398
  )
343
- if key in self.objects:
344
- objects.append(self.objects[key])
399
+ with self._lock:
400
+ if key not in self.objects:
401
+ self.objects[key] = ReturnPredicate(
402
+ event, comp, value=None
403
+ )
404
+ objects.append(self.objects[key])
345
405
  return objects
406
+ return None
346
407
 
347
408
 
348
409
  class ConstantCompFactory(AnalysisFactory):
@@ -350,7 +411,9 @@ class ConstantCompFactory(AnalysisFactory):
350
411
  super().__init__()
351
412
  self.class_ = class_
352
413
 
353
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
414
+ def get_analysis(
415
+ self, event, event_file: EventFile, scope: Scope = None
416
+ ) -> List[AnalysisObject]:
354
417
  if event.event_type == EventType.DEF:
355
418
  objects = list()
356
419
  for comp in Comp.EQ, Comp.NE:
@@ -361,10 +424,13 @@ class ConstantCompFactory(AnalysisFactory):
361
424
  event.var,
362
425
  comp,
363
426
  )
364
- if key not in self.objects:
365
- self.objects[key] = self.class_(event)
427
+ with self._lock:
428
+ if key not in self.objects:
429
+ # noinspection PyArgumentList
430
+ self.objects[key] = self.class_(event)
366
431
  objects.append(self.objects[key])
367
432
  return objects
433
+ return None
368
434
 
369
435
 
370
436
  class NoneFactory(ConstantCompFactory):
@@ -387,7 +453,9 @@ class PredicateFunctionFactory(AnalysisFactory):
387
453
  super().__init__()
388
454
  self.class_ = class_
389
455
 
390
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
456
+ def get_analysis(
457
+ self, event, event_file: EventFile, scope: Scope = None
458
+ ) -> List[AnalysisObject]:
391
459
  if event.event_type == EventType.DEF:
392
460
  key = (
393
461
  self.class_.analysis_type(),
@@ -395,9 +463,12 @@ class PredicateFunctionFactory(AnalysisFactory):
395
463
  event.line,
396
464
  event.var,
397
465
  )
398
- if key not in self.objects:
399
- self.objects[key] = self.class_(event)
466
+ with self._lock:
467
+ if key not in self.objects:
468
+ # noinspection PyArgumentList
469
+ self.objects[key] = self.class_(event)
400
470
  return [self.objects[key]]
471
+ return None
401
472
 
402
473
 
403
474
  class IsAsciiFactory(PredicateFunctionFactory):
@@ -427,20 +498,28 @@ class LengthFactory(AnalysisFactory):
427
498
  def get_all(self) -> Set[AnalysisObject]:
428
499
  return set(obj for value in self.objects.values() for obj in value)
429
500
 
430
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
501
+ def get_analysis(
502
+ self, event, event_file: EventFile, scope: Scope = None
503
+ ) -> List[AnalysisObject]:
431
504
  if event.event_type == EventType.LEN:
432
505
  key = (Length.analysis_type(), event.file, event.line, event.var)
433
- if key not in self.objects:
434
- self.objects[key] = []
435
- if self.length_0:
436
- self.objects[key].append(Length(event, Length.evaluate_length_0)),
437
- if self.length_1:
438
- self.objects[key].append(Length(event, Length.evaluate_length_1)),
439
- if self.length_more:
440
- self.objects[key].append(
441
- Length(event, Length.evaluate_length_more)
442
- ),
506
+ with self._lock:
507
+ if key not in self.objects:
508
+ self.objects[key] = []
509
+ if self.length_0:
510
+ self.objects[key].append(
511
+ Length(event, Length.evaluate_length_0)
512
+ ),
513
+ if self.length_1:
514
+ self.objects[key].append(
515
+ Length(event, Length.evaluate_length_1)
516
+ ),
517
+ if self.length_more:
518
+ self.objects[key].append(
519
+ Length(event, Length.evaluate_length_more)
520
+ )
443
521
  return self.objects[key][:]
522
+ return None
444
523
 
445
524
 
446
525
  class FunctionErrorFactory(AnalysisFactory):
@@ -448,22 +527,28 @@ class FunctionErrorFactory(AnalysisFactory):
448
527
  super().__init__()
449
528
  self.function_mapping = dict()
450
529
 
451
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
530
+ def get_analysis(
531
+ self, event, event_file: EventFile, scope: Scope = None
532
+ ) -> List[AnalysisObject]:
452
533
  if event.event_type == EventType.FUNCTION_ENTER:
453
- self.function_mapping[event.function_id] = event.line
534
+ with self._lock:
535
+ self.function_mapping[event.function_id] = event.line
454
536
  if event.event_type in (EventType.FUNCTION_ERROR, EventType.FUNCTION_EXIT):
455
- line = self.function_mapping.get(event.function_id, event.line)
537
+ with self._lock:
538
+ line = self.function_mapping.get(event.function_id, event.line)
456
539
  key = (
457
540
  FunctionErrorPredicate.analysis_type(),
458
541
  event.file,
459
542
  line,
460
543
  event.function_id,
461
544
  )
462
- if key not in self.objects:
463
- self.objects[key] = FunctionErrorPredicate(
464
- event.file, line, event.function
465
- )
545
+ with self._lock:
546
+ if key not in self.objects:
547
+ self.objects[key] = FunctionErrorPredicate(
548
+ event.file, line, event.function
549
+ )
466
550
  return [self.objects[key]]
551
+ return None
467
552
 
468
553
 
469
554
  analysis_factory_mapping = {