예제 #1
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();
    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;
  }
예제 #2
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;
  }
예제 #3
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;
  }
예제 #4
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;
  }
예제 #5
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");
  }