Modify code at runtime

Hi,

I want to be able to modify Ruby code at runtime. I know that it is
possible to add classes/methods at runtime. But that is not what I want.
I want to insert statements or method calls at runtime. For example if I
have a method:

def foo a
x = a + 5
end

I want to change it at runtime to:

def foo a
puts a
x = a + 5
end

That means a Ruby application has to be able to modify itself. I am
pretty sure that this is not trivial.
My approach would be as follows:

  1. Learn how MRI works internally. I was already able to download and
    compile the interpreter. But I don’t know any good sources which
    describes how the interpeter works internally. A book that I have came
    across is “Ruby under a microscope”
    (Pat Shaughnessy). Is this book
    recommendable?

  2. Learn how YARV Bytecode works. Maybe I can add/modify the bytecode
    instructions at runtime. But I don’t know how complicated this is.

Another approach that came into my mind would be to intercept the
loader, inject my statements (like “puts a” above) and send the modified
file to the loader. The problem is that this can’t be done at runtime,
since a ruby script gets loaded only once when the application starts.

What do you think?

On Wed, Mar 5, 2014 at 7:31 AM, Martin E. [email protected]
wrote:

x = a + 5
end

That means a Ruby application has to be able to modify itself. I am
pretty sure that this is not trivial.

Depends how you do it. Your proposed approach is “not trivial”, and
also not portable. Instead, you could rename foo to old_foo, and
create a new foo that does “puts a” and calls old_foo(a). Heck, you
could even have it “puts x” afterward (assuming x is defined in some
wrapping scope). For as simple a situation as this, I’d call that
trivial.

-Dave

Thanks for the reply Dave. Why is it not portable? Do you mean with
portable that it isn’t possible to give the modified ruby code to
someone else? That would be true of course. But that’s not a problem.
With renaming do you mean defining a new method with alias and
alias_method? I considered that already. But the thing is that I don’t
want the user having to change the ruby source code.

I want the following: Let’s say a system administrator has a Rails
application and wants to know how long a certain method call inside the
Rails application lasts. Now he has my tool which adds Timer.start and
Timer.End at the start and end before and after the method call
respectively. When he does not want this instrumentation any longer than
the can switch it simply off. The system administrator shouldn’t have to
change the Rails app source code.

On Wed, Mar 5, 2014 at 1:31 PM, Martin E. [email protected]
wrote:

def foo a
puts a
x = a + 5
end

This specific example could be done with method decoration. I once
hacked a crude piece of Ruby code together that is able to add pre,
mid and post execute hooks to methods. Maybe you can find it in the
archives.

  1. Learn how YARV Bytecode works. Maybe I can add/modify the bytecode
    instructions at runtime. But I don’t know how complicated this is.

You do not need that: meta programming is sufficient.

Another approach that came into my mind would be to intercept the
loader, inject my statements (like “puts a” above) and send the modified
file to the loader. The problem is that this can’t be done at runtime,
since a ruby script gets loaded only once when the application starts.

What do you think?

You can redefine the method and delegate to the old one. Simplistic
code that would work for your case:

class Module
def prefix_method(method, &code)
tmp = instance_method(method)

define_method(method) do |*args, &b|
  code[*args]
  tmp.bind(self).call(*args, &b)
end

end
end

class Foo
def out(x)
printf “out: <%s>\n”, x
yield x if block_given?
end
end

f = Foo.new
f.out 123

class Foo
prefix_method :out do |*args|
printf “before out: <%p>\n”, args
end
end

f.out 456

f.out 789 do |x|
printf “even with block in out: <%p>\n”, x
end

Kind regards

robert

I have already looked at how New Relic does that: They do it pretty the
same way Robert K. described above. You have to change the Ruby
source code if you want to instrument a method. Here is an example:

require ‘new_relic/agent/method_tracer’
class Foo
include ::NewRelic::Agent::MethodTracer

def generate_image

end

add_method_tracer :generate_image, ‘Custom/generate_image’
end

So this does not help me a lot.

So how exactly do you expect the “system administrator” to indicate
which
method he wants wrapped? Somewhere it would appear that they would need
to
declare the class/method desired.

Now for a second take your New Relic example and think just an inch
outside
of the box

File A.rb

class Foo
def generate_image

end
end

New file Wrappers.rb written by the system administrator to use your
tool

require ‘my_great_new_tool’
class Foo
include ::MyGreatNewTool::Wrapper

add_wrapper :generate_image, “Hi kids”
end

Well look at that - I haven’t touched A.rb but I’ve just wrapped my
proc.
And of course it could be as simple as a config file that listed
Foo::generate_image as the method that wrapping was desired for and then
your tool could wrap without them writing Wrappers.rb but that’s another
exercise.

But somewhere the end user has to tell you what they want wrapped and
there
is no reason why you can’t wrap the methods outside of the original
source
code.

John

A very good idea! I will try that out next week and will tell you if
that solves the problem.

On Wed, Mar 5, 2014 at 8:59 AM, Martin E. [email protected]
wrote:

Thanks for the reply Dave. Why is it not portable?

As in, if you do it by messing with how MRI’s C code works, you almost
certainly won’t be able to use the same technique on, say, JRuby, or
Rubinius, or MRuby, or whatever else, other than MRI.

That would be true of course. But that’s not a problem.
With renaming do you mean defining a new method with alias and
alias_method?

Right.

Let’s say a system administrator has a Rails
application and wants to know how long a certain method call inside the
Rails application lasts. Now he has my tool which adds Timer.start and
Timer.End at the start and end before and after the method call
respectively. When he does not want this instrumentation any longer than
the can switch it simply off. The system administrator shouldn’t have to
change the Rails app source code.

Ah! Hmmm. Maybe you could look into how things like New Relic wrap
calls, apply a similar technique at the server level, and have some
kind of configuration in the server turn it on and off.

-Dave