Object* System::vm_check_callable(STATE, Object* obj, Symbol* sym, Object* self) { Module* mod = obj->lookup_begin(state); MethodTableBucket* entry; bool skip_vis_check = false; while(!mod->nil_p()) { entry = mod->method_table()->find_entry(state, sym); if(entry) { if(entry->undef_p(state)) return Qfalse; if(!skip_vis_check) { if(entry->private_p(state)) return Qfalse; if(entry->protected_p(state)) { if(!self->kind_of_p(state, mod)) return Qfalse; } } // It's callable, ok, but see if we should see if it's just a stub // to change the visibility of another method. if(entry->method()->nil_p()) { skip_vis_check = true; } else { return Qtrue; } } mod = mod->superclass(); } return Qfalse; }
MethodMissingReason InlineCache::fill_public(STATE, Object* self, Symbol* name) { MethodTableBucket* entry; Module* module = klass_; // Check the global cache first! GlobalCache::cache_entry* global_entry = state->global_cache()->lookup(module, name); if(global_entry && global_entry->is_public && !global_entry->method_missing) { this->method = global_entry->method; this->module = global_entry->module; return eNone; } bool skip_vis_check = false; MethodTableBucket* vis_entry = 0; Symbol* target_name = name; do { entry = module->method_table()->find_entry(state, target_name); /* Nothing, there? Ok, keep looking. */ if(entry) { /* A 'false' method means to terminate method lookup. * (eg. undef_method) */ if(entry->undef_p(state)) return eNormal; if(!skip_vis_check) { /* The method is private, but this wasn't a private send. */ if(entry->private_p(state)) { return ePrivate; } else if(entry->protected_p(state)) { /* The method is protected, but it's not being called from * the same module */ Module* check_mod; if(IncludedModule* im = try_as<IncludedModule>(module)) { check_mod = im->module(); } else { check_mod = module; } if(!self->kind_of_p(state, check_mod)) { return eProtected; } } } /* The method was callable, but we need to keep looking * for the implementation, so make the invocation bypass all further * visibility checks. * * This is pretty much always where a subclass marks a superclass * method as public. We don't move the method, we just put this * marker into the method table. */ if(entry->method()->nil_p()) { vis_entry = entry; skip_vis_check = true; } else { Module* use_module; Executable* use_exec; if(Alias* alias = try_as<Alias>(entry->method())) { // Same check as above, allow an alias to be for a superclass // method. if(alias->original_exec()->nil_p()) { vis_entry = entry; skip_vis_check = true; use_exec = 0; target_name = alias->original_name(); } else { use_exec = alias->original_exec(); use_module = alias->original_module(); } } else { use_module = module; use_exec = entry->method(); } if(use_exec) { this->module = use_module; this->method = use_exec; if(!vis_entry) vis_entry = entry; state->global_cache()->retain(state, klass_, name, this->module, this->method, false, !vis_entry->public_p(state)); return eNone; } } } module = module->superclass(); /* No more places to look, we couldn't find it. */ if(module->nil_p()) return eNormal; } while(1); // Shouldn't be here! rubinius::abort(); }
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() && entry->method_id()->nil_p()) { // TODO: fix using method() == cNil for this // 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->get_method(state); 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() && entry->method_id()->nil_p()) { // TODO: fix using method() == cNil for this // 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->get_method(state); 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"); }
MethodMissingReason InlineCache::fill_public(STATE, Object* self, Symbol* name) { MethodTableBucket* entry; Module* module = klass_; // Check the global cache first! GlobalCache::cache_entry* global_entry = state->global_cache->lookup(module, name); if(global_entry && global_entry->is_public && !global_entry->method_missing) { this->method = global_entry->method; this->module = global_entry->module; return eNone; } bool skip_vis_check = false; do { entry = module->method_table()->find_entry(state, name); /* Nothing, there? Ok, keep looking. */ if(entry) { /* A 'false' method means to terminate method lookup. * (eg. undef_method) */ if(entry->undef_p(state)) return eNormal; if(!skip_vis_check) { /* The method is private, but this wasn't a private send. */ if(entry->private_p(state)) { return ePrivate; } else if(entry->protected_p(state)) { /* The method is protected, but it's not being called from * the same module */ if(!self->kind_of_p(state, module)) { return eProtected; } } } /* The method was callable, but we need to keep looking * for the implementation, so make the invocation bypass all further * visibility checks. * * This is pretty much always where a subclass marks a superclass * method as public. We don't move the method, we just put this * marker into the method table. */ if(entry->method()->nil_p()) { skip_vis_check = true; } else { this->method = entry->method(); this->module = module; state->global_cache->retain(state, klass_, name, this->module, this->method, false, !entry->public_p(state)); return eNone; } } module = module->superclass(); /* No more places to look, we couldn't find it. */ if(module->nil_p()) return eNormal; } while(1); // Shouldn't be here! rubinius::abort(); }
static bool hierarchy_resolve(STATE, Symbol* name, Dispatch& msg, LookupData& lookup, bool* was_private) { Module* module = lookup.from; MethodTableBucket* entry; bool skip_vis_check = false; do { 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)) return false; /* If this was a private send, then we can handle use * any method seen. */ if(lookup.priv || skip_vis_check) { /* nil means that the actual method object is 'up' from here */ if(entry->method()->nil_p()) goto keep_looking; *was_private = entry->private_p(state); if(Alias* alias = try_as<Alias>(entry->method())) { msg.method = alias->original_exec(); msg.module = alias->original_module(); } else { msg.method = entry->method(); msg.module = module; } break; } else { /* The method is private, but this wasn't a private send. */ if(entry->private_p(state)) { return false; } else if(entry->protected_p(state)) { /* The method is protected, but it's not being called from * the same module */ if(!lookup.recv->kind_of_p(state, module)) { 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(entry->method()->nil_p()) { skip_vis_check = true; goto keep_looking; } if(Alias* alias = try_as<Alias>(entry->method())) { msg.method = alias->original_exec(); msg.module = alias->original_module(); } else { msg.method = entry->method(); msg.module = module; } break; } keep_looking: module = module->superclass(); /* No more places to look, we couldn't find it. */ if(module->nil_p()) return false; } while(1); return true; }