Devious or what? : hacking super : should this work?

I’ve discovered that an unadorned call to “super” need not call the
parent method with the same arguments!!! If you bind the entire
argument list to a variable you can modify the list before calling
“super”. Check it out:

class Show
  def initialize(*args)
puts args.inspect
  end
end

Show.new(1,34,"asd")

class Show2 < Show
  def initialize(*args)
puts args.shift
super
  end
end

Show2.new(1,34,"asd")

===>

[1, 34, "asd"]
1
[34, "asd"]

So “args” is bound to the actual array of arguments, not just a
copy. Is this a bug or a feature? Might this behaviour change in
future?

Regards,

Jeremy H.

Hi –

On Sun, 16 Jul 2006, Jeremy H. wrote:

===>

[1, 34, “asd”]
1
[34, “asd”]

So “args” is bound to the actual array of arguments, not just a
copy. Is this a bug or a feature? Might this behaviour change in
future?

I assume it’s a feature. super should be pretty transparent; for
example, if an argument is for some reason going to be altered in
place by the original method, you want that to still happen when you
call the method that calls super.

David

On 2006-07-15, [email protected] [email protected] wrote:

On Sun, 16 Jul 2006, Jeremy H. wrote:

If you bind the entire argument list to a variable you can modify
the list before calling “super”. Check it out:

… super should be pretty transparent; for example, if an argument
is for some reason going to be altered in place by the original
method, you want that to still happen when you call the method that
calls super.

True, but that’s not what surprised me. Given “def foo(*args) …” I
always expected the members of args to be the arguments of the
method call (and not copies of them). I did not expect args itself
to be part of the Ruby call stack. I assumed it was just a temporary
object created on the fly to give me access to the method arguments.
I thought that nothing else in Ruby had a reference to it, so I
expected that modifying args itself (rather than its members) would
have no effect outside of the current scope.

But I was wrong. It looks like args is the actual array of method
arguments in the Ruby call stack. Modifying args changes the call
stack, causing super to supply a different list of objects to the
parent method.

Regards,

Jeremy H.

On 2006-07-16, Dominik B. [email protected] wrote:

example with 1.8.4, only with 1.9.
I am running 1.8.5-preview1 . If I downgrade to 1.8.4 I get the
behaviour I first expected. So my expectations were not that
ridiculous, which is comforting. And I guess the answer to my
question “might this behaviour change” is clearly “yes”! (Which means
I have some code to rewrite, /me sighs.)

end

B.new.foo(1,2,3)

Under 1.8.4 :
[1, 2, 3]
-605563672
[1, 2, 3]
-605563762

Under 1.8.5 :
[1, 2, 3]
-605295548
[2, 3]
-605295638

Is this an intentional change or a side-effect of something else?

And even in 1.9 it actually is a different Ruby array:

Yes, the array is always a new VALUE (with a different
(RARRAY(self))->ptr). What’s changed is the relation between the C
array of method arguments that is copied when we call super, and the C
array that is wrapped by args. It seems that in 1.8.4 the latter is a
copy of the former, so the call to super gets a copy of the array that
was originally passed to the current method, even if the contents of
args have subsequently changed. In 1.8.5 they look to be the same, so
the call to super gets a copy of the current contents of args.

Regards,

Jeremy H.

On Sat, 15 Jul 2006 23:15:05 +0200, Jeremy H. [email protected]
wrote:

True, but that’s not what surprised me. Given “def foo(*args) …” I
always expected the members of args to be the arguments of the
method call (and not copies of them). I did not expect args itself
to be part of the Ruby call stack. I assumed it was just a temporary
object created on the fly to give me access to the method arguments.

This definitely is the case for Ruby 1.8 and I can’t reproduce your
example with 1.8.4, only with 1.9. I am not sure why this was changed.

In 1.8 the arguments are stored in a C array in the FRAME struct and
when
splatting is used a new Ruby array is created every time:

$ cat super_splat.rb
class A
def foo(*a); p a, a.object_id; end
end

class B < A
def foo(*a); p a, a.object_id; a.shift; super; end
end

B.new.foo(1,2,3)

$ ruby super_splat.rb
[1, 2, 3]
-604430366
[1, 2, 3]
-604430956

And even in 1.9 it actually is a different Ruby array:

$ ruby19 super_splat.rb
[1, 2, 3]
-604528228
[2, 3]
-604528338

Dominik

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs