instance_exec

A

ara.t.howard

i seems to recall someone came up with an impl of instance_exec (instance_eval
that takes args) a while back - might have even been me! ;-) anyone remember?

-a
 
M

Marcel Molina Jr.

i seems to recall someone came up with an impl of instance_exec
(instance_eval
that takes args) a while back - might have even been me! ;-) anyone
remember?

There is one in Rails' ActiveSupport:

class Object
unless defined? instance_exec # 1.9
def instance_exec(*arguments, &block)
block.bind(self)[*arguments]
end
end
end

class Proc
def bind(object)
block, time = self, Time.now
(class << object; self end).class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end

marcel
 
A

ara.t.howard

i seems to recall someone came up with an impl of instance_exec
(instance_eval
that takes args) a while back - might have even been me! ;-) anyone
remember?

There is one in Rails' ActiveSupport:

class Object
unless defined? instance_exec # 1.9
def instance_exec(*arguments, &block)
block.bind(self)[*arguments]
end
end
end

class Proc
def bind(object)
block, time = self, Time.now
(class << object; self end).class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end

thanks! i just hacked this one out:

harp:~ > cat a.rb
class Object
unless instance_methods.include? 'instance_exec'
def instance_exec *a, &b
m = "__instance_exec_#{ Thread.current.object_id.abs }__"
singleton_class{ define_method m, &b }
send m, *a
ensure
singleton_class{ undef_method m } rescue nil
end
end
unless instance_methods.include? 'singleton_class'
def singleton_class &b
sc = class << self; self; end
sc.module_eval &b if b
sc
end
end
end

a = []
a.instance_exec 42 do |elem|
push elem
end
p a

harp:~ > ruby a.rb
[42]

regards.

-a
 
S

Sean O'Halpin

i seems to recall someone came up with an impl of instance_exec
(instance_eval
that takes args) a while back - might have even been me! ;-) anyone
remember?

There is one in Rails' ActiveSupport:

class Object
unless defined? instance_exec # 1.9
def instance_exec(*arguments, &block)
block.bind(self)[*arguments]
end
end
end

class Proc
def bind(object)
block, time = self, Time.now
(class << object; self end).class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end

marcel
This is not thread-safe - relying on time.usec may not work on
sufficiently fast or clock-impaired machines.

Regards,
Sean
 
T

transfire

Marcel said:
i seems to recall someone came up with an impl of instance_exec
(instance_eval
that takes args) a while back - might have even been me! ;-) anyone
remember?

There is one in Rails' ActiveSupport:

class Object
unless defined? instance_exec # 1.9
def instance_exec(*arguments, &block)
block.bind(self)[*arguments]
end
end
end

class Proc
def bind(object)
block, time = self, Time.now
(class << object; self end).class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end

Huh. That looks like the one I wrote a while back (minus the time
stamp).

Mauricio Fernandez's looks better though --think I'll have to rip it
for Facets ;-)

T.
 
S

Sean O'Halpin

Huh. That looks like the one I wrote a while back (minus the time
stamp).

Mauricio Fernandez's looks better though --think I'll have to rip it
for Facets ;-)

T.
Do - it's the best version for 1.8 so far (IMHO).

Regards,
Sean
 
A

ara.t.howard

Mauricio Fernandez's looks better though --think I'll have to rip it
for Facets ;-)

i just came across a bug in it (it's not re-entrant) - here's the fix:

harp:~ > cat a.rb
class Object
unless instance_methods.include? 'instance_exec'
def instance_exec *a, &b
m, n = nil, -1
loop{
m = "__instance_exec_#{ Thread.current.object_id.abs }_#{ n += 1 }__"
break unless respond_to? m
}
singleton_class{ define_method m, &b }
send m, *a
ensure
singleton_class{ undef_method m }
end
end

unless instance_methods.include? 'singleton_class'
def singleton_class &b
sc =
class << self; self; end
sc.module_eval &b if b
sc
end
end
end

a = []

a.instance_exec 42 do |x|
instance_exec x do |y|
push y
p size
end
end


harp:~ > ruby a.rb
1


figured you'd use this if anyone would tom... cheers.


-a
 
M

Mauricio Fernandez

i just came across a bug in it (it's not re-entrant) - here's the fix:
[#instance_exec implementation]

The example you gave does work with my implementation too:

RUBY_VERSION # => "1.8.5"
RUBY_RELEASE_DATE # => "2006-06-24"
class Object
module InstanceExecHelper; end
include InstanceExecHelper
def instance_exec(*args, &block)
mname = "__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}"
InstanceExecHelper.module_eval{ define_method(mname, &block) }
begin
ret = send(mname, *args)
ensure
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
# ==========
# thx to this
end
ret
end
end

a = []

a.instance_exec 42 do |x|
instance_exec x do |y|
push y
p size
end
end

a # => [42]
a.size # => 1
# >> 1


I had actually a test for that (see test_instance_exec_nested below). Do you
have any additional assertions handy?
(So far test_instance_exec_with_frozen_obj fails in all the other
implementations I've seen.)

require 'test/unit'
class TestInstanceEvalWithArgs < Test::Unit::TestCase
def test_instance_exec
# Create a block that returns the value of an argument and a value
# of a method call to +self+.
block = lambda { |a| [a, f] }

assert_equal [:arg_value, :dummy_value],
Dummy.new.instance_exec:)arg_value, &block)
end

def test_instance_exec_with_frozen_obj
block = lambda { |a| [a, f] }

obj = Dummy.new
obj.freeze
assert_equal [:arg_value, :dummy_value],
obj.instance_exec:)arg_value, &block)
end

def test_instance_exec_nested
i = 0
obj = Dummy.new
block = lambda do |arg|
[arg] + instance_exec(1){|a| [f, a] }
end

# the following assertion expanded by the xmp filter automagically from:
# obj.instance_exec:)arg_value, &block) #=>
assert_equal([:arg_value, :dummy_value, 1], obj.instance_exec:)arg_value, &block))
end
end
 
E

Erik Veenstra

The example you gave does work with my implementation too:
RUBY_VERSION # => "1.8.5"
RUBY_RELEASE_DATE # => "2006-06-24"
class Object
module InstanceExecHelper; end
include InstanceExecHelper
def instance_exec(*args, &block)
mname = "__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}"

If instance_exec is called recursively, like in the example,
the mname for the outer method is reused for defining the inner
method. This isn't really a problem, but conceptually not very
"clean"...

Did it work "by accident", or did you really intent to reuse
the name? ;]
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil

This "rescue nil" might have been an indication of this "bad
design"... ;]

I wonder, why did you use a module for the context, instead of
the good old meta class?

gegroet,
Erik V. - http://www.erikveen.dds.nl/
 
E

Erik Veenstra

Okay, criticizing somebody's work without providing a
"solution", isn't nice at all...

So, here's my version...

gegroet,
Erik V. - http://www.erikveen.dds.nl/

PS: I'm still curious about the reason for using a module.

----------------------------------------------------------------

class Object
def instance_exec(*args, &block)
object = self
res = nil

class << self
self
end.instance_eval do
begin
Thread.critical = true
@_instance_exec_count_ ||= 0
@_instance_exec_count_ += 1
method_name = "_instance_exec_#{@_instance_exec_count_}_"
ensure
Thread.critical = false
end

begin
define_method(method_name, &block)

res = object.send(method_name, *args)
ensure
remove_method(method_name) rescue nil
end
end

res
end
end

----------------------------------------------------------------
 
T

transfire

Erik said:
Okay, criticizing somebody's work without providing a
"solution", isn't nice at all...

So, here's my version...

gegroet,
Erik V. - http://www.erikveen.dds.nl/

PS: I'm still curious about the reason for using a module.

I think the idea was to save frm creting a singleton class, but at the
expense of a module I don't see why either. Anyway, here the version I
put in Facets. Please evaluate.

module Kernel

# Like instace_eval but allows parameters to be passed.
#
# The implementation is thread-safe thanks to the
# Thread.current.object_id trick, but it doesn't work with
# immediate values (Fixnums and friends), and most
# importantly it bombs when given a frozen object.

def instance_exec(*args, &block)
mname =
"__instance_exec(#{Thread.current.object_id},#{caller.object_id})"
Object.class_eval{ define_method(mname, &block) }
begin
ret = send(mname, *args)
ensure
Object.class_eval{ undef_method(mname) }
end
ret
end

end
 
T

transfire

Erik said:
Anyway, here the version I put in Facets. Please evaluate.

# The implementation is thread-safe thanks to the
# Thread.current.object_id trick, but it doesn't work with
# immediate values (Fixnums and friends),...

(You're version _does_ work with immediate versions.)

Mmh... The Module version does work for immediate versions,
too. That might be a good reason... ;]

Mine doesn't...
# ...and most
# importantly it bombs when given a frozen object.

Object.class_eval{ define_method(mname, &block) }

This pollutes the global class Object. Not funny... ;] But,
yes, it does work...

It should delete it when done, so it's a temporary pollution. But I
think to do that right, instead of undef_method that should be
remove_method:

Object.class_eval{ remove_method(mname) }

Good catch thanks.

T.
 
A

ara.t.howard

I think the idea was to save frm creting a singleton class, but at the
expense of a module I don't see why either. Anyway, here the version I
put in Facets. Please evaluate.

module Kernel

# Like instace_eval but allows parameters to be passed.
#
# The implementation is thread-safe thanks to the
# Thread.current.object_id trick, but it doesn't work with
# immediate values (Fixnums and friends), and most
# importantly it bombs when given a frozen object.

def instance_exec(*args, &block)
mname =
"__instance_exec(#{Thread.current.object_id},#{caller.object_id})"
Object.class_eval{ define_method(mname, &block) }
begin
ret = send(mname, *args)
ensure
Object.class_eval{ undef_method(mname) }
end
ret
end

end

-a
 
T

transfire

Duh! I'm sitting here screwing with this and the answer's sitting one
directory over.. with only a minor modification (I added recv=nil) I
introduce you to Facets' Proc#to_method:

#--
# Credit for #to_method goes to Forian Gross (flgr).
#--

require 'thread'

class Proc

MethodMutexes = Hash.new do |hash, key|
hash[key] = Mutex.new
end

# Creates a local method based on a Proc.
def to_method(name=nil, recv=nil)
name ||= "!to_method_temp#{self.object_id}"
recv ||= eval("self", self)
klass = recv.class
MethodMutexes[klass => name].synchronize do
begin
klass.send:)define_method, name, &self)
return recv.method(name)
ensure
klass.send:)remove_method, name) if not name
end
end
end

end

Then instance_exec is simply:

module Kernel

def instance_exec(*args, &block)
block.to_method(nil,self).call(*args)
end

end

Proof is in the pudding. Facets' really does pay off!

T.
 
M

Mauricio Fernandez

* it allows instance_exec to work on frozen objects and immediate values
* it doesn't pollute Object with several methods (even if temporarily), only
one module
I think the idea was to save frm creting a singleton class, but at the
expense of a module I don't see why either. Anyway, here the version I
put in Facets. Please evaluate.

module Kernel

# Like instace_eval but allows parameters to be passed.
#
# The implementation is thread-safe thanks to the
# Thread.current.object_id trick, but it doesn't work with
# immediate values (Fixnums and friends), and most
# importantly it bombs when given a frozen object.
def instance_exec(*args, &block)
mname =
"__instance_exec(#{Thread.current.object_id},#{caller.object_id})"
Object.class_eval{ define_method(mname, &block) }
begin
ret = send(mname, *args)
ensure
Object.class_eval{ undef_method(mname) }
end
ret
end

end

heh you copied the comment from my blog entry[1] but that's not the
implementation it applied to ;). The above does work with immediate values and
frozen objects. But I now consider it flawed nonetheless, read

http://eigenclass.org/hiki.rb?bounded+space+instance_exec

[1] http://eigenclass.org/hiki.rb?instance_exec
 
T

transfire

Okay, I think I've improved it some more:

require 'thread'

class Proc
MethodMutexes = Hash.new do |hash, key|
hash[key] = Mutex.new
end

# Creates a local method based on a Proc.
def to_method(name=nil, recv=nil)
unless name
n = 0
n += 1 while recv.respond_to?(name = "__to_method_#{n}")
end
recv ||= eval("self", self)
klass = recv.class
MethodMutexes[klass => name].synchronize do
begin
klass.send:)define_method, name, &self)
return recv.method(name)
ensure
klass.send:)remove_method, name) unless name
end
end
end
end
This can create a lot of different symbols, leading to large memory leaks.
recv ||= eval("self", self)
klass = recv.class
MethodMutexes[klass => name].synchronize do
============================

This makes the memleak even heavier, going from potentially ~70 bytes to
nearly 300 bytes per call...

I don't understand this though. isn't the memory freed when done? Or is
some symbol getting created in the process, or ?
==========

This will fail if the class was frozen.

Ah, I see if the _klass_ is frozen. But that will fail regardless won't
it? You can;t even include a module (ef InstanceExecHelper) in a frozen
class. Right?

Thanks,
T.
 
E

Erik Veenstra

I don't understand this though. isn't the memory freed when
define_method creates a symbol, beneath the surface:

symbols1 = Symbol.all_symbols
Module.new.instance_eval{define_method("bar"){}}
symbols2 = Symbol.all_symbols
p symbols2-symbols1 # ===> [:bar]
Yes, that's why InstanceExecHelper must be included in Object
(and you only have to do that once).

You should include the Module before Object gets frozen. Who
wants to freeze Object, anyway?... ;]
Thread.critical = old

AFAIK, we need to be in critical mode when determining the
method name. As soon as we know it, we can (and should...)
return to old_critical. At least before we call the
block/method.
module Kernel
def instance_exec(*args, &block)

Kernel#instance_exec ? Does that make sense?

About $SAFE: It's possible to do an instance_eval up to level
5, whereas instance_exec fails on level 4. Challenge?... ;]

gegroet,
Erik V. - http://www.erikveen.dds.nl/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,208
Messages
2,571,082
Members
47,683
Latest member
AustinFairchild

Latest Threads

Top