Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Vastly simplify and fix constant lookup

* New constant lookup specs to test behavior
* Added StaticScope object and field on CompiledMethod which stores
  a StaticScope instance which indicates the lexical scope of the CM.
  • Loading branch information...
commit 7010073617a4fa95ea5491284fef97a083d9d4f3 1 parent 163e566
@evanphx evanphx authored
View
10 Rakefile
@@ -510,6 +510,16 @@ RbConfig = Config
# Note: Steps to rebuild load_order were defined above
end
+ namespace :vm do
+ task "clean" do
+ sh "cd shotgun/lib; make clean"
+ end
+
+ task "dev" do
+ sh "cd shotgun/lib; make DEV=1"
+ end
+ end
+
end
desc "Build task for CruiseControl"
View
7 compiler/bytecode/system_hints.rb
@@ -11,7 +11,7 @@ module Bootstrap
"Float"=>{:@__ivars__=>0},
"Array"=>{:@total=>0, :@tuple=>1, :@start => 2, :@shared => 3},
"String"=>{:@bytes=>0, :@characters=>1, :@encoding=>2, :@data=>3, :@hash => 4, :@shared => 5},
- "CompiledMethod"=>{:@__ivars__=>0, :@primitive => 1, :@required=>2, :@serial=>3, :@bytecodes=>4, :@name=>5, :@file=>6, :@locals=>7, :@literals=>8, :@arguments=>9, :@scope=>10, :@exceptions=>11, :@lines=>12, :@path=>13, :@cache=>14, :@bonus => 15, :@compiled => 16},
+ "CompiledMethod"=>{:@__ivars__=>0, :@primitive => 1, :@required=>2, :@serial=>3, :@bytecodes=>4, :@name=>5, :@file=>6, :@locals=>7, :@literals=>8, :@arguments=>9, :@scope=>10, :@exceptions=>11, :@lines=>12, :@path=>13, :@cache=>14, :@bonus => 15, :@compiled => 16, :@staticscope => 17},
"SymbolTable"=>{:@__ivars__=>0,:@symbols=>1, :@strings=>2},
"IO"=>{:@__ivars__ => 0, :@descriptor => 1, :@buffer => 2, :mode => 3 },
"Module"=>{:@__ivars__=>0, :@methods=>1, :@method_cache=>2, :@name=>3, :@constants=>4, :@parent=>5, :@superclass => 6},
@@ -19,7 +19,8 @@ module Bootstrap
"Hash"=>{:@__ivars__=>0, :@keys=>1, :@values=>2,:@bins=>3, :@entries=>4, :@default=>5, :@default_proc=>6},
"BlockEnvironment"=>{:@__ivars__=>0, :@home=>1, :@initial_ip=>2, :@last_ip=>3, :@post_send=>4, :@home_block => 5, :@local_count => 6, :@bonus => 7, :@method => 8},
"Exception" => {:@__ivars__ => 0, :@message => 1, :@context => 2 },
- "InlineCache" => {:@__ivars__ => 0, :@method => 1, :@class => 2, :@module => 3, :@serial => 4, :@hotness => 5, :@trip => 6 }
+ "InlineCache" => {:@__ivars__ => 0, :@method => 1, :@class => 2, :@module => 3, :@serial => 4, :@hotness => 5, :@trip => 6 },
+ "StaticScope" => {:@__ivars__ => 0, :@module => 1, :@parent => 2 }
}
TYPES = {:symtbl=>"SymbolTable", :blank=>"BlankObject", :matchdata=>"MatchData",
@@ -29,7 +30,7 @@ module Bootstrap
:regexpdata=>"RegexpData", :regexp=>"Regexp", :bytearray=>"ByteArray",
:hash=>"Hash", :string=>"String", :tuple=>"Tuple", :blokenv=>"BlockEnvironment",
:symbol=>"Symbol", :methctx=>"MethodContext", :exception => "Exception",
- :icache => "InlineCache"
+ :icache => "InlineCache", :staticscope => "StaticScope"
}
end
end
View
15 kernel/core/compiled_method.rb
@@ -16,8 +16,20 @@ def line_from_ip(i)
end
end
+class StaticScope
+ ivar_as_index :__ivars__ => 0, :module => 1, :parent => 2
+
+ def module
+ @module
+ end
+
+ def parent
+ @parent
+ end
+end
+
class CompiledMethod
- ivar_as_index :__ivars__ => 0, :primitive => 1, :required => 2, :serial => 3, :bytecodes => 4, :name => 5, :file => 6, :locals => 7, :literals => 8, :arguments => 9, :scope => 10, :exceptions => 11, :lines => 12, :path => 13, :cache => 14, :bonus => 15, :compiled => 16
+ ivar_as_index :__ivars__ => 0, :primitive => 1, :required => 2, :serial => 3, :bytecodes => 4, :name => 5, :file => 6, :locals => 7, :literals => 8, :arguments => 9, :scope => 10, :exceptions => 11, :lines => 12, :path => 13, :cache => 14, :bonus => 15, :compiled => 16, :staticscope => 17
def __ivars__ ; @__ivars__ ; end
def primitive ; @primitive ; end
def required ; @required ; end
@@ -35,6 +47,7 @@ def path ; @path ; end
def cache ; @cache ; end
def bonus ; @bonus ; end
def compiled ; @compiled ; end
+ def staticscope; @staticscope; end
def inspect
"#<#{self.class.name}:0x#{self.object_id.to_s(16)} name=#{@name} file=#{@file}>"
View
2  shotgun/lib/bootstrap.c
@@ -65,6 +65,7 @@ void cpu_bootstrap(STATE) {
BC(io) = _io_class(state, obj);
BC(blokenv) = _blokenv_class(state, obj);
BC(icache) = _icache_class(state, obj);
+ BC(staticscope) = _staticscope_class(state, obj);
/* The symbol table */
state->global->symbols = symtbl_new(state);
@@ -86,6 +87,7 @@ void cpu_bootstrap(STATE) {
module_setup(state, BC(io), "IO");
module_setup(state, BC(blokenv), "BlockEnvironment");
module_setup(state, BC(icache), "InlineCache");
+ module_setup(state, BC(staticscope), "StaticScope");
rbs_const_set(state, obj, "Symbols", state->global->symbols);
BC(nil_class) = rbs_class_new(state, "NilClass", 0, obj);
View
154 shotgun/lib/cpu.c
@@ -49,6 +49,20 @@ void cpu_setup_top_scope(STATE, cpu c) {
c->new_class_of = state->global->class;
}
+OBJECT cpu_scope_push(STATE, cpu c, OBJECT mod) {
+ OBJECT scope = staticscope_allocate(state);
+ staticscope_set_module(scope, mod);
+ staticscope_set_parent(scope, c->current_scope);
+
+ c->current_scope = scope;
+ return scope;
+}
+
+OBJECT cpu_scope_pop(STATE, cpu c) {
+ c->current_scope = staticscope_get_parent(c->current_scope);
+ return c->current_scope;
+}
+
void cpu_initialize_context(STATE, cpu c) {
c->active_context = Qnil;
c->home_context = c->active_context;
@@ -91,6 +105,10 @@ void cpu_initialize_context(STATE, cpu c) {
c->current_task = cpu_thread_get_task(state, c->current_thread);
c->main_task = c->current_task;
+ c->current_scope = Qnil;
+ cpu_scope_push(state, c, BASIC_CLASS(object));
+ state->global->top_scope = c->current_scope;
+
cpu_event_setup_children(state, c);
}
@@ -138,6 +156,7 @@ void cpu_add_roots(STATE, cpu c, ptr_array roots) {
ar(c->outstanding);
ar(c->debug_channel);
ar(c->control_channel);
+ ar(c->current_scope);
len = ptr_array_length(c->paths);
ptr_array_append(roots, (xpointer)I2N(len));
// printf("Paths: %d\n", len);
@@ -201,6 +220,7 @@ void cpu_update_roots(STATE, cpu c, ptr_array roots, int start) {
ar(c->outstanding);
ar(c->debug_channel);
ar(c->control_channel);
+ ar(c->current_scope);
tmp = ptr_array_get_index(roots, start++);
len = FIXNUM_TO_INT((OBJECT)tmp);
for(i = 0; i < len; start++, i++) {
@@ -294,108 +314,46 @@ OBJECT cpu_new_exception(STATE, cpu c, OBJECT klass, const char *msg) {
return obj;
}
-/* FIXME: the inline caches of constants aren't flushed! */
-
-#define update_cache(val) // if(c->cache_index >= 0) tuple_put(state, cmethod_get_cache(cpu_current_method(state, c)), c->cache_index, val)
-
OBJECT cpu_const_get_in_context(STATE, cpu c, OBJECT sym) {
OBJECT cur, klass, start, hsh, val;
+ OBJECT cref, cbase;
+
+ c->cache_index = -1;
/* Look up the lexical scope first */
- cur = cpu_current_module(state, c);
-
- while(!NIL_P(cur) && cur != state->global->object) {
- // printf(" looking in %s\n", rbs_symbol_to_cstring(state, module_get_name(cur)));
-
- hsh = module_get_constants(cur);
- val = hash_find_undef(state, hsh, sym);
- if(val != Qundef) {
- // printf(" found!\n");
- return val;
- }
- cur = module_get_parent(cur);
- }
-
- /* If self is a module, we start with it, otherwise we start with
- self's class. */
- if(object_kind_of_p(state, c->self, state->global->module)) {
- klass = c->self;
+ cref = cmethod_get_staticscope(cpu_current_method(state, c));
+ if(NIL_P(cref)) {
+ start = state->global->object;
} else {
- klass = object_class(state, c->self);
- }
-
- if(sym == module_get_name(klass)) {
- // printf(" found, it's where you are.\n");
- return klass;
- }
-
- cur = cpu_current_module(state, c);
-
- /* If the current module is a metaclass, then .. */
- if(ISA(cur, state->global->metaclass)) {
- /* If the attached instance of the metaclass is a module...
- start from it */
- OBJECT inst = metaclass_get_attached_instance(cur);
- if(ISA(inst, state->global->module)) {
- cur = inst;
- }
- }
-
- while(!NIL_P(cur) && cur != state->global->object) {
- hsh = module_get_constants(cur);
- val = hash_find_undef(state, hsh, sym);
- if(val != Qundef) {
- // printf(" found!\n");
- return val;
- }
- cur = module_get_parent(cur);
- }
-
- start = cur = klass;
-
- // printf("Looking for %s in the current context.\n", rbs_symbol_to_cstring(state, sym));
-
-
- while(!NIL_P(cur) && cur != state->global->object) {
- // printf(" looking in %s\n", rbs_symbol_to_cstring(state, module_get_name(cur)));
+ cbase = cref;
+
+ while(!NIL_P(cbase)) {
+ klass = staticscope_get_module(cbase);
+ hsh = module_get_constants(klass);
+ val = hash_find_undef(state, hsh, sym);
+ if(val != Qundef) return val;
- hsh = module_get_constants(cur);
- val = hash_find_undef(state, hsh, sym);
- if(val != Qundef) {
- // printf(" found!\n");
- return val;
+ cbase = staticscope_get_parent(cbase);
}
- cur = module_get_superclass(cur);
- }
-
- cur = object_class(state, c->self);
- // printf("Couldn't find in lex scope. Looking up from %s\n", rbs_symbol_to_cstring(state, module_get_name(cur)));
- while(!NIL_P(cur) && cur != state->global->object) {
- // printf(" looking in %s\n", rbs_symbol_to_cstring(state, module_get_name(cur)));
+ start = cur = staticscope_get_module(cref);
+
+ while(!NIL_P(cur) && cur != state->global->object) {
- hsh = module_get_constants(cur);
- val = hash_find_undef(state, hsh, sym);
- if(val != Qundef) {
- // printf(" found!\n");
- return val;
+ hsh = module_get_constants(cur);
+ val = hash_find_undef(state, hsh, sym);
+ if(val != Qundef) return val;
+ cur = module_get_superclass(cur);
}
- /* Object's superclass MUST be nil, but we check directly just
- to be safe. */
- cur = class_get_superclass(cur);
}
- // printf("Still unable to find, firing const_missing.\n");
-
// As a last rescue, we search in Object's constants
hsh = module_get_constants(state->global->object);
val = hash_find_undef(state, hsh, sym);
- if(val != Qundef) {
- return val;
- }
-
+ if(val != Qundef) return val;
+
c->cache_index = -1;
stack_push(sym);
cpu_unified_send(state, c, start, state->global->sym_const_missing, 1, Qnil);
@@ -457,21 +415,12 @@ OBJECT cpu_const_set(STATE, cpu c, OBJECT sym, OBJECT val, OBJECT under) {
void cpu_set_encloser_path(STATE, cpu c, OBJECT cls) {
int len;
len = ptr_array_length(c->paths);
- /*
- if(len > 0 && ptr_array_get_index(c->paths, len-1) == cls) {
- printf("Removing %p from paths.\n", cls);
- ptr_array_remove_ordered(c->paths, len-1);
- } else {
- printf("Adding %p (%d) to the path...\n", cls, cls);
- ptr_array_append(c->paths, cls);
- }
- */
/* add stuff for @paths here */
ptr_array_append(c->paths, (xpointer)c->enclosing_class);
- /*
- printf("Push %s (%d) to paths (%d)\n", _inspect(c->enclosing_class),
- c->enclosing_class, c->paths->len);
- */
+
+ cmethod_set_staticscope(cpu_current_method(state, c),
+ cpu_scope_push(state, c, cls));
+
c->enclosing_class = cls;
}
@@ -480,7 +429,7 @@ void cpu_push_encloser(STATE, cpu c) {
len = ptr_array_length(c->paths);
if(len > 0) {
c->enclosing_class = (OBJECT)ptr_array_remove_index_ordered(c->paths, len - 1);
- //printf("Setting encloser to %s\n", _inspect(c->enclosing_class));
+ cpu_scope_pop(state, c);
}
}
@@ -529,8 +478,13 @@ void cpu_add_method(STATE, cpu c, OBJECT target, OBJECT sym, OBJECT method) {
printf("=> Adding method %s to %s.\n", rbs_symbol_to_cstring(state, sym), _inspect(target));
}
+ // HACK. the 10 sucks, it protects things that go in a method table, but
+ // aren't exactly CompiledMethods.
+ if(NUM_FIELDS(method) > 10 && NIL_P(cmethod_get_staticscope(method))) {
+ cmethod_set_staticscope(method, c->current_scope);
+ }
+
hash_set(state, meths, sym, tuple_new2(state, 2, vis, method));
- // hash_set(state, meths, sym, method);
c->call_flags = 0;
}
View
1  shotgun/lib/cpu.h
@@ -71,6 +71,7 @@ struct fast_context {
ptr_array paths; \
unsigned int depth; \
OBJECT context_cache; \
+ OBJECT current_scope; \
IP_TYPE *ip_ptr; \
OBJECT *sp_ptr; \
int call_flags; \
View
3  shotgun/lib/state.h
@@ -31,7 +31,7 @@ struct rubinius_globals {
OBJECT string, symbol, io, metaclass, symtbl;
OBJECT nil_class, true_class, false_class, fixnum_class, undef_class;
OBJECT floatpoint, fastctx, data, nmethod, nmc, task, list, list_node;
- OBJECT channel, thread;
+ OBJECT channel, thread, staticscope;
/* the primary symbol table */
OBJECT symbols;
@@ -49,6 +49,7 @@ struct rubinius_globals {
OBJECT sym_public, sym_private, sym_protected, sym_const_missing;
OBJECT sym_object_id;
OBJECT exception, iseq, icache;
+ OBJECT top_scope;
OBJECT special_classes[SPECIAL_CLASS_SIZE];
};
View
37 spec/language/constants_spec.rb
@@ -0,0 +1,37 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+require File.dirname(__FILE__) + '/fixtures/constants'
+
+describe "Constant lookup rule" do
+ it "finds a toplevel constant" do
+ Exception.should == ::Exception
+ end
+
+ it "looks up the static, lexical scope in a class method" do
+ A::B::C.number.should == 47
+ A::B::C.name.should == "specs"
+ A::B::C.place.should == "boston"
+ end
+
+ it "looks up the static, lexical scope in an instance method" do
+ A::B::C.new.number.should == 47
+ A::B::C.new.name.should == "specs"
+ A::B::C.new.place.should == "boston"
+ end
+
+ it "looks up the superclass chain" do
+ D.new.number.should == 47
+ D.number.should == 47
+ end
+
+ it "isn't influenced by the calling scope" do
+ E.new.go.should == 8
+ end
+
+ it "isn't influenced by the calling scope, in modules" do
+ I.new.go.should == ::Exception
+ end
+
+ it "calls const_missing on the original scope" do
+ A::B::C.new.fire_missing.should == :missing!
+ end
+end
View
90 spec/language/fixtures/constants.rb
@@ -0,0 +1,90 @@
+module A
+ Place = "boston"
+
+ module B
+ Name = "specs"
+
+ class C
+ Number = 47
+
+ def self.number
+ Number
+ end
+
+ def self.name
+ Name
+ end
+
+ def self.place
+ Place
+ end
+
+ def number
+ Number
+ end
+
+ def name
+ Name
+ end
+
+ def place
+ Place
+ end
+
+ def fire_missing
+ NotAround
+ end
+
+ def self.const_missing(name)
+ :missing!
+ end
+ end
+ end
+end
+
+class D < A::B::C
+ def self.number
+ Number
+ end
+
+ def number
+ Number
+ end
+end
+
+TopNumber = 8
+
+class E
+
+ TopNumber = 1
+
+ def go
+ F.new.go
+ end
+end
+
+class F
+ def go
+ TopNumber
+ end
+end
+
+module G
+ def go
+ Exception
+ end
+end
+
+class H
+ include G
+end
+
+class I
+
+ Exception = 10
+
+ def go
+ H.new.go
+ end
+end
+
Please sign in to comment.
Something went wrong with that request. Please try again.