This post originated from an RSS feed registered with Ruby Buzz
by Eric Hodel.
Original Post: Binding#remove_local_variable
Feed Title: Segment7
Feed URL: http://blog.segment7.net/articles.rss
Feed Description: Posts about and around Ruby, MetaRuby, ruby2c, ZenTest and work at The Robot Co-op.
require 'rubygems'
require 'inline'
class Binding
inline do |builder|
builder.include '"node.h"'
builder.include '"env.h"'
##
# struct BLOCK isn't in any header, so include it here.
builder.prefix <<-C
struct BLOCK {
NODE *var;
NODE *body;
VALUE self;
struct FRAME frame;
struct SCOPE *scope;
VALUE klass;
NODE *cref;
int iter;
int vmode;
int flags;
int uniq;
struct RVarmap *dyna_vars;
VALUE orig_thread;
VALUE wrapper;
VALUE block_obj;
struct BLOCK *outer;
struct BLOCK *prev;
};
C
##
# :method: remove_local_variable
#
# Removes the local variable +name+ and replaces it with nil.
#
# Ordinarily if a local variable didn't exist it would raise
# NoMethodError, but currently after #remove_local_variable it doesn't, it
# just returns nil.
#
# In order to make this behave correctly, block->body would need to be
# walked, duping nodes (so as not to affect future invocations of this
# method) and replacing the LVAR nodes with VCALL nodes.
#
# That's just too much work for 17:15, though. Maybe tomorrow.
builder.c <<-C
VALUE remove_local_variable(VALUE name) {
struct BLOCK *block;
struct SCOPE *scope;
ID name_id;
ID *local_table;
int i, n;
VALUE entry;
name_id = SYM2ID(name);
Data_Get_Struct(self, struct BLOCK, block);
scope = block->scope;
local_table = scope->local_tbl;
if (local_table) {
n = *local_table++;
for (i = 2; i < n; i++) { /* skip $_ and $~ */
if (!rb_is_local_id(local_table[i])) continue; /* skip flip states */
if (local_table[i] == name_id) {
entry = scope->local_vars[i];
local_table[i] = (ID)NULL;
scope->local_vars[i] = Qnil;
return entry;
}
}
}
return Qnil;
}
C
end
end
a = :my_value
p :lvar_a => a
b = binding
p :remove_local_variable_says => b.remove_local_variable(:a)
p :lvar_a => a # TODO raise exception