Class: Rigor::Plugin::ContributionIndex

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/plugin/registry.rb

Overview

ADR-52 WD1 — the compiled contribution table. Categorises a loaded plugin set by which per-call contribution paths each plugin actually implements, AND compiles the declarative gates (method names, block_as_methods method names, owns_receivers) into frozen lookup structures, so the engine's hot sites discover "no plugin cares about this call" in O(1) instead of O(plugins × rules) — a top hotspot on plugin-heavy projects (GitLab's 11 plugins, of which only 2 implement any per-call path). Built once per Registry.

Ordering contract: the gates only PRUNE consultations that could not fire (every pruned rule would have failed its own methods: / method_names: check); the engine still iterates the plugin subsets in registry order and each plugin's rules in declaration order, so the surviving contributions arrive in exactly the order the ungated walk produced — diagnostics stay byte-identical. The receiver-class ancestry match for dynamic_return still happens per-dispatch inside Plugin::Base#dynamic_return_type.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(plugins) ⇒ ContributionIndex

Returns a new instance of ContributionIndex.



28
29
30
31
32
33
34
35
36
37
# File 'lib/rigor/plugin/registry.rb', line 28

def initialize(plugins)
  compile_memberships(plugins)
  compile_gates
  @block_entries_by_method_name = build_block_entries(plugins)
  @owns_receivers = plugins.flat_map { |p| manifest_for(p)&.owns_receivers || [] }.uniq.freeze
  # Per-run ancestry verdict memo, keyed by environment identity then class name. Mutable inside the
  # frozen index — sound because the class graph is fixed for the lifetime of a run.
  @owns_receiver_memo = {}.compare_by_identity
  freeze
end

Instance Attribute Details

#for_file_diagnosticsObject (readonly)

Ordered (registry-order) subsets relevant to each collector, and the membership sets used to gate the paths within a plugin. for_file_diagnostics is the subset the runner's per-file diagnostic loop visits: plugins overriding #diagnostics_for_file or declaring at least one node_rule.



23
24
25
# File 'lib/rigor/plugin/registry.rb', line 23

def for_file_diagnostics
  @for_file_diagnostics
end

#for_method_dispatchObject (readonly)

Ordered (registry-order) subsets relevant to each collector, and the membership sets used to gate the paths within a plugin. for_file_diagnostics is the subset the runner's per-file diagnostic loop visits: plugins overriding #diagnostics_for_file or declaring at least one node_rule.



23
24
25
# File 'lib/rigor/plugin/registry.rb', line 23

def for_method_dispatch
  @for_method_dispatch
end

#for_statementObject (readonly)

Ordered (registry-order) subsets relevant to each collector, and the membership sets used to gate the paths within a plugin. for_file_diagnostics is the subset the runner's per-file diagnostic loop visits: plugins overriding #diagnostics_for_file or declaring at least one node_rule.



23
24
25
# File 'lib/rigor/plugin/registry.rb', line 23

def for_statement
  @for_statement
end

Instance Method Details

#block_entries_for(method_name) ⇒ Object

The Macro::BlockAsMethod entries whose method_names include method_name, in (plugin registration, manifest declaration) order — the same first-match order the previous plugins × entries walk visited.



81
82
83
# File 'lib/rigor/plugin/registry.rb', line 81

def block_entries_for(method_name)
  @block_entries_by_method_name.fetch(method_name, EMPTY_BLOCK_ENTRIES)
end

#dispatch_candidate?(method_name) ⇒ Boolean

O(1) "could any plugin contribute a return type for a call named method_name?" — false only when every dynamic_return rule is methods:-gated on other names, in which case the ungated walk would have produced zero contributions too.

Returns:

  • (Boolean)


45
46
47
48
49
# File 'lib/rigor/plugin/registry.rb', line 45

def dispatch_candidate?(method_name)
  return true if @dynamic_global_gate.nil?

  @dynamic_global_gate.include?(method_name)
end

#dynamic?(plugin) ⇒ Boolean

Returns:

  • (Boolean)


39
# File 'lib/rigor/plugin/registry.rb', line 39

def dynamic?(plugin) = @dynamic.include?(plugin)

#dynamic_candidate_for?(plugin, method_name) ⇒ Boolean

Per-plugin gate: false when the plugin declares no dynamic_return rules at all, or when every rule is methods:-gated and none lists method_name — i.e. when #dynamic_return_type would return nil without ever entering a rule block. Subsumes the old dynamic?(plugin) membership check at the collector's call site.

Returns:

  • (Boolean)


63
64
65
66
67
68
# File 'lib/rigor/plugin/registry.rb', line 63

def dynamic_candidate_for?(plugin, method_name)
  return false unless @dynamic.include?(plugin)

  gate = @dynamic_gates[plugin]
  gate.nil? || gate.include?(method_name)
end

#owns_receiver?(class_name, environment) ⇒ Boolean

True when class_name equals or inherits from any plugin's manifest-declared owns_receivers: entry. The union is compiled at build time (almost always empty → O(1) false) and per-class verdicts memoise per environment.

Returns:

  • (Boolean)


88
89
90
91
92
93
94
95
96
# File 'lib/rigor/plugin/registry.rb', line 88

def owns_receiver?(class_name, environment)
  return false if @owns_receivers.empty? || class_name.nil?

  memo = (@owns_receiver_memo[environment] ||= {})
  memo.fetch(class_name) do
    memo[class_name] =
      @owns_receivers.any? { |owner| class_matches_owner?(class_name, owner, environment) }
  end
end

#statement_candidate?(method_name) ⇒ Boolean

O(1) statement-path sibling of #dispatch_candidate? over the type_specifier rules (which are always methods:-gated).

Returns:

  • (Boolean)


53
54
55
56
57
# File 'lib/rigor/plugin/registry.rb', line 53

def statement_candidate?(method_name)
  return true if @type_specifier_global_gate.nil?

  @type_specifier_global_gate.include?(method_name)
end

#type_specifier?(plugin) ⇒ Boolean

Returns:

  • (Boolean)


40
# File 'lib/rigor/plugin/registry.rb', line 40

def type_specifier?(plugin) = @type_specifier.include?(plugin)

#type_specifier_candidate_for?(plugin, method_name) ⇒ Boolean

Per-plugin gate over type_specifier rules; same contract as #dynamic_candidate_for?.

Returns:

  • (Boolean)


71
72
73
74
75
76
# File 'lib/rigor/plugin/registry.rb', line 71

def type_specifier_candidate_for?(plugin, method_name)
  return false unless @type_specifier.include?(plugin)

  gate = @type_specifier_gates[plugin]
  gate.nil? || gate.include?(method_name)
end