MethodTable* MethodTable::duplicate(STATE) { size_t size, i; MethodTable* dup = 0; utilities::thread::SpinLock::LockGuard lg(lock_); size = bins_->to_native(); dup = MethodTable::create(state, size); // Allow for subclassing. dup->klass(state, class_object(state)); size_t num = bins_->to_native(); for(i = 0; i < num; i++) { MethodTableBucket* entry = try_as<MethodTableBucket>(values_->at(state, i)); while(entry) { dup->store(state, entry->name(), entry->method_id(), entry->method(), entry->scope(), entry->serial(), entry->visibility()); entry = try_as<MethodTableBucket>(entry->next()); } } return dup; }
Object* MethodTable::alias(STATE, Symbol* name, Symbol* vis, Symbol* orig_name, Object* orig_method, Module* orig_mod) { check_frozen(state); utilities::thread::SpinLock::LockGuard lg(lock_); Executable* orig_exec; if(Alias* alias = try_as<Alias>(orig_method)) { orig_exec = alias->original_exec(); orig_mod = alias->original_module(); orig_name = alias->original_name(); } else if(orig_method->nil_p()) { orig_exec = nil<Executable>(); } else { orig_exec = as<Executable>(orig_method); } Alias* method = Alias::create(state, orig_name, orig_mod, orig_exec); native_int num_entries = entries_->to_native(); native_int num_bins = bins_->to_native(); if(max_density_p(num_entries, num_bins)) { redistribute(state, num_bins <<= 1); } native_int bin = find_bin(key_hash(name), num_bins); MethodTableBucket* entry = try_as<MethodTableBucket>(values_->at(state, bin)); MethodTableBucket* last = NULL; while(entry) { if(entry->name() == name) { entry->method_id(state, nil<String>()); entry->method(state, method); entry->scope(state, cNil); entry->serial(state, Fixnum::from(0)); entry->visibility(state, vis); return name; } last = entry; entry = try_as<MethodTableBucket>(entry->next()); } if(last) { last->next(state, MethodTableBucket::create( state, name, nil<String>(), method, cNil, Fixnum::from(0), vis)); } else { values_->put(state, bin, MethodTableBucket::create( state, name, nil<String>(), method, cNil, Fixnum::from(0), vis)); } entries(state, Fixnum::from(num_entries + 1)); return name; }
Object* MethodTable::store(STATE, Symbol* name, Object* method_id, Object* method, Object* scope, Fixnum* serial, Symbol* visibility) { check_frozen(state); utilities::thread::SpinLock::LockGuard lg(lock_); if(!method->nil_p()) { if(Alias* stored_alias = try_as<Alias>(method)) { lock_.unlock(); Object* res = alias(state, name, visibility, stored_alias->original_name(), stored_alias->original_exec(), stored_alias->original_module()); lock_.lock(); return res; } } native_int num_entries = entries_->to_native(); native_int num_bins = bins_->to_native(); if(max_density_p(num_entries, num_bins)) { redistribute(state, num_bins <<= 1); } native_int bin = find_bin(key_hash(name), num_bins); MethodTableBucket* entry = try_as<MethodTableBucket>(values_->at(state, bin)); MethodTableBucket* last = NULL; while(entry) { if(entry->name() == name) { entry->method_id(state, method_id); entry->method(state, method); entry->scope(state, scope); entry->serial(state, serial); entry->visibility(state, visibility); return name; } last = entry; entry = try_as<MethodTableBucket>(entry->next()); } if(last) { last->next(state, MethodTableBucket::create( state, name, method_id, method, scope, serial, visibility)); } else { values_->put(state, bin, MethodTableBucket::create( state, name, method_id, method, scope, serial, visibility)); } entries(state, Fixnum::from(num_entries + 1)); return name; }
MethodTableBucket* MethodTableBucket::create(STATE, Symbol* name, Object* method_id, Object* method, Object* scope, Fixnum* serial, Symbol* vis) { MethodTableBucket *entry = state->new_object<MethodTableBucket>(G(methtblbucket)); entry->name(state, name); entry->method_id(state, method_id); entry->method(state, method); entry->scope(state, scope); entry->serial(state, serial); entry->visibility(state, vis); return entry; }
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"); }