4 ms·
> Before calling the method_missing method itself, Ruby sets a thread-local variable called call_status that reflects whether or not the original call was a vca
by jey 17y ago
> Before calling the method_missing method itself, Ruby sets a thread-local variable called call_status that reflects whether or not the original call was a vcall or a normal call.
Yuck, MRI (Matz's Ruby Interpreter) uses a global variable for that?
(Yes OK, it's actually thread-local, but that's effectively the same thing.) Awful style begets awful bugs. That's what you get for having such a messed up interface.
- wycats 17y agoHa. I knew someone was going to use this post as an excuse to wail on Ruby. No other Ruby VM uses a global (or thread-local) for this problem; it's not necessary to implement the functionality. While we were working through the problem, Evan (who works on Rubinius, another Ruby VM) remarked how much better the other implementations (like JRuby and Rubinius) handled the same problem.
- jey 17y agoI meant "MRI" and not "Ruby". It's clearly just an implementation detail and not mandated by the language itself. I'll change the original post to clarify this. If we're going to be pedantic, it's a "Ruby Interpreter" or "Ruby implementation", but not "Ruby VM". Ruby implementations can use a VM-based design, but it's certainly not required.
- wycats 17y agoThanks! Ruby 1.9 and Rubinius are both "Ruby VMs", while Ruby 1.8 is an interpreter, and IronRuby/JRuby leverage existing VMs. In this case, Evan, who works on a Ruby VM, was looking at Ruby 1.9, another Ruby VM ;)
- holygoat 17y agoI agree with you, but out of interest: how would you do it? I'd be tempted to ask "why distinguish between vcalls and normal calls?", but in general I don't think it's possible to build a system that is completely non-ugly, and all areas of ugliness have the potential for bugs.
- jey 17y agoSimplest approach: add a parameter to the call-dispatch machinery. For example, let's say you have functions op_call() and op_vcall() corresponding to the bytecodes or AST nodes representing normal calls and vcalls respectively, and these methods work by calling the lower-level internal method dispatch_call(). What they're doing right now is having call() and vcall() set a global that is then inspected later on by dispatch_call() to select the correct error to raise. I'd say that the better thing to do is to leverage the call stack and just turn it into a parameter that's passed to dispatch_call(). That way you can't forget to set it before dispatch_call(), and you get cleanup for free when the stack gets unwound. (Even if you end up longjmp()ing out of the call due to an exception or whatever!) I'm sure the above is a simplification of the actual scenario in MRI, but I bet the same general schematic can be applied, even if the implementation details are a bit more complicated in a real-world interpreter like MRI.