~tim/natalie

8e9e487dd5b7be3438aadcad6503e8b46a72e0b0 — Tim Morgan a month ago 08ccafe
Store method name and owner on the Method object
M include/natalie/method.hpp => include/natalie/method.hpp +17 -12
@@ 9,12 9,16 @@
namespace Natalie {

struct Method : public gc {
    Method(MethodFnPtr fn)
        : m_fn { fn }
    Method(const char *name, ModuleValue *owner, MethodFnPtr fn)
        : m_name { name }
        , m_owner { owner }
        , m_fn { fn }
        , m_undefined { !fn } { }

    Method(Block *block)
        : m_env { *block->env() } {
    Method(const char *name, ModuleValue *owner, Block *block)
        : m_name { name }
        , m_owner { owner }
        , m_env { *block->env() } {
        block->copy_fn_pointer_to_method(this);
        m_env.clear_caller();
    }


@@ 26,27 30,28 @@ struct Method : public gc {

    bool is_undefined() { return m_undefined; }

    ValuePtr run(Env *env, ValuePtr self, size_t argc = 0, ValuePtr *args = nullptr, Block *block = nullptr) {
        return m_fn(env, self, argc, args, block);
    }

    ValuePtr call(Env *env, ModuleValue *method_owner, const char *method_name, ValuePtr self, size_t argc, ValuePtr *args, Block *block) {
    ValuePtr call(Env *env, ValuePtr self, size_t argc, ValuePtr *args, Block *block) {
        assert(!is_undefined());
        Env *closure_env;
        if (has_env()) {
            closure_env = this->env();
        } else {
            closure_env = method_owner->env();
            closure_env = m_owner->env();
        }
        Env e = Env { closure_env, env };
        e.set_file(env->file());
        e.set_line(env->line());
        e.set_method_name(method_name);
        e.set_method_name(m_name);
        e.set_block(block);
        return run(&e, self, argc, args, block);
        return m_fn(&e, self, argc, args, block);
    }

    const char *name() { return m_name; }
    ModuleValue *owner() { return m_owner; }

private:
    const char *m_name;
    ModuleValue *m_owner;
    MethodFnPtr m_fn;
    Env m_env {};
    bool m_undefined { false };

M include/natalie/method_value.hpp => include/natalie/method_value.hpp +6 -9
@@ 4,6 4,7 @@
#include "natalie/env.hpp"
#include "natalie/forward.hpp"
#include "natalie/global_env.hpp"
#include "natalie/method.hpp"
#include "natalie/string_value.hpp"
#include "natalie/symbol_value.hpp"
#include "natalie/value.hpp"


@@ 11,33 12,29 @@
namespace Natalie {

struct MethodValue : Value {
    MethodValue(Env *env, ModuleValue *owner, SymbolValue *owner_name, bool owner_is_singleton, SymbolValue *name, Method *method)
    MethodValue(Env *env, SymbolValue *owner_name, bool owner_is_singleton, Method *method)
        : Value { Value::Type::Method, env->Object()->const_fetch(env, "Method")->as_class() }
        , m_owner { owner }
        , m_owner_name { owner_name }
        , m_owner_is_singleton { owner_is_singleton }
        , m_name { name }
        , m_method { method } { }

    MethodValue(Env *env, ClassValue *klass)
        : Value { Value::Type::Method, klass } { }

    ModuleValue *owner() { return m_owner; }
    SymbolValue *name() { return m_name; }
    ModuleValue *owner() { return m_method->owner(); }
    SymbolValue *name(Env *env) { return SymbolValue::intern(env, m_method->name()); }
    Method *method() { return m_method; }

    ValuePtr inspect(Env *env) {
        if (m_owner_is_singleton)
            return StringValue::sprintf(env, "#<Method: %s.%s(*)>", m_owner_name->c_str(), m_name->c_str());
            return StringValue::sprintf(env, "#<Method: %s.%s(*)>", m_owner_name->c_str(), m_method->name());
        else
            return StringValue::sprintf(env, "#<Method: %s#%s(*)>", m_owner_name->c_str(), m_name->c_str());
            return StringValue::sprintf(env, "#<Method: %s#%s(*)>", m_owner_name->c_str(), m_method->name());
    }

private:
    ModuleValue *m_owner { nullptr };
    SymbolValue *m_owner_name { nullptr };
    bool m_owner_is_singleton { false };
    SymbolValue *m_name { nullptr };
    Method *m_method { nullptr };
};
}

M src/kernel_module.cpp => src/kernel_module.cpp +2 -2
@@ 175,12 175,12 @@ ValuePtr KernelModule::method(Env *env, Value *name) {
                env->raise("NoMethodError", "undefined method `%s' for %s:Class", name, m_klass->class_name());
            }
            auto owner_name = this->inspect(env)->to_symbol(env, Conversion::Strict);
            return new MethodValue { env, singleton, owner_name, true, name_symbol, method };
            return new MethodValue { env, owner_name, true, method };
        }
    }
    Method *method = m_klass->find_method(env, name_symbol->c_str());
    if (method)
        return new MethodValue { env, m_klass, SymbolValue::intern(env, m_klass->class_name()), false, name_symbol, method };
        return new MethodValue { env, SymbolValue::intern(env, m_klass->class_name()), false, method };
    env->raise("NoMethodError", "undefined method `%s' for %s:Class", name, m_klass->class_name());
}


M src/module_value.cpp => src/module_value.cpp +4 -5
@@ 178,13 178,13 @@ ValuePtr ModuleValue::cvar_set(Env *env, const char *name, ValuePtr val) {
}

void ModuleValue::define_method(Env *env, const char *name, MethodFnPtr fn) {
    Method *method = new Method { fn };
    Method *method = new Method { name, this, fn };
    GC_FREE(hashmap_remove(env, &m_methods, name));
    hashmap_put(env, &m_methods, name, method);
}

void ModuleValue::define_method_with_block(Env *env, const char *name, Block *block) {
    Method *method = new Method { block };
    Method *method = new Method { name, this, block };
    GC_FREE(hashmap_remove(env, &m_methods, name));
    hashmap_put(env, &m_methods, name, method);
}


@@ 240,10 240,9 @@ Method *ModuleValue::find_method(Env *env, const char *method_name, ModuleValue 
}

ValuePtr ModuleValue::call_method(Env *env, ValuePtr instance_class, const char *method_name, ValuePtr self, size_t argc, ValuePtr *args, Block *block) {
    ModuleValue *method_owner;
    Method *method = find_method(env, method_name, &method_owner);
    Method *method = find_method(env, method_name);
    if (method && !method->is_undefined()) {
        return method->call(env, method_owner, method_name, self, argc, args, block);
        return method->call(env, self, argc, args, block);
    } else if (self->is_module()) {
        env->raise("NoMethodError", "undefined method `%s' for %s:%v", method_name, self->as_module()->class_name(), instance_class);
    } else if (strcmp(method_name, "inspect") == 0) {

M src/value.cpp => src/value.cpp +5 -10
@@ 49,10 49,6 @@ ValuePtr Value::_new(Env *env, ValuePtr klass_value, size_t argc, ValuePtr *args
        obj = new MatchDataValue { env, klass };
        break;

    case Value::Type::Method:
        obj = new MethodValue { env, klass };
        break;

    case Value::Type::Module:
        obj = new ModuleValue { env, klass };
        break;


@@ 81,6 77,7 @@ ValuePtr Value::_new(Env *env, ValuePtr klass_value, size_t argc, ValuePtr *args
        obj = new VoidPValue { env, klass };
        break;

    case Value::Type::Method:
    case Value::Type::Nil:
    case Value::Type::False:
    case Value::Type::True:


@@ 94,10 91,9 @@ ValuePtr Value::_new(Env *env, ValuePtr klass_value, size_t argc, ValuePtr *args
}

ValuePtr Value::initialize(Env *env, size_t argc, ValuePtr *args, Block *block) {
    ModuleValue *method_owner;
    Method *method = m_klass->find_method(env, "initialize", &method_owner);
    Method *method = m_klass->find_method(env, "initialize");
    if (method && !method->is_undefined()) {
        method->call(env, method_owner, "initialize", this, argc, args, block);
        method->call(env, this, argc, args, block);
    }
    return this;
}


@@ 418,12 414,11 @@ void Value::undefine_method(Env *env, const char *name) {
ValuePtr Value::send(Env *env, const char *name, size_t argc, ValuePtr *args, Block *block) {
    auto singleton = singleton_class();
    if (singleton) {
        ModuleValue *method_owner;
        Method *method = singleton_class()->find_method(env, name, &method_owner);
        Method *method = singleton_class()->find_method(env, name);
        if (method) {
            if (method->is_undefined())
                env->raise("NoMethodError", "undefined method `%s' for %s:Class", name, m_klass->class_name());
            return method->call(env, method_owner, name, this, argc, args, block);
            return method->call(env, this, argc, args, block);
        }
    }
    return m_klass->call_method(env, m_klass, name, this, argc, args, block);