Class: AppMap::Hook::Method
Direct Known Subclasses
Constant Summary collapse
- ARRAY_OF_EMPTY_HASH =
[{}.freeze].freeze
Instance Attribute Summary collapse
-
#hook_class ⇒ Object
readonly
Returns the value of attribute hook_class.
-
#hook_method ⇒ Object
readonly
Returns the value of attribute hook_method.
-
#hook_package ⇒ Object
readonly
Returns the value of attribute hook_package.
-
#method_display_name ⇒ Object
readonly
method_display_namemay be nil if name resolution gets deferred until runtime (e.g. for a singleton method on an embedded Struct).
Instance Method Summary collapse
- #activate ⇒ Object
-
#initialize(hook_package, hook_class, hook_method) ⇒ Method
constructor
A new instance of Method.
Constructor Details
#initialize(hook_package, hook_class, hook_method) ⇒ Method
Returns a new instance of Method.
45 46 47 48 49 50 51 52 53 |
# File 'lib/appmap/hook/method.rb', line 45 def initialize(hook_package, hook_class, hook_method) @hook_package = hook_package @hook_class = hook_class @hook_method = hook_method # Get the class for the method, if it's known. @defined_class, method_symbol = Hook.qualify_method_name(@hook_method) @method_display_name = [@defined_class, method_symbol, @hook_method.name].join if @defined_class end |
Instance Attribute Details
#hook_class ⇒ Object (readonly)
Returns the value of attribute hook_class.
28 29 30 |
# File 'lib/appmap/hook/method.rb', line 28 def hook_class @hook_class end |
#hook_method ⇒ Object (readonly)
Returns the value of attribute hook_method.
28 29 30 |
# File 'lib/appmap/hook/method.rb', line 28 def hook_method @hook_method end |
#hook_package ⇒ Object (readonly)
Returns the value of attribute hook_package.
28 29 30 |
# File 'lib/appmap/hook/method.rb', line 28 def hook_package @hook_package end |
#method_display_name ⇒ Object (readonly)
method_display_name may be nil if name resolution gets
deferred until runtime (e.g. for a singleton method on an
embedded Struct).
33 34 35 |
# File 'lib/appmap/hook/method.rb', line 33 def method_display_name @method_display_name end |
Instance Method Details
#activate ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/appmap/hook/method.rb', line 55 def activate if Hook::LOG msg = if method_display_name "#{method_display_name}" else "#{hook_method.name} (class resolution deferred)" end warn "AppMap: Hooking #{msg} at line #{(hook_method.source_location || []).join(':')}" end defined_class = @defined_class hook_package = self.hook_package hook_method = self.hook_method before_hook = self.method(:before_hook) after_hook = self.method(:after_hook) with_disabled_hook = self.method(:with_disabled_hook) is_array_containing_empty_hash = ->(obj) { obj.is_a?(Array) && obj.length == 1 && obj[0].is_a?(Hash) && obj[0].size == 0 } call_instance_method = lambda do |receiver, args, &block| # https://github.com/applandinc/appmap-ruby/issues/153 if NEW_RUBY && is_array_containing_empty_hash.(args) && hook_method.arity == 1 hook_method.bind_call(receiver, {}, &block) else if NEW_RUBY hook_method.bind_call(receiver, *args, &block) else hook_method.bind(receiver).call(*args, &block) end end end hook_method_def = Proc.new do |*args, &block| # We may not have gotten the class for the method during # initialization (e.g. for a singleton method on an embedded # struct), so make sure we have it now. defined_class, = Hook.qualify_method_name(hook_method) unless defined_class reentrant = Thread.current[HOOK_DISABLE_KEY] disabled_by_shallow_flag = \ -> { hook_package&.shallow? && AppMap.tracing.last_package_for_current_thread == hook_package } enabled = true if AppMap.tracing.enabled? && !reentrant && !disabled_by_shallow_flag.call enabled = false if %i[instance_eval instance_exec].member?(hook_method.name) && args.empty? return call_instance_method.call(self, args, &block) unless enabled call_event, start_time = with_disabled_hook.call do before_hook.call(self, defined_class, args) end return_value = nil exception = nil begin return_value = call_instance_method.call(self, args, &block) rescue exception = $ERROR_INFO raise ensure with_disabled_hook.call do after_hook.call(self, call_event, start_time, return_value, exception) if call_event end end end hook_method_def = hook_method_def.ruby2_keywords if hook_method_def.respond_to?(:ruby2_keywords) hook_method_parameters = hook_method.parameters.dup.freeze SIGNATURES[[ hook_class, hook_method.name ]] = hook_method_parameters # irb(main):001:0> Kernel.public_instance_method(:system) # (irb):1:in `public_instance_method': method `system' for module `Kernel' is private (NameError) if hook_class == Kernel hook_class.define_method_with_arity(hook_method.name, hook_method.arity, hook_method_def) else hook_class.ancestors.find { |cls| cls.method_defined?(hook_method.name, false) }.tap do |cls| if cls cls.define_method_with_arity(hook_method.name, hook_method.arity, hook_method_def) else warn "#{hook_method.name} not found on #{hook_class}" end end end end |