Esempio n. 1
0
  Object* System::vm_check_super_callable(STATE, CallFrame* call_frame) {
    if(call_frame->native_method_p()) return Qtrue;

    Module* mod = call_frame->module()->superclass();

    MethodTableBucket* entry;
    Symbol* sym = call_frame->original_name();

    while(!mod->nil_p()) {
      entry = mod->method_table()->find_entry(state, sym);

      if(entry) {
        if(entry->undef_p(state)) 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()) {
          return Qtrue;
        }
      }

      mod = mod->superclass();
    }

    return Qfalse;
  }
Esempio n. 2
0
  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();

    MethodTableBucket* entry = 0;

    for(i = 0; i < num; i++) {
      entry = try_as<MethodTableBucket>(values_->at(state, i));

      while(entry) {
        dup->store(state, entry->name(), entry->method(), entry->visibility());
        entry = try_as<MethodTableBucket>(entry->next());
      }
    }

    return dup;
  }
Esempio n. 3
0
  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;
  }
Esempio n. 4
0
  void MethodTable::Info::show(STATE, Object* self, int level) {
    MethodTable* tbl = as<MethodTable>(self);
    size_t size = tbl->bins()->to_native();

    if(size == 0) {
      class_info(state, self, true);
      return;
    }

    class_info(state, self);
    std::cout << ": " << size << std::endl;
    indent(++level);
    for(size_t i = 0; i < size; i++) {
      MethodTableBucket* entry = try_as<MethodTableBucket>(tbl->values()->at(state, i));

      while(entry) {
       if(Symbol* sym = try_as<Symbol>(entry->name())) {
          std::cout << ":" << sym->debug_str(state);
        } else if(Fixnum* fix = try_as<Fixnum>(entry->name())) {
          std::cout << fix->to_native();
        }
        entry = try_as<MethodTableBucket>(entry->next());
      }
      if(i < size - 1) std::cout << ", ";
    }
    std::cout << std::endl;
    close_body(level);
  }
Esempio n. 5
0
  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;
  }
Esempio n. 6
0
  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;
  }
Esempio n. 7
0
  MethodTableBucket* MethodTableBucket::create(STATE, Symbol* name,
      Executable* method, Symbol* vis)
  {
    MethodTableBucket *entry =
      state->new_object<MethodTableBucket>(G(methtblbucket));

    entry->name(state, name);
    entry->method(state, method);
    entry->visibility(state, vis);
    return entry;
  }
Esempio n. 8
0
  bool InlineCache::fill_private(STATE, Symbol* name, Module* start) {
    MethodTableBucket* entry;

    Module* module = start;

    // Check the global cache first!
    GlobalCache::cache_entry* global_entry =
      state->global_cache->lookup(module, name);

    if(global_entry && !global_entry->method_missing) {
      this->method = global_entry->method;
      this->module = global_entry->module;

      return true;
    }

    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 false;

        /* 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()) {
          this->method = entry->method();
          this->module = module;
          state->global_cache->retain(state, start, name, this->module,
              this->method, false,
              !entry->public_p(state));

          return true;
        }
      }

      module = module->superclass();

      /* No more places to look, we couldn't find it. */
      if(module->nil_p()) return false;
    } while(1);

    // Shouldn't be here!
    rubinius::abort();
  }
Esempio n. 9
0
  Object* MethodTableBucket::append(STATE, MethodTableBucket* nxt) {
    MethodTableBucket* cur = try_as<MethodTableBucket>(this->next());
    MethodTableBucket* last = this;

    while(cur) {
      last = cur;
      cur = try_as<MethodTableBucket>(cur->next());
    }

    last->next(state, nxt);
    return nxt;
  }
Esempio n. 10
0
  Executable* Module::find_method(Symbol* name, Module** defined_in) {
    Module* mod = this;

    do {
      MethodTableBucket* buk = mod->method_table()->find_entry(name);
      if(buk) {
        if(defined_in) *defined_in = mod;
        return buk->method();
      }
      mod = mod->superclass();
    } while(!mod->nil_p());

    return NULL;
  }
Esempio n. 11
0
  bool InlineCache::fill_method_missing(STATE, Class* klass,
                                        MethodCacheEntry*& mce)
  {
    MethodTableBucket* entry;

    Module* module = klass;

    Symbol* name = G(sym_method_missing);

    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 false;

        /* 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()) {
          if(Alias* alias = try_as<Alias>(entry->method())) {
            mce = MethodCacheEntry::create(state, klass,
                                           alias->original_module(),
                                           alias->original_exec());
          } else {
            mce = MethodCacheEntry::create(state, klass,
                                           module,
                                           entry->method());
          }

          return true;
        }
      }

      module = module->superclass();

      /* No more places to look, we couldn't find it. */
      if(module->nil_p()) return false;
    } while(1);

    // Shouldn't be here!
    rubinius::bug("Control flow bug in method lookup");
  }
Esempio n. 12
0
  Executable* MethodTable::remove(STATE, Symbol* name) {
    hashval bin;
    MethodTableBucket* entry;
    MethodTableBucket* last = NULL;

    size_t num_entries = entries_->to_native();
    size_t num_bins = bins_->to_native();

    if(min_density_p(num_entries, num_bins) && (num_bins >> 1) >= METHODTABLE_MIN_SIZE) {
      redistribute(state, num_bins >>= 1);
    }

    bin = find_bin(key_hash(name), num_bins);
    entry = try_as<MethodTableBucket>(values_->at(state, bin));

    while(entry) {
      if(entry->name() == name) {
        Executable* val = entry->method();
        if(last) {
          last->next(state, entry->next());
        } else {
          values_->put(state, bin, entry->next());
        }
        entries(state, Fixnum::from(entries_->to_native() - 1));
        return val;
      }

      last = entry;
      entry = try_as<MethodTableBucket>(entry->next());
    }

    return reinterpret_cast<Executable*>(Qnil);
  }
Esempio n. 13
0
  MethodTableBucket* MethodTable::find_entry(Symbol* name) {
    unsigned int bin;

    bin = find_bin(key_hash(name), bins_->to_native());
    MethodTableBucket *entry = try_as<MethodTableBucket>(values_->at(bin));

    while(entry) {
      if(entry->name() == name) {
        return entry;
      }
      entry = try_as<MethodTableBucket>(entry->next());
    }

    return 0;
  }
Esempio n. 14
0
  MethodTableBucket* MethodTable::find_entry(Symbol* name) {
    unsigned int bin;
    utilities::thread::SpinLock::LockGuard lg(lock_);

    bin = find_bin(key_hash(name), bins_->to_native());
    MethodTableBucket *entry = try_as<MethodTableBucket>(values_->at(bin));

    while(entry) {
      if(entry->name() == name) {
        return entry;
      }
      entry = try_as<MethodTableBucket>(entry->next());
    }

    return 0;
  }
Esempio n. 15
0
  MethodTable* MethodTable::duplicate(STATE) {
    size_t size, i;
    MethodTable *dup;

    size = bins_->to_native();
    dup = MethodTable::create(state, size);

    // Allow for subclassing.
    dup->klass(state, class_object(state));

    size_t num = entries_->to_native();

    Array* entries = all_entries(state);
    for(i = 0; i < num; i++) {
      MethodTableBucket* entry = as<MethodTableBucket>(entries->get(state, i));
      dup->store(state, entry->name(), entry->method(), entry->visibility());
    }
    return dup;
  }
Esempio n. 16
0
  Array* MethodTable::collect(STATE, MethodTable* tbl,
                              Object* (*action)(STATE, MethodTableBucket*))
  {
    size_t i, j;
    Tuple* values;
    MethodTableBucket* entry;

    Array* ary = Array::create(state, tbl->entries()->to_native());
    size_t num_bins = tbl->bins()->to_native();
    values = tbl->values();

    for(i = j = 0; i < num_bins; i++) {
      entry = try_as<MethodTableBucket>(values->at(state, i));

      while(entry) {
        ary->set(state, j++, action(state, entry));
        entry = try_as<MethodTableBucket>(entry->next());
      }
    }
    return ary;
  }
Esempio n. 17
0
  void MethodTable::redistribute(STATE, size_t size) {
    size_t num = bins_->to_native();
    Tuple* new_values = Tuple::create(state, size);

    for(size_t i = 0; i < num; i++) {
      MethodTableBucket* entry = try_as<MethodTableBucket>(values_->at(state, i));

      while(entry) {
        MethodTableBucket* link = try_as<MethodTableBucket>(entry->next());
        entry->next(state, nil<MethodTableBucket>());

        size_t bin = find_bin(key_hash(entry->name()), size);
        MethodTableBucket* slot = try_as<MethodTableBucket>(new_values->at(state, bin));

        if(slot) {
          slot->append(state, entry);
        } else {
          new_values->put(state, bin, entry);
        }

        entry = link;
      }
    }

    values(state, new_values);
    bins(state, Fixnum::from(size));
  }
Esempio n. 18
0
  bool InlineCache::fill_method_missing(STATE, Module* module) {
    MethodTableBucket* entry;

    Symbol* name = G(sym_method_missing);

    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 false;

        /* 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()) {
          this->method = entry->method();
          this->module = module;

          return true;
        }
      }

      module = module->superclass();

      /* No more places to look, we couldn't find it. */
      if(module->nil_p()) return false;
    } while(1);

    // Shouldn't be here!
    rubinius::abort();
  }
Esempio n. 19
0
  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;
  }
Esempio n. 20
0
  Object* MethodTable::alias(STATE, Symbol* name, Symbol* vis,
                             Symbol* orig_name, Executable* orig_method,
                             Module* orig_mod)
  {
    unsigned int num_entries, num_bins, bin;
    MethodTableBucket* entry;
    MethodTableBucket* last = NULL;

    if(Alias* alias = try_as<Alias>(orig_method)) {
      orig_method = alias->original_exec();
      orig_mod = alias->original_module();
      orig_name = alias->original_name();
    }

    Alias* method = Alias::create(state, orig_name, orig_mod, orig_method);

    num_entries = entries_->to_native();
    num_bins = bins_->to_native();

    if(max_density_p(num_entries, num_bins)) {
      redistribute(state, num_bins <<= 1);
    }

    bin = find_bin(key_hash(name), num_bins);
    entry = try_as<MethodTableBucket>(values_->at(state, bin));

    while(entry) {
      if(entry->name() == name) {
        entry->method(state, method);
        entry->visibility(state, vis);
        return name;
      }
      last = entry;
      entry = try_as<MethodTableBucket>(entry->next());
    }

    if(last) {
      last->next(state, MethodTableBucket::create(state, name, method, vis));
    } else {
      values_->put(state, bin, MethodTableBucket::create(state, name, method, vis));
    }

    entries(state, Fixnum::from(num_entries + 1));
    return name;
  }
Esempio n. 21
0
  Object* MethodTable::store(STATE, Symbol* name, Object* exec, Symbol* vis) {
    unsigned int num_entries, num_bins, bin;
    MethodTableBucket* entry;
    MethodTableBucket* last = NULL;

    Executable* method;
    if(exec->nil_p()) {
      method = reinterpret_cast<Executable*>(Qnil);
    } else {
      if(Alias* alias = try_as<Alias>(exec)) {
        method = alias->original_exec();
      } else {
        method = as<Executable>(exec);
      }
    }

    num_entries = entries_->to_native();
    num_bins = bins_->to_native();

    if(max_density_p(num_entries, num_bins)) {
      redistribute(state, num_bins <<= 1);
    }

    bin = find_bin(key_hash(name), num_bins);
    entry = try_as<MethodTableBucket>(values_->at(state, bin));

    while(entry) {
      if(entry->name() == name) {
        entry->method(state, method);
        entry->visibility(state, vis);
        return name;
      }
      last = entry;
      entry = try_as<MethodTableBucket>(entry->next());
    }

    if(last) {
      last->next(state, MethodTableBucket::create(state, name, method, vis));
    } else {
      values_->put(state, bin, MethodTableBucket::create(state, name, method, vis));
    }

    entries(state, Fixnum::from(num_entries + 1));
    return name;
  }
Esempio n. 22
0
  Executable* MethodTable::remove(STATE, Symbol* name) {
    check_frozen(state);

    utilities::thread::SpinLock::LockGuard lg(lock_);

    native_int num_entries = entries_->to_native();
    native_int num_bins = bins_->to_native();

    if(min_density_p(num_entries, num_bins) &&
         (num_bins >> 1) >= METHODTABLE_MIN_SIZE) {
      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) {
        Executable* val = entry->method();
        if(last) {
          last->next(state, entry->next());
        } else {
          values_->put(state, bin, entry->next());
        }

        entries(state, Fixnum::from(entries_->to_native() - 1));
        return val;
      }

      last = entry;
      entry = try_as<MethodTableBucket>(entry->next());
    }

    return nil<Executable>();
  }
Esempio n. 23
0
  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;
  }
Esempio n. 24
0
  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();
  }
Esempio n. 25
0
  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");
  }
Esempio n. 26
0
  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();
  }
Esempio n. 27
0
  bool InlineCache::fill_private(STATE, Symbol* name, Module* start,
                                 Class* klass,
                                 MethodCacheEntry*& mce)
  {
    MethodTableBucket* entry;

    Module* module = start;

    // Check the global cache first!
    mce = state->vm()->global_cache()->lookup_private(state, start, klass, name);
    if(mce) return true;

    MethodTableBucket* vis_entry = 0;

    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 false;

        /* 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()) {
          if(Alias* alias = try_as<Alias>(entry->method())) {
            mce = MethodCacheEntry::create(state, klass,
                                           alias->original_module(),
                                           alias->original_exec());
          } else {
            mce = MethodCacheEntry::create(state, klass, module, entry->method());
          }

          if(!vis_entry) vis_entry = entry;

          state->vm()->global_cache()->retain(state, start, name, mce->stored_module(),
                mce->method(), false,
                !vis_entry->public_p(state));

          return true;
        } else {
          // Remember this entry as defining the visibility
          vis_entry = entry;
        }
      }

      module = module->superclass();

      /* No more places to look, we couldn't find it. */
      if(module->nil_p()) return false;
    } while(1);

    // Shouldn't be here!
    rubinius::bug("Control flow bug in method lookup");
  }