Module: Ryac::LazyRegions

Defined in:
lib/ryac/analysis/lazy_regions.rb,
sig/ryac/analysis/lazy_regions.rbs

Overview

A file the program loads at runtime through a dynamic require (optcarrot's require_relative "driver/#{name}_#{type}") is bundled by the Concatenator as a registration:

RYAC_LAZY["optcarrot/driver/sdl2_video"] = -> { <the file> }

The lambda is the file's schedule and nothing more: its body runs when the original require would have, so a require "ffi" or ffi_lib "SDL2" at the file's top level keeps its optional-dependency timing. For analysis the body is ordinary code in the closed world — one rename table across the core and the files that subclass it.

Two things about a region are special. TypeProf does not register a class defined inside a block, so it is handed a view of the source with the wrapper bytes blanked (typeprof_view): every byte of the body keeps its position, and the body reads as the top-level code it becomes when it runs. And a region does not exist at boot: a constant it alone defines cannot be aliased at the end of the file, and an external constant it references cannot be hoisted into the preamble.

Regions are recognized by shape — a top-level Const["key"] = -> { ... } statement with a parameterless lambda — not by the registry's name, which the aliaser renames like any other constant. Reading a program's own statement of that shape as a region is safe: every consequence keeps more names, never fewer.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.blank(bytes, from, to) ⇒ Object



83
84
85
# File 'lib/ryac/analysis/lazy_regions.rb', line 83

def blank(bytes, from, to)
  (from...to).each { |i| bytes.setbyte(i, 0x20) unless bytes.getbyte(i) == 0x0a }
end

.collect(prism_root) ⇒ Object

The lambdas of every registration, in source order.



33
34
35
36
37
# File 'lib/ryac/analysis/lazy_regions.rb', line 33

def collect(prism_root)
  lambdas = [] #: Array[Prism::LambdaNode]
  each_registration(prism_root) { |_statement, lambda_node| lambdas << lambda_node }
  lambdas
end

.contains?(lambdas, node) ⇒ Boolean

Returns:

  • (Boolean)


39
40
41
42
# File 'lib/ryac/analysis/lazy_regions.rb', line 39

def contains?(lambdas, node)
  offset = node.location.start_offset
  lambdas.any? { |lam| offset > lam.opening_loc.start_offset && offset < lam.closing_loc.start_offset }
end

.each_registration(prism_root) ⇒ Object



62
63
64
65
66
67
# File 'lib/ryac/analysis/lazy_regions.rb', line 62

def each_registration(prism_root)
  prism_root.statements.body.each do |statement|
    lambda_node = registration_lambda(statement)
    yield statement, lambda_node if lambda_node
  end
end

.registration_lambda(statement) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/ryac/analysis/lazy_regions.rb', line 69

def registration_lambda(statement)
  return nil unless statement.is_a?(Prism::CallNode) && statement.name == :[]=
  return nil unless statement.receiver.is_a?(Prism::ConstantReadNode)

  args = statement.arguments&.arguments
  return nil unless args && args.size == 2

  key, value = args
  return nil unless key.is_a?(Prism::StringNode) && value.is_a?(Prism::LambdaNode)
  return nil if value.parameters

  value
end

.typeprof_view(content, prism_root) ⇒ Object

The source as TypeProf must read it: each registration's wrapper — ["key"] = -> {` after the registry constant, and the closing `} — replaced by spaces, newlines kept, so the body sits at top level at its original byte positions. The registry constant stays, as its own statement: it is a reference the aliaser renames like any other, and it has to be renamed at the registration too.



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/ryac/analysis/lazy_regions.rb', line 50

def typeprof_view(content, prism_root)
  bytes = content.b
  each_registration(prism_root) do |statement, lambda_node|
    # statement.receiver is the registry constant (registration_lambda)
    from = statement.receiver.location.end_offset # steep:ignore NoMethod
    blank(bytes, from, lambda_node.opening_loc.end_offset)
    bytes.setbyte(from, 0x3b)
    blank(bytes, lambda_node.closing_loc.start_offset, lambda_node.closing_loc.end_offset)
  end
  bytes.force_encoding(content.encoding)
end

Instance Method Details

#self?.blank ⇒ void

This method returns an undefined value.

Parameters:

  • bytes (String)
  • from (Integer)
  • to (Integer)


8
# File 'sig/ryac/analysis/lazy_regions.rbs', line 8

def self?.blank: (String bytes, Integer from, Integer to) -> void

#self?.collect ⇒ Array[Prism::LambdaNode]

Parameters:

  • prism_root (Prism::ProgramNode)

Returns:

  • (Array[Prism::LambdaNode])


3
# File 'sig/ryac/analysis/lazy_regions.rbs', line 3

def self?.collect: (Prism::ProgramNode prism_root) -> Array[Prism::LambdaNode]

#self?.contains? ⇒ Boolean

Parameters:

  • lambdas (Array[Prism::LambdaNode])
  • node (Prism::Node)

Returns:

  • (Boolean)


4
# File 'sig/ryac/analysis/lazy_regions.rbs', line 4

def self?.contains?: (Array[Prism::LambdaNode] lambdas, Prism::Node node) -> bool

#self?.each_registration {|arg0, arg1| ... } ⇒ void

This method returns an undefined value.

Parameters:

  • prism_root (Prism::ProgramNode)

Yields:

Yield Parameters:

  • arg0 (Prism::Node)
  • arg1 (Prism::LambdaNode)

Yield Returns:

  • (void)


6
# File 'sig/ryac/analysis/lazy_regions.rbs', line 6

def self?.each_registration: (Prism::ProgramNode prism_root) { (Prism::Node, Prism::LambdaNode) -> void } -> void

#self?.registration_lambda ⇒ Prism::LambdaNode?

Parameters:

  • statement (Prism::Node)

Returns:

  • (Prism::LambdaNode, nil)


7
# File 'sig/ryac/analysis/lazy_regions.rbs', line 7

def self?.registration_lambda: (Prism::Node statement) -> Prism::LambdaNode?

#self?.typeprof_view ⇒ String

Parameters:

  • content (String)
  • prism_root (Prism::ProgramNode)

Returns:

  • (String)


5
# File 'sig/ryac/analysis/lazy_regions.rbs', line 5

def self?.typeprof_view: (String content, Prism::ProgramNode prism_root) -> String