Class: Audition::Static::Checks::RactorIsolation

Inherits:
Base
  • Object
show all
Defined in:
lib/audition/static/checks/ractor_isolation.rb

Overview

Ractor.new verifies block isolation at creation time: a block that touches locals from the enclosing scope raises ArgumentError before the Ractor ever runs. Prism records the resolution depth of every local reference, so captures are detectable exactly: a reference whose depth reaches past the Ractor block's own scope is an outer capture.

Ractor.shareable_proc applies a weaker rule to any block at conversion time: a captured local may not hold an unshareable object and may not be assigned more than once. Rails converts the blocks handed to its callback macros the same way once unshareable_proc_action is set, so a callback whose capture is provably unshareable is reported too; a capture of unknown value stays silent, and the boot gate is its detector.

Defined Under Namespace

Classes: CaptureScanner

Constant Summary collapse

CAPTURE_FIX =
"Capture a shareable value: freeze the local (a " \
"frozen literal or .freeze), inline it, or hoist a " \
"shareable leaf such as a Symbol into a fresh local " \
"assigned once before the block."
CALLBACK_MACROS =

Methods that keep their block for later, as a callback or boot hook; Rails runs each through try_shareable_proc.

%i[
  validate validates_each set_callback on_load initializer
  to_prepare rescue_from scope default_scope
].freeze
CALLBACK_PREFIXES =
%w[
  before_ after_ around_ prepend_before_ prepend_after_
  prepend_around_ append_before_ append_after_
  append_around_
].freeze
CONVERTERS =
%i[shareable_proc shareable_lambda].freeze
UNSHAREABLE_KINDS =

Classifications a captured value must have for the conversion to fail for certain.

%i[
  mutable_string mutable_container mutable_call
  sync_primitive default_proc proc
].freeze

Constants inherited from Base

Base::EMPTY_CATALOG, Base::WRAPPER

Instance Attribute Summary

Attributes inherited from Base

#file, #findings

Instance Method Summary collapse

Methods inherited from Base

call, check_name, explain, explanations, handlers, on

Constructor Details

#initialize(file) ⇒ RactorIsolation

Returns a new instance of RactorIsolation.



83
84
85
86
87
# File 'lib/audition/static/checks/ractor_isolation.rb', line 83

def initialize(file)
  super
  @frames = []
  @pending = []
end

Instance Method Details

#visit_block_node(node) ⇒ Object



104
# File 'lib/audition/static/checks/ractor_isolation.rb', line 104

def visit_block_node(node) = framed { super }

#visit_call_node(node) ⇒ Object



133
134
135
136
# File 'lib/audition/static/checks/ractor_isolation.rb', line 133

def visit_call_node(node)
  examine(node)
  super
end

#visit_class_node(node) ⇒ Object



96
# File 'lib/audition/static/checks/ractor_isolation.rb', line 96

def visit_class_node(node) = framed { super }

#visit_def_node(node) ⇒ Object



102
# File 'lib/audition/static/checks/ractor_isolation.rb', line 102

def visit_def_node(node) = framed { super }

#visit_lambda_node(node) ⇒ Object



106
# File 'lib/audition/static/checks/ractor_isolation.rb', line 106

def visit_lambda_node(node) = framed { super }

#visit_local_variable_and_write_node(node) ⇒ Object



123
124
125
126
# File 'lib/audition/static/checks/ractor_isolation.rb', line 123

def visit_local_variable_and_write_node(node)
  assign(node.name, node.depth, nil)
  super
end

#visit_local_variable_operator_write_node(node) ⇒ Object



118
119
120
121
# File 'lib/audition/static/checks/ractor_isolation.rb', line 118

def visit_local_variable_operator_write_node(node)
  assign(node.name, node.depth, nil)
  super
end

#visit_local_variable_or_write_node(node) ⇒ Object



113
114
115
116
# File 'lib/audition/static/checks/ractor_isolation.rb', line 113

def visit_local_variable_or_write_node(node)
  assign(node.name, node.depth, nil)
  super
end

#visit_local_variable_target_node(node) ⇒ Object



128
129
130
131
# File 'lib/audition/static/checks/ractor_isolation.rb', line 128

def visit_local_variable_target_node(node)
  assign(node.name, node.depth, nil)
  super
end

#visit_local_variable_write_node(node) ⇒ Object



108
109
110
111
# File 'lib/audition/static/checks/ractor_isolation.rb', line 108

def visit_local_variable_write_node(node)
  assign(node.name, node.depth, node.value)
  super
end

#visit_module_node(node) ⇒ Object



98
# File 'lib/audition/static/checks/ractor_isolation.rb', line 98

def visit_module_node(node) = framed { super }

#visit_program_node(node) ⇒ Object

Captures are judged once the whole file is read: a local reassigned after the block is refused just the same.



91
92
93
94
# File 'lib/audition/static/checks/ractor_isolation.rb', line 91

def visit_program_node(node)
  framed { super }
  @pending.each { |entry| judge(entry) }
end

#visit_singleton_class_node(node) ⇒ Object



100
# File 'lib/audition/static/checks/ractor_isolation.rb', line 100

def visit_singleton_class_node(node) = framed { super }