sflkit 0.5.0__tar.gz → 0.5.1__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.1}/PKG-INFO +2 -2
  2. {sflkit-0.5.0 → sflkit-0.5.1}/pyproject.toml +1 -1
  3. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/__init__.py +1 -1
  4. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/analysis/analysis_type.py +13 -13
  5. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/analysis/analyzer.py +14 -4
  6. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/analysis/factory.py +166 -89
  7. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/analysis/predicate.py +86 -63
  8. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/analysis/similarity.py +1 -1
  9. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/analysis/spectra.py +100 -49
  10. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/events/event_file.py +8 -0
  11. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/features/handler.py +55 -24
  12. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/features/value.py +26 -5
  13. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/features/vector.py +27 -14
  14. sflkit-0.5.1/src/sflkit/model/model.py +150 -0
  15. sflkit-0.5.1/src/sflkit/model/parallel.py +97 -0
  16. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/runners/run.py +49 -50
  17. sflkit-0.5.1/src/sflkit/weights/models.py +329 -0
  18. {sflkit-0.5.0 → sflkit-0.5.1/src/sflkit.egg-info}/PKG-INFO +2 -2
  19. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit.egg-info/SOURCES.txt +1 -0
  20. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit.egg-info/requires.txt +1 -1
  21. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_cli.py +2 -1
  22. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_config.py +2 -2
  23. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_dependency_weights.py +4 -2
  24. sflkit-0.5.1/tests/test_features.py +451 -0
  25. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_runner.py +1 -0
  26. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_runner_parallel.py +3 -1
  27. {sflkit-0.5.0 → sflkit-0.5.1}/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.1}/LICENSE +0 -0
  32. {sflkit-0.5.0 → sflkit-0.5.1}/README.md +0 -0
  33. {sflkit-0.5.0 → sflkit-0.5.1}/setup.cfg +0 -0
  34. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/analysis/__init__.py +0 -0
  35. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/analysis/mapping.py +0 -0
  36. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/analysis/suggestion.py +0 -0
  37. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/cli.py +0 -0
  38. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/color.py +0 -0
  39. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/config.py +0 -0
  40. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/evaluation.py +0 -0
  41. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/events/__init__.py +0 -0
  42. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/events/mapping.py +0 -0
  43. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/features/__init__.py +0 -0
  44. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/instrumentation/__init__.py +0 -0
  45. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/instrumentation/dir_instrumentation.py +0 -0
  46. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/instrumentation/file_instrumentation.py +0 -0
  47. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/__init__.py +0 -0
  48. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/extract.py +0 -0
  49. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/finder.py +0 -0
  50. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/java/__init__.py +0 -0
  51. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/java/extract.py +0 -0
  52. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/java/factory.py +0 -0
  53. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/java/finder.py +0 -0
  54. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/java/visitor.py +0 -0
  55. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/language.py +0 -0
  56. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/meta.py +0 -0
  57. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/python/__init__.py +0 -0
  58. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/python/extract.py +0 -0
  59. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/python/factory.py +0 -0
  60. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/python/finder.py +0 -0
  61. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/python/visitor.py +0 -0
  62. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/language/visitor.py +0 -0
  63. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/logger.py +0 -0
  64. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/model/__init__.py +0 -0
  65. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/model/scope.py +0 -0
  66. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/runners/__init__.py +0 -0
  67. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/weights/__init__.py +0 -0
  68. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit/weights/analyzer.py +0 -0
  69. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit.egg-info/dependency_links.txt +0 -0
  70. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit.egg-info/entry_points.txt +0 -0
  71. {sflkit-0.5.0 → sflkit-0.5.1}/src/sflkit.egg-info/top_level.txt +0 -0
  72. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_analysis_objects.py +0 -0
  73. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_analyzer.py +0 -0
  74. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_color.py +0 -0
  75. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_coverage.py +0 -0
  76. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_evaluation.py +0 -0
  77. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_events.py +0 -0
  78. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_execution.py +0 -0
  79. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_instrumentation.py +0 -0
  80. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_language.py +0 -0
  81. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_parallelization.py +0 -0
  82. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_predicates.py +0 -0
  83. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_runner_pytest_versions.py +0 -0
  84. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_scope.py +0 -0
  85. {sflkit-0.5.0 → sflkit-0.5.1}/tests/test_spectra.py +0 -0
  86. {sflkit-0.5.0 → sflkit-0.5.1}/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.1
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.1"
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,72 @@ 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
+ self.id_to_def[event_file] = dict()
153
183
 
154
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
184
+ def get_analysis(
185
+ self, event, event_file: EventFile, scope: Scope = None
186
+ ) -> List[AnalysisObject]:
155
187
  if event.event_type == EventType.DEF:
156
- self.id_to_def[(event.var, event.var_id)] = event
188
+ self.id_to_def[event_file][(event.var, event.var_id)] = event
157
189
  elif event.event_type == EventType.USE:
158
- if (event.var, event.var_id) in self.id_to_def:
190
+ if (event.var, event.var_id) in self.id_to_def[event_file]:
159
191
  key = (
160
192
  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,
193
+ self.id_to_def[event_file][(event.var, event.var_id)].file,
194
+ self.id_to_def[event_file][(event.var, event.var_id)].line,
163
195
  event.file,
164
196
  event.line,
165
197
  event.var,
166
198
  )
167
- if key not in self.objects:
168
- self.objects[key] = DefUse(
169
- self.id_to_def[(event.var, event.var_id)], event
170
- )
199
+ with self._lock:
200
+ if key not in self.objects:
201
+ self.objects[key] = DefUse(
202
+ self.id_to_def[event_file][(event.var, event.var_id)], event
203
+ )
171
204
  return [self.objects[key]]
205
+ return None
172
206
 
173
207
 
174
208
  class ConditionFactory(AnalysisFactory):
175
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
209
+ def get_analysis(
210
+ self, event, event_file: EventFile, scope: Scope = None
211
+ ) -> List[AnalysisObject]:
176
212
  objects = list()
177
213
  if event.event_type == EventType.CONDITION:
178
214
  for negate in (True, False):
@@ -183,10 +219,11 @@ class ConditionFactory(AnalysisFactory):
183
219
  event.condition,
184
220
  negate,
185
221
  )
186
- if key not in self.objects:
187
- self.objects[key] = Condition(
188
- event.file, event.line, event.condition, negate=negate
189
- )
222
+ with self._lock:
223
+ if key not in self.objects:
224
+ self.objects[key] = Condition(
225
+ event.file, event.line, event.condition, negate=negate
226
+ )
190
227
  objects.append(self.objects[key])
191
228
  return objects
192
229
 
@@ -218,7 +255,9 @@ class ComparisonFactory(AnalysisFactory, abc.ABC):
218
255
 
219
256
 
220
257
  class ScalarPairFactory(ComparisonFactory):
221
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
258
+ def get_analysis(
259
+ self, event, event_file: EventFile, scope: Scope = None
260
+ ) -> List[AnalysisObject]:
222
261
  if event.event_type == EventType.DEF:
223
262
  variables = scope.get_all_vars()
224
263
  objects = list()
@@ -238,10 +277,11 @@ class ScalarPairFactory(ComparisonFactory):
238
277
  comp,
239
278
  types[0],
240
279
  )
241
- if key not in self.objects:
242
- self.objects[key] = ScalarPair(
243
- event, comp, variable.var
244
- )
280
+ with self._lock:
281
+ if key not in self.objects:
282
+ self.objects[key] = ScalarPair(
283
+ event, comp, variable.var
284
+ )
245
285
  objects.append(self.objects[key])
246
286
  else:
247
287
  for variable in variables:
@@ -257,16 +297,20 @@ class ScalarPairFactory(ComparisonFactory):
257
297
  comp,
258
298
  event.type_,
259
299
  )
260
- if key not in self.objects:
261
- self.objects[key] = ScalarPair(
262
- event, comp, variable.var
263
- )
300
+ with self._lock:
301
+ if key not in self.objects:
302
+ self.objects[key] = ScalarPair(
303
+ event, comp, variable.var
304
+ )
264
305
  objects.append(self.objects[key])
265
306
  return objects
307
+ return None
266
308
 
267
309
 
268
310
  class VariableFactory(ComparisonFactory):
269
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
311
+ def get_analysis(
312
+ self, event, event_file: EventFile, scope: Scope = None
313
+ ) -> List[AnalysisObject]:
270
314
  if event.event_type == EventType.DEF and event.type_ in [
271
315
  "int",
272
316
  "float",
@@ -282,14 +326,18 @@ class VariableFactory(ComparisonFactory):
282
326
  comp,
283
327
  "int",
284
328
  )
285
- if key not in self.objects:
286
- self.objects[key] = VariablePredicate(event, comp)
329
+ with self._lock:
330
+ if key not in self.objects:
331
+ self.objects[key] = VariablePredicate(event, comp)
287
332
  objects.append(self.objects[key])
288
333
  return objects
334
+ return None
289
335
 
290
336
 
291
337
  class ReturnFactory(ComparisonFactory):
292
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
338
+ def get_analysis(
339
+ self, event, event_file: EventFile, scope: Scope = None
340
+ ) -> List[AnalysisObject]:
293
341
  if event.event_type == EventType.FUNCTION_EXIT:
294
342
  objects = list()
295
343
  if event.type_ in ("int", "float", "bool", "str", "bytes"):
@@ -312,8 +360,11 @@ class ReturnFactory(ComparisonFactory):
312
360
  comp,
313
361
  type_,
314
362
  )
315
- if key not in self.objects:
316
- self.objects[key] = ReturnPredicate(event, comp, value=tr)
363
+ with self._lock:
364
+ if key not in self.objects:
365
+ self.objects[key] = ReturnPredicate(
366
+ event, comp, value=tr
367
+ )
317
368
  objects.append(self.objects[key])
318
369
  if event.type_ == "NoneType":
319
370
  for comp in Comp.EQ, Comp.NE:
@@ -326,8 +377,11 @@ class ReturnFactory(ComparisonFactory):
326
377
  comp,
327
378
  event.type_,
328
379
  )
329
- if key not in self.objects:
330
- self.objects[key] = ReturnPredicate(event, comp, value=None)
380
+ with self._lock:
381
+ if key not in self.objects:
382
+ self.objects[key] = ReturnPredicate(
383
+ event, comp, value=None
384
+ )
331
385
  objects.append(self.objects[key])
332
386
  else:
333
387
  for comp in Comp.EQ, Comp.NE:
@@ -343,6 +397,7 @@ class ReturnFactory(ComparisonFactory):
343
397
  if key in self.objects:
344
398
  objects.append(self.objects[key])
345
399
  return objects
400
+ return None
346
401
 
347
402
 
348
403
  class ConstantCompFactory(AnalysisFactory):
@@ -350,7 +405,9 @@ class ConstantCompFactory(AnalysisFactory):
350
405
  super().__init__()
351
406
  self.class_ = class_
352
407
 
353
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
408
+ def get_analysis(
409
+ self, event, event_file: EventFile, scope: Scope = None
410
+ ) -> List[AnalysisObject]:
354
411
  if event.event_type == EventType.DEF:
355
412
  objects = list()
356
413
  for comp in Comp.EQ, Comp.NE:
@@ -361,10 +418,13 @@ class ConstantCompFactory(AnalysisFactory):
361
418
  event.var,
362
419
  comp,
363
420
  )
364
- if key not in self.objects:
365
- self.objects[key] = self.class_(event)
421
+ with self._lock:
422
+ if key not in self.objects:
423
+ # noinspection PyArgumentList
424
+ self.objects[key] = self.class_(event)
366
425
  objects.append(self.objects[key])
367
426
  return objects
427
+ return None
368
428
 
369
429
 
370
430
  class NoneFactory(ConstantCompFactory):
@@ -387,7 +447,9 @@ class PredicateFunctionFactory(AnalysisFactory):
387
447
  super().__init__()
388
448
  self.class_ = class_
389
449
 
390
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
450
+ def get_analysis(
451
+ self, event, event_file: EventFile, scope: Scope = None
452
+ ) -> List[AnalysisObject]:
391
453
  if event.event_type == EventType.DEF:
392
454
  key = (
393
455
  self.class_.analysis_type(),
@@ -395,9 +457,12 @@ class PredicateFunctionFactory(AnalysisFactory):
395
457
  event.line,
396
458
  event.var,
397
459
  )
398
- if key not in self.objects:
399
- self.objects[key] = self.class_(event)
460
+ with self._lock:
461
+ if key not in self.objects:
462
+ # noinspection PyArgumentList
463
+ self.objects[key] = self.class_(event)
400
464
  return [self.objects[key]]
465
+ return None
401
466
 
402
467
 
403
468
  class IsAsciiFactory(PredicateFunctionFactory):
@@ -427,20 +492,28 @@ class LengthFactory(AnalysisFactory):
427
492
  def get_all(self) -> Set[AnalysisObject]:
428
493
  return set(obj for value in self.objects.values() for obj in value)
429
494
 
430
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
495
+ def get_analysis(
496
+ self, event, event_file: EventFile, scope: Scope = None
497
+ ) -> List[AnalysisObject]:
431
498
  if event.event_type == EventType.LEN:
432
499
  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
- ),
500
+ with self._lock:
501
+ if key not in self.objects:
502
+ self.objects[key] = []
503
+ if self.length_0:
504
+ self.objects[key].append(
505
+ Length(event, Length.evaluate_length_0)
506
+ ),
507
+ if self.length_1:
508
+ self.objects[key].append(
509
+ Length(event, Length.evaluate_length_1)
510
+ ),
511
+ if self.length_more:
512
+ self.objects[key].append(
513
+ Length(event, Length.evaluate_length_more)
514
+ )
443
515
  return self.objects[key][:]
516
+ return None
444
517
 
445
518
 
446
519
  class FunctionErrorFactory(AnalysisFactory):
@@ -448,7 +521,9 @@ class FunctionErrorFactory(AnalysisFactory):
448
521
  super().__init__()
449
522
  self.function_mapping = dict()
450
523
 
451
- def get_analysis(self, event, scope: Scope = None) -> List[AnalysisObject]:
524
+ def get_analysis(
525
+ self, event, event_file: EventFile, scope: Scope = None
526
+ ) -> List[AnalysisObject]:
452
527
  if event.event_type == EventType.FUNCTION_ENTER:
453
528
  self.function_mapping[event.function_id] = event.line
454
529
  if event.event_type in (EventType.FUNCTION_ERROR, EventType.FUNCTION_EXIT):
@@ -459,11 +534,13 @@ class FunctionErrorFactory(AnalysisFactory):
459
534
  line,
460
535
  event.function_id,
461
536
  )
462
- if key not in self.objects:
463
- self.objects[key] = FunctionErrorPredicate(
464
- event.file, line, event.function
465
- )
537
+ with self._lock:
538
+ if key not in self.objects:
539
+ self.objects[key] = FunctionErrorPredicate(
540
+ event.file, line, event.function
541
+ )
466
542
  return [self.objects[key]]
543
+ return None
467
544
 
468
545
 
469
546
  analysis_factory_mapping = {