Class: Taski::StaticAnalysis::StartDepAnalyzer
- Inherits:
-
Object
- Object
- Taski::StaticAnalysis::StartDepAnalyzer
- Defined in:
- lib/taski/static_analysis/start_dep_analyzer.rb
Overview
Analyzes a task's run method AST to find dependencies that are safe to speculatively pre-start (start_dep). Uses a whitelist approach: only confirmed patterns are collected; unknown patterns cause the analyzer to stop (returning what was collected so far up to that point).
Currently handles variable assignment patterns only:
a = Dep.value (LocalVariableWriteNode)
@a = Dep.value (InstanceVariableWriteNode)
This is a performance optimization only — if analysis fails or returns empty, tasks still work correctly via lazy Fiber pull (need_dep).
Defined Under Namespace
Classes: AnalysisResult, DepInfo
Constant Summary collapse
- SAFE_TYPES =
AST node types that are known safe (not dependencies, won't stop scanning)
Set[ Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode, Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::IntegerNode, Prism::FloatNode, Prism::StringNode, Prism::SymbolNode, Prism::NilNode, Prism::TrueNode, Prism::FalseNode, Prism::SelfNode ].freeze
- EMPTY_RESULT =
AnalysisResult.new(start_deps: Set.new.freeze, sync_deps: Set.new.freeze).freeze
Class Method Summary collapse
-
.analyze(task_class) ⇒ Array<DepInfo>
Analyze a task class and return deps safe to prestart.
-
.clear_cache! ⇒ Object
Clear cache (for testing).
Instance Method Summary collapse
-
#analyze(task_class) ⇒ AnalysisResult
Analyze a task class's run method and return safe-to-prestart deps and sync_dep_classes (deps whose proxy variables are used unsafely).
-
#initialize ⇒ StartDepAnalyzer
constructor
A new instance of StartDepAnalyzer.
Constructor Details
#initialize ⇒ StartDepAnalyzer
Returns a new instance of StartDepAnalyzer.
59 60 61 62 |
# File 'lib/taski/static_analysis/start_dep_analyzer.rb', line 59 def initialize @deps = [] @seen_classes = Set.new end |
Class Method Details
.analyze(task_class) ⇒ Array<DepInfo>
Analyze a task class and return deps safe to prestart. Results are cached per task class.
39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/taski/static_analysis/start_dep_analyzer.rb', line 39 def analyze(task_class) @cache_mutex.synchronize do return @cache[task_class] if @cache.key?(task_class) end result = new.analyze(task_class) @cache_mutex.synchronize do @cache[task_class] ||= result end end |
.clear_cache! ⇒ Object
Clear cache (for testing)
52 53 54 |
# File 'lib/taski/static_analysis/start_dep_analyzer.rb', line 52 def clear_cache! @cache_mutex.synchronize { @cache.clear } end |
Instance Method Details
#analyze(task_class) ⇒ AnalysisResult
Analyze a task class's run method and return safe-to-prestart deps and sync_dep_classes (deps whose proxy variables are used unsafely).
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/taski/static_analysis/start_dep_analyzer.rb', line 68 def analyze(task_class) @task_class = task_class @exported_ivars = Set.new(task_class.exported_methods.map { |m| :"@#{m}" }) source_location = task_class.instance_method(:run).source_location return EMPTY_RESULT unless source_location file_path, _line = source_location parse_result = Prism.parse_file(file_path) run_node = find_run_method(parse_result.value, task_class) return EMPTY_RESULT unless run_node&.body scan_statements(run_node.body) unsafe_classes = detect_unsafe_proxy_usage(run_node.body) all_dep_classes = Set.new(@deps.map(&:klass)) start_deps = all_dep_classes - unsafe_classes sync_deps = unsafe_classes AnalysisResult.new(start_deps: start_deps, sync_deps: sync_deps) rescue NameError EMPTY_RESULT end |