Skip to content

Commit

Permalink
Fix Autoload handling by properly reusing the current scope
Browse files Browse the repository at this point in the history
In the Autoload parts in the VM we now do the lookup again so it's much
easier to do it with the correct current scope and we follow the same
lookup mechanisms as a normal constant lookup.

For this we modify the Helpers for const_get so it can filter a result
so the autoload is filtered and the real class or a class with the same
name higher in the hierarchy is returned.
  • Loading branch information
dbussink committed Jul 1, 2012
1 parent da39663 commit 1c079a8
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 33 deletions.
20 changes: 12 additions & 8 deletions kernel/common/autoload.rb
Expand Up @@ -29,29 +29,33 @@ def set_path(path)
##
# When any code that finds a constant sees an instance of Autoload as its match,
# it calls this method on us
def call(honor_require=false)
def call(under, honor_require=false)
# We leave the Autoload object in the constant table so that if another
# thread hits this while we're mid require they'll be come in here and
# be held by require until @path is available, at which time they'll
# attempt the lookup again.
#
worked = Rubinius::CodeLoader.require @path
worked = resolve

if !honor_require or worked
find_const
find_const under
end
end

def find_const
current, constant = @scope, undefined
def resolve
Rubinius::CodeLoader.require @path
end

def find_const under
current, constant = under, undefined

while current
constant = current.constant_table.fetch name, undefined
unless constant.equal?(undefined)
if constant.equal? self
constant = Object.constant_table.fetch name, undefined
if constant.equal?(undefined)
return scope.const_missing(name)
return under.const_missing(name)
end
end
return constant
Expand All @@ -64,13 +68,13 @@ def find_const
constant = Object.constant_table.fetch name, undefined
unless constant.equal?(undefined)
if constant.equal? self
return scope.const_missing(name)
return under.const_missing(name)
end
return constant
end
end

scope.const_missing(name)
under.const_missing(name)
end

end
4 changes: 2 additions & 2 deletions kernel/common/module18.rb
Expand Up @@ -11,7 +11,7 @@ def const_get(name)
while current
constant = current.constant_table.fetch name, undefined
unless constant.equal?(undefined)
constant = constant.call if constant.kind_of?(Autoload)
constant = constant.call(current) if constant.kind_of?(Autoload)
return constant
end

Expand All @@ -21,7 +21,7 @@ def const_get(name)
if instance_of?(Module)
constant = Object.constant_table.fetch name, undefined
unless constant.equal?(undefined)
constant = constant.call if constant.kind_of?(Autoload)
constant = constant.call(current) if constant.kind_of?(Autoload)
return constant
end
end
Expand Down
4 changes: 2 additions & 2 deletions kernel/common/module19.rb
Expand Up @@ -10,7 +10,7 @@ def const_get(name, inherit = true)
while current
constant = current.constant_table.fetch name, undefined
unless constant.equal?(undefined)
constant = constant.call if constant.kind_of?(Autoload)
constant = constant.call(current) if constant.kind_of?(Autoload)
return constant
end

Expand All @@ -24,7 +24,7 @@ def const_get(name, inherit = true)
if instance_of?(Module)
constant = Object.constant_table.fetch name, undefined
unless constant.equal?(undefined)
constant = constant.call if constant.kind_of?(Autoload)
constant = constant.call(current) if constant.kind_of?(Autoload)
return constant
end
end
Expand Down
2 changes: 1 addition & 1 deletion kernel/delta/rubinius.rb
Expand Up @@ -54,7 +54,7 @@ def self.open_module_under(name, mod)
else
obj = tbl[name]
if Type.object_kind_of? obj, Autoload
obj = obj.call(true)
obj = obj.call(mod, true)

# See comment above about autoload returning nil
unless obj
Expand Down
2 changes: 1 addition & 1 deletion kernel/delta/rubinius18.rb
Expand Up @@ -14,7 +14,7 @@ def self.open_class_under(name, sup, mod)
else
obj = tbl[name]
if Type.object_kind_of? obj, Autoload
obj = obj.call(true)
obj = obj.call(mod, true)

# nil is returned if the autoload was abort, usually because
# the file to be required has already been loaded. In which case
Expand Down
2 changes: 1 addition & 1 deletion kernel/delta/rubinius19.rb
Expand Up @@ -15,7 +15,7 @@ def self.open_class_under(name, sup, mod)
obj = tbl[name]
if Type.object_kind_of? obj, Autoload
obj = begin
obj.call(true)
obj.call(mod, true)
rescue NameError # Constant not found in loaded file
nil
end
Expand Down
45 changes: 39 additions & 6 deletions vm/builtin/autoload.cpp
Expand Up @@ -2,8 +2,10 @@
#include "call_frame.hpp"
#include "builtin/class.hpp"
#include "builtin/autoload.hpp"
#include "helpers.hpp"

#include "ontology.hpp"
#include "on_stack.hpp"

namespace rubinius {
Autoload* Autoload::create(STATE) {
Expand All @@ -15,14 +17,45 @@ namespace rubinius {
G(autoload)->set_object_type(state, AutoloadType);
}

Object* Autoload::resolve(STATE, CallFrame* call_frame, Module* under, bool honor_require) {
Autoload* self = this;
OnStack<1> os(state, self);
Object* res = send(state, call_frame, state->symbol("resolve"));

if(!res) return NULL;

if(CBOOL(res) || !honor_require) {
bool found;
Object* constant = Helpers::const_get_under(state, under, self->name(), &found, self);

if(!constant) return NULL;

if(found) {
return constant;
}
return Helpers::const_missing_under(state, under, self->name(), call_frame);
}
return cNil;
}

Object* Autoload::resolve(STATE, CallFrame* call_frame, bool honor_require) {
if(honor_require) {
Array* args = Array::create(state, 1);
args->set(state, 0, cTrue);
Autoload* self = this;
OnStack<1> os(state, self);
Object* res = send(state, call_frame, state->symbol("resolve"));

if(!res) return NULL;

if(CBOOL(res) || !honor_require) {
bool found;
Object* constant = Helpers::const_get(state, call_frame, self->name(), &found, self);

if(!constant) return NULL;

return send(state, call_frame, G(sym_call), args);
} else {
return send(state, call_frame, G(sym_call));
if(found) {
return constant;
}
return Helpers::const_missing(state, self->name(), call_frame);
}
return cNil;
}
}
1 change: 1 addition & 0 deletions vm/builtin/autoload.hpp
Expand Up @@ -27,6 +27,7 @@ namespace rubinius {
// Rubinius.primitive :autoload_allocate
static Autoload* create(STATE);

Object* resolve(STATE, CallFrame* call_frame, Module* under, bool honor_require = false);
Object* resolve(STATE, CallFrame* call_frame, bool honor_require = false);

public: /* TypeInfo */
Expand Down
28 changes: 20 additions & 8 deletions vm/helpers.cpp
Expand Up @@ -30,14 +30,17 @@

namespace rubinius {
namespace Helpers {
Object* const_get_under(STATE, Module* mod, Symbol* name, bool* found) {
Object* res;
Object* const_get_under(STATE, Module* mod, Symbol* name, bool* found, Object* filter) {
Object* result;

*found = false;

while(!mod->nil_p()) {
res = mod->get_const(state, name, found);
if(*found) return res;
result = mod->get_const(state, name, found);
if(*found) {
if(result != filter) return result;
*found = false;
}

// Don't stop when you see Object, because we need to check any
// includes into Object as well, and they're found via superclass
Expand All @@ -47,7 +50,7 @@ namespace rubinius {
return cNil;
}

Object* const_get(STATE, CallFrame* call_frame, Symbol* name, bool* found) {
Object* const_get(STATE, CallFrame* call_frame, Symbol* name, bool* found, Object* filter) {
ConstantScope *cur;
Object* result;

Expand Down Expand Up @@ -91,7 +94,10 @@ namespace rubinius {
if(cur->top_level_p(state)) break;

result = cur->module()->get_const(state, name, found);
if(*found) return result;
if(*found) {
if(result != filter) return result;
*found = false;
}

cur = cur->parent();
}
Expand All @@ -102,15 +108,21 @@ namespace rubinius {
Module* mod = cur->module();
while(!mod->nil_p()) {
result = mod->get_const(state, name, found);
if(*found) return result;
if(*found) {
if(result != filter) return result;
*found = false;
}

mod = mod->superclass();
}
}

// Lastly, check Object specifically
result = G(object)->get_const(state, name, found, true);
if(*found) return result;
if(*found) {
if(result != filter) return result;
*found = false;
}

return cNil;
}
Expand Down
4 changes: 2 additions & 2 deletions vm/helpers.hpp
Expand Up @@ -13,8 +13,8 @@ namespace rubinius {
class Tuple;

namespace Helpers {
Object* const_get_under(State*, Module* under, Symbol* name, bool* found);
Object* const_get(State*, CallFrame* call_frame, Symbol* name, bool* found);
Object* const_get_under(State*, Module* under, Symbol* name, bool* found, Object* filter = NULL);
Object* const_get(State*, CallFrame* call_frame, Symbol* name, bool* found, Object* filter = NULL);
Object* const_missing_under(State*, Module* under, Symbol* sym, CallFrame* call_frame);
Object* const_missing(State*, Symbol* sym, CallFrame* call_frame);
void const_set(State*, CallFrame* call_frame, Symbol* name, Object* val);
Expand Down
2 changes: 1 addition & 1 deletion vm/instructions.def
Expand Up @@ -803,7 +803,7 @@ instruction find_const(index) [ module -- constant ]
res = Helpers::const_missing_under(state, under, sym, call_frame);
} else if(Autoload* autoload = try_as<Autoload>(res)) {
flush_ip();
res = autoload->resolve(state, call_frame);
res = autoload->resolve(state, call_frame, under);
}

CHECK_AND_PUSH(res);
Expand Down
2 changes: 1 addition & 1 deletion vm/llvm/jit_util.cpp
Expand Up @@ -599,7 +599,7 @@ extern "C" {
if(!found) {
res = Helpers::const_missing_under(state, under, sym, call_frame);
} else if(Autoload* autoload = try_as<Autoload>(res)) {
res = autoload->resolve(state, call_frame);
res = autoload->resolve(state, call_frame, under);
}

return res;
Expand Down

0 comments on commit 1c079a8

Please sign in to comment.