Module: Tapioca::Runtime::Reflection

Constant Summary collapse

CLASS_METHOD =

: UnboundMethod

Kernel.instance_method(:class)
CONSTANTS_METHOD =

: UnboundMethod

Module.instance_method(:constants)
NAME_METHOD =

: UnboundMethod

Module.instance_method(:name)
SINGLETON_CLASS_METHOD =

: UnboundMethod

Object.instance_method(:singleton_class)
ANCESTORS_METHOD =

: UnboundMethod

Module.instance_method(:ancestors)
SUPERCLASS_METHOD =

: UnboundMethod

Class.instance_method(:superclass)
OBJECT_ID_METHOD =

: UnboundMethod

BasicObject.instance_method(:__id__)
EQUAL_METHOD =

: UnboundMethod

BasicObject.instance_method(:equal?)
PUBLIC_INSTANCE_METHODS_METHOD =

: UnboundMethod

Module.instance_method(:public_instance_methods)
PROTECTED_INSTANCE_METHODS_METHOD =

: UnboundMethod

Module.instance_method(:protected_instance_methods)
PRIVATE_INSTANCE_METHODS_METHOD =

: UnboundMethod

Module.instance_method(:private_instance_methods)
METHOD_METHOD =

: UnboundMethod

Kernel.instance_method(:method)
UNDEFINED_CONSTANT =
Module.new.freeze
REQUIRED_FROM_LABELS =

: Array

["<top (required)>", "<main>", "<compiled>"].freeze
SignatureBlockError =
Class.new(Tapioca::Error)

Instance Method Summary collapse

Instance Method Details

#abstract_type_of(constant) ⇒ Object

: (T::Module constant) -> untyped



222
223
224
225
# File 'lib/tapioca/runtime/reflection.rb', line 222

def abstract_type_of(constant)
  T::Private::Abstract::Data.get(constant, :abstract_type) ||
    T::Private::Abstract::Data.get(singleton_class_of(constant), :abstract_type)
end

#ancestors_of(constant) ⇒ Object

: (T::Module constant) -> Array[T::Module]



63
64
65
# File 'lib/tapioca/runtime/reflection.rb', line 63

def ancestors_of(constant)
  ANCESTORS_METHOD.bind_call(constant)
end

#are_equal?(object, other) ⇒ Boolean

: (BasicObject object, BasicObject other) -> bool

Returns:

  • (Boolean)


84
85
86
# File 'lib/tapioca/runtime/reflection.rb', line 84

def are_equal?(object, other)
  EQUAL_METHOD.bind_call(object, other)
end

#attached_class_of(singleton_class) ⇒ Object

: (Class singleton_class) -> T::Module?



73
74
75
76
# File 'lib/tapioca/runtime/reflection.rb', line 73

def attached_class_of(singleton_class)
  result = singleton_class.attached_object
  Module === result ? result : nil
end

#class_of(object) ⇒ Object

: (BasicObject object) -> Class



42
43
44
# File 'lib/tapioca/runtime/reflection.rb', line 42

def class_of(object)
  CLASS_METHOD.bind_call(object)
end

#const_source_location(constant_name) ⇒ Object

: ((String | Symbol) constant_name) -> SourceLocation?



173
174
175
176
177
178
179
# File 'lib/tapioca/runtime/reflection.rb', line 173

def const_source_location(constant_name)
  return unless Object.respond_to?(:const_source_location)

  file, line = Object.const_source_location(constant_name)

  SourceLocation.from_loc([file, line]) if file && line
end

#constant_defined?(constant) ⇒ Boolean

: (BasicObject constant) -> bool

Returns:

  • (Boolean)


29
30
31
# File 'lib/tapioca/runtime/reflection.rb', line 29

def constant_defined?(constant)
  !UNDEFINED_CONSTANT.eql?(constant)
end

#constantize(symbol, inherit: false, namespace: Object) ⇒ Object

: (String symbol, ?inherit: bool, ?namespace: T::Module) -> BasicObject



35
36
37
38
39
# File 'lib/tapioca/runtime/reflection.rb', line 35

def constantize(symbol, inherit: false, namespace: Object)
  namespace.const_get(symbol, inherit)
rescue NameError, LoadError, RuntimeError, ArgumentError, TypeError
  UNDEFINED_CONSTANT
end

#constants_of(constant) ⇒ Object

: (T::Module constant) -> Array



47
48
49
# File 'lib/tapioca/runtime/reflection.rb', line 47

def constants_of(constant)
  CONSTANTS_METHOD.bind_call(constant, false)
end

#descendants_of(klass) ⇒ Object

Returns an array with all classes that are < than the supplied class.

class C; end
descendants_of(C) # => []

class B < C; end
descendants_of(C) # => [B]

class A < B; end
descendants_of(C) # => [B, A]

class D < C; end
descendants_of(C) # => [B, A, D]

: [U] ((Class & U) klass) -> Array



164
165
166
167
168
169
170
# File 'lib/tapioca/runtime/reflection.rb', line 164

def descendants_of(klass)
  result = ObjectSpace.each_object(klass.singleton_class).reject do |k|
    k.singleton_class? || k == klass
  end

  T.unsafe(result)
end

#file_candidates_for(constant) ⇒ Object

: (T::Module constant) -> Set



215
216
217
218
219
# File 'lib/tapioca/runtime/reflection.rb', line 215

def file_candidates_for(constant)
  relevant_methods_for(constant).filter_map do |method|
    method.source_location&.first
  end.to_set
end

#final_module?(constant) ⇒ Boolean

: (T::Module constant) -> bool

Returns:

  • (Boolean)


228
229
230
# File 'lib/tapioca/runtime/reflection.rb', line 228

def final_module?(constant)
  T::Private::Final.final_module?(constant)
end

#inherited_ancestors_of(constant) ⇒ Object

: (T::Module constant) -> Array[T::Module]



104
105
106
107
108
109
110
# File 'lib/tapioca/runtime/reflection.rb', line 104

def inherited_ancestors_of(constant)
  if Class === constant
    ancestors_of(superclass_of(constant) || Object)
  else
    Module.new.ancestors
  end
end

#method_of(constant, method) ⇒ Object

: (T::Module constant, Symbol method) -> Method



146
147
148
# File 'lib/tapioca/runtime/reflection.rb', line 146

def method_of(constant, method)
  METHOD_METHOD.bind_call(constant, method)
end

#name_of(constant) ⇒ Object

: (T::Module constant) -> String?



52
53
54
55
# File 'lib/tapioca/runtime/reflection.rb', line 52

def name_of(constant)
  name = NAME_METHOD.bind_call(constant)
  name&.start_with?("#<") ? nil : name
end

#name_of_type(type) ⇒ Object

: (T::Types::Base type) -> String



141
142
143
# File 'lib/tapioca/runtime/reflection.rb', line 141

def name_of_type(type)
  type.to_s
end

#object_id_of(object) ⇒ Object

: (BasicObject object) -> Integer



79
80
81
# File 'lib/tapioca/runtime/reflection.rb', line 79

def object_id_of(object)
  OBJECT_ID_METHOD.bind_call(object)
end

#private_instance_methods_of(constant) ⇒ Object

: (T::Module constant) -> Array



99
100
101
# File 'lib/tapioca/runtime/reflection.rb', line 99

def private_instance_methods_of(constant)
  PRIVATE_INSTANCE_METHODS_METHOD.bind_call(constant)
end

#protected_instance_methods_of(constant) ⇒ Object

: (T::Module constant) -> Array



94
95
96
# File 'lib/tapioca/runtime/reflection.rb', line 94

def protected_instance_methods_of(constant)
  PROTECTED_INSTANCE_METHODS_METHOD.bind_call(constant)
end

#public_instance_methods_of(constant) ⇒ Object

: (T::Module constant) -> Array



89
90
91
# File 'lib/tapioca/runtime/reflection.rb', line 89

def public_instance_methods_of(constant)
  PUBLIC_INSTANCE_METHODS_METHOD.bind_call(constant)
end

#qualified_name_of(constant) ⇒ Object

: (T::Module constant) -> String?



113
114
115
116
117
118
119
120
121
122
# File 'lib/tapioca/runtime/reflection.rb', line 113

def qualified_name_of(constant)
  name = name_of(constant)
  return if name.nil?

  if name.start_with?("::")
    name
  else
    "::#{name}"
  end
end

#resolve_loc(locations) ⇒ Object

Examines the call stack to identify the closest location where a “require” is performed by searching for the label “<top (required)>” or “block in <class:…>” in the case of an ActiveSupport.on_load hook. If none is found, it returns the location labeled “<main>”, which is the original call site. : (Array? locations) -> SourceLocation?



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/tapioca/runtime/reflection.rb', line 186

def resolve_loc(locations)
  return unless locations

  # Find the location of the closest file load, which should give us the location of the file that
  # triggered the definition.
  resolved_loc = locations.find do |loc|
    label = loc.label
    next unless label

    REQUIRED_FROM_LABELS.include?(label) || label.start_with?("block in <class:")
  end
  return unless resolved_loc

  resolved_loc_path = resolved_loc.absolute_path || resolved_loc.path

  # Find the location of the last frame in this file to get the most accurate line number.
  resolved_loc = locations.find { |loc| loc.absolute_path == resolved_loc_path }
  return unless resolved_loc

  # If the last operation was a `require`, and we have no more frames,
  # we are probably dealing with a C-method.
  return if locations.first&.label == "require"

  file = resolved_loc.absolute_path || resolved_loc.path || ""

  SourceLocation.from_loc([file, resolved_loc.lineno])
end

#sealed_module?(constant) ⇒ Boolean

: (T::Module constant) -> bool

Returns:

  • (Boolean)


233
234
235
# File 'lib/tapioca/runtime/reflection.rb', line 233

def sealed_module?(constant)
  T::Private::Sealed.sealed_module?(constant)
end

#signature_of(method) ⇒ Object

: ((UnboundMethod | Method) method) -> untyped



134
135
136
137
138
# File 'lib/tapioca/runtime/reflection.rb', line 134

def signature_of(method)
  signature_of!(method)
rescue SignatureBlockError
  nil
end

#signature_of!(method) ⇒ Object

: ((UnboundMethod | Method) method) -> untyped



127
128
129
130
131
# File 'lib/tapioca/runtime/reflection.rb', line 127

def signature_of!(method)
  T::Utils.signature_for_method(method)
rescue LoadError, StandardError
  Kernel.raise SignatureBlockError
end

#singleton_class_of(constant) ⇒ Object

: (T::Module constant) -> Class



58
59
60
# File 'lib/tapioca/runtime/reflection.rb', line 58

def singleton_class_of(constant)
  SINGLETON_CLASS_METHOD.bind_call(constant)
end

#superclass_of(constant) ⇒ Object

: (Class constant) -> Class?



68
69
70
# File 'lib/tapioca/runtime/reflection.rb', line 68

def superclass_of(constant)
  SUPERCLASS_METHOD.bind_call(constant)
end