Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

235 lines (200 sloc) 7.081 kB
#include "vm.hpp"
#include "global_cache.hpp"
#include "object_position.hpp"
#include "object_memory.hpp"
#include "lookup_data.hpp"
#include "dispatch.hpp"
#include "builtin/symbol.hpp"
#include "builtin/class.hpp"
#include "builtin/module.hpp"
#include "builtin/object.hpp"
#include "builtin/method_table.hpp"
#include "builtin/alias.hpp"
namespace rubinius {
static bool hierarchy_resolve(STATE, Symbol* name, Dispatch& msg, LookupData& lookup) {
Module* module = lookup.from;
MethodTableBucket* entry;
bool skip_vis_check = false;
do {
if(module != module->origin()) {
module = module->superclass();
}
entry = module->method_table()->find_entry(state, name);
/* Nothing, there? Ok, keep looking. */
if(!entry) goto keep_looking;
/* A 'false' method means to terminate method lookup.
* (eg. undef_method) */
if(entry->undef_p(state)) {
msg.method_missing = eNormal;
return false;
}
/* If this was a private send, then we can handle use
* any method seen. */
if(lookup.min_visibility == G(sym_private) || skip_vis_check) {
/* The method was callable, but we need to keep looking
* for the implementation, so make the invocation bypass all further
* visibility checks. If we are skipping visibility checks we
* shouldn't update visibility anymore because the implementation
* might have a different visibility than the original lookup.
*/
if(!skip_vis_check) {
msg.visibility = entry->visibility();
}
if(entry->method()->nil_p()) {
skip_vis_check = true;
goto keep_looking;
}
if(Alias* alias = try_as<Alias>(entry->method())) {
if(alias->original_exec()->nil_p()) {
name = alias->original_name();
msg.visibility = entry->visibility();
skip_vis_check = true;
} else {
msg.method = alias->original_exec();
msg.module = alias->original_module();
}
} else {
msg.method = entry->method();
msg.module = module;
}
if(msg.method) return true;
} else {
/* The method is private, but this wasn't a private send. */
if(entry->private_p(state)) {
msg.method_missing = ePrivate;
return false;
} else if(entry->protected_p(state)) {
/* The method is protected, but it's not being called from
* the same module, or we only want public methods. */
Module* check_mod;
if(IncludedModule* im = try_as<IncludedModule>(module)) {
check_mod = im->module();
} else {
check_mod = module;
}
if(lookup.min_visibility == G(sym_public) && !lookup.recv->kind_of_p(state, check_mod)) {
msg.method_missing = eProtected;
return false;
}
}
/* The method was callable, but we need to keep looking
* for the implementation, so make the invocation bypass all further
* visibility checks. If we are skipping visibility checks we
* shouldn't update visibility anymore because the implementation
* might have a different visibility than the original lookup.
*/
if(!skip_vis_check) {
msg.visibility = entry->visibility();
}
if(entry->method()->nil_p()) {
skip_vis_check = true;
goto keep_looking;
}
if(Alias* alias = try_as<Alias>(entry->method())) {
if(alias->original_exec()->nil_p()) {
name = alias->original_name();
msg.visibility = entry->visibility();
skip_vis_check = true;
} else {
msg.method = alias->original_exec();
msg.module = alias->original_module();
}
} else {
msg.method = entry->method();
msg.module = module;
}
if(msg.method) return true;
}
keep_looking:
module = module->superclass();
/* No more places to look, we couldn't find it. */
if(module->nil_p()) {
msg.method_missing = eNormal;
return false;
}
} while(1);
rubinius::bug("Control flow bug in method lookup");
}
bool GlobalCache::resolve(STATE, Symbol* name, Dispatch& msg, LookupData& lookup) {
return state->vm()->global_cache()->resolve_i(state, name, msg, lookup);
}
bool GlobalCache::resolve_i(STATE, Symbol* name, Dispatch& msg, LookupData& lookup) {
utilities::thread::SpinLock::LockGuard guard(lock_);
Module* klass = lookup.from;
CacheEntry* entry = this->lookup(state, klass, name);
if(entry) {
if(lookup.min_visibility == G(sym_private) ||
entry->visibility == G(sym_public) ||
lookup.min_visibility == entry->visibility) {
msg.method = entry->method;
msg.module = entry->module;
msg.visibility = entry->visibility;
return true;
}
}
if(hierarchy_resolve(state, name, msg, lookup)) {
retain_i(state, klass, name,
msg.module, msg.method, msg.visibility);
return true;
}
return false;
}
bool GlobalCache::has_seen(STATE, Symbol* sym) {
utilities::thread::SpinLock::LockGuard guard(lock_);
SeenMethodSet::const_iterator seen = seen_methods.find(sym->index());
return seen != seen_methods.end();
}
void GlobalCache::add_seen(STATE, Symbol* sym) {
utilities::thread::SpinLock::LockGuard guard(lock_);
seen_methods.insert(sym->index());
}
void GlobalCache::prune_young() {
for(size_t i = 0; i < CPU_CACHE_SIZE; i++) {
CacheEntry* entry = &entries[i];
bool clear = false;
Object* klass = entry->klass;
if(!klass) continue;
if(klass->young_object_p()) {
if(klass->forwarded_p()) {
Module* fwd = force_as<Module>(klass->forward());
entry->klass = fwd;
} else {
clear = true;
}
}
Object* mod = entry->module;
if(mod->young_object_p()) {
if(mod->forwarded_p()) {
entry->module = force_as<Module>(mod->forward());
} else {
clear = true;
}
}
Object* exec = entry->method;
if(exec->young_object_p()) {
if(exec->forwarded_p()) {
entry->method = force_as<Executable>(exec->forward());
} else {
clear = true;
}
}
if(clear) {
entry_names[i] = NULL;
entry->clear();
}
}
}
void GlobalCache::prune_unmarked(unsigned int mark) {
for(size_t i = 0; i < CPU_CACHE_SIZE; i++) {
CacheEntry* entry = &entries[i];
Object* klass = entry->klass;
if(!klass) continue;
Object* mod = entry->module;
Object* exec = entry->method;
if(!klass->marked_p(mark) || !mod->marked_p(mark) || !exec->marked_p(mark)) {
entry_names[i] = NULL;
entry->clear();
}
}
}
}
Jump to Line
Something went wrong with that request. Please try again.