~tim/natalie

f5a482b5ccd757b3cd29779e40922a6db95e541a — Tim Morgan a month ago 8e9e487
Add Method#owner and Class#singleton_class?
M include/natalie/class_value.hpp => include/natalie/class_value.hpp +4 -0
@@ 45,8 45,12 @@ struct ClassValue : ModuleValue {
        return klass;
    }

    bool is_singleton() { return m_is_singleton; }
    void set_is_singleton(bool is_singleton) { m_is_singleton = is_singleton; }

private:
    Type m_object_type { Type::Object };
    bool m_is_singleton { false };
};

}

M include/natalie/method_value.hpp => include/natalie/method_value.hpp +7 -8
@@ 12,10 12,9 @@
namespace Natalie {

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

    MethodValue(Env *env, ClassValue *klass)


@@ 26,15 25,15 @@ struct MethodValue : Value {
    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_method->name());
        auto the_owner = owner();
        if (the_owner->is_class() && the_owner->as_class()->is_singleton())
            return StringValue::sprintf(env, "#<Method: %s.%s(*)>", m_object->inspect_str(env), m_method->name());
        else
            return StringValue::sprintf(env, "#<Method: %s#%s(*)>", m_owner_name->c_str(), m_method->name());
            return StringValue::sprintf(env, "#<Method: %s#%s(*)>", owner()->class_name(), m_method->name());
    }

private:
    SymbolValue *m_owner_name { nullptr };
    bool m_owner_is_singleton { false };
    Value *m_object { nullptr };
    Method *m_method { nullptr };
};
}

M include/natalie/value.hpp => include/natalie/value.hpp +1 -1
@@ 170,7 170,7 @@ struct Value : public gc {
    ClassValue *singleton_class() { return m_singleton_class; }
    ClassValue *singleton_class(Env *);

    void set_singleton_class(ClassValue *c) { m_singleton_class = c; }
    void set_singleton_class(ClassValue *);

    virtual ValuePtr const_get(Env *, const char *);
    virtual ValuePtr const_fetch(Env *, const char *);

M lib/natalie/compiler/binding_gen.rb => lib/natalie/compiler/binding_gen.rb +2 -0
@@ 275,6 275,7 @@ gen.binding('BasicObject', 'instance_eval', 'Value', 'instance_eval', argc: 0..1

gen.static_binding('Class', 'new', 'ClassValue', 'new_method', argc: 0..1, pass_env: true, pass_block: true, return_type: :Value)
gen.binding('Class', 'superclass', 'ClassValue', 'superclass', argc: 0, pass_env: false, pass_block: false, return_type: :NullableValue)
gen.binding('Class', 'singleton_class?', 'ClassValue', 'is_singleton', argc: 0, pass_env: false, pass_block: false, return_type: :bool)

gen.static_binding('Encoding', 'list', 'EncodingValue', 'list', argc: 0, pass_env: true, pass_block: false, return_type: :Value)
gen.binding('Encoding', 'inspect', 'EncodingValue', 'inspect', argc: 0, pass_env: true, pass_block: false, return_type: :Value)


@@ 460,6 461,7 @@ gen.binding('MatchData', 'to_s', 'MatchDataValue', 'to_s', argc: 0, pass_env: tr
gen.binding('MatchData', '[]', 'MatchDataValue', 'ref', argc: 1, pass_env: true, pass_block: false, return_type: :Value)

gen.binding('Method', 'inspect', 'MethodValue', 'inspect', argc: 0, pass_env: true, pass_block: false, return_type: :Value)
gen.binding('Method', 'owner', 'MethodValue', 'owner', argc: 0, pass_env: false, pass_block: false, return_type: :Value)

gen.binding('Module', '===', 'ModuleValue', 'eqeqeq', argc: 1, pass_env: true, pass_block: false, return_type: :bool)
gen.binding('Module', 'alias_method', 'ModuleValue', 'alias_method', argc: 2, pass_env: true, pass_block: false, return_type: :Value)

M src/kernel_module.cpp => src/kernel_module.cpp +2 -3
@@ 174,13 174,12 @@ ValuePtr KernelModule::method(Env *env, Value *name) {
            if (method->is_undefined()) {
                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, owner_name, true, method };
            return new MethodValue { env, this, method };
        }
    }
    Method *method = m_klass->find_method(env, name_symbol->c_str());
    if (method)
        return new MethodValue { env, SymbolValue::intern(env, m_klass->class_name()), false, method };
        return new MethodValue { env, this, method };
    env->raise("NoMethodError", "undefined method `%s' for %s:Class", name, m_klass->class_name());
}


M src/value.cpp => src/value.cpp +6 -0
@@ 248,6 248,11 @@ SymbolValue *Value::to_symbol(Env *env, Conversion conversion) {
    }
}

void Value::set_singleton_class(ClassValue *klass) {
    klass->set_is_singleton(true);
    m_singleton_class = klass;
}

ClassValue *Value::singleton_class(Env *env) {
    if (m_singleton_class) {
        return m_singleton_class;


@@ 258,6 263,7 @@ ClassValue *Value::singleton_class(Env *env) {
    }

    m_singleton_class = m_klass->subclass(env);
    m_singleton_class->set_is_singleton(true);
    if (is_module()) {
        char name[255];
        snprintf(name, 255, "#<Class:%s>", as_module()->class_name());

M test/natalie/method_test.rb => test/natalie/method_test.rb +2 -0
@@ 315,9 315,11 @@ describe 'method' do
  describe '#method' do
    it 'returns a method object' do
      Foo.new.method(:foo).should be_an_instance_of(Method)
      Foo.new.method(:foo).owner.should == Foo
      Foo.new.method('foo').should be_an_instance_of(Method)
      Foo.new.method('foo').inspect.should =~ /^#<Method: Foo#foo.*>$/
      Foo.method('foo1').inspect.should =~ /^#<Method: Foo.foo1.*>$/
      Foo.method('foo1').owner.should == Foo.singleton_class
    end
  end
end

M test/natalie/singleton_class_test.rb => test/natalie/singleton_class_test.rb +5 -0
@@ 38,4 38,9 @@ describe 'singleton_class' do
    klass = klass.superclass
    klass.should == Class
  end

  it 'knows it is a singleton class' do
    Bar.singleton_class.singleton_class?.should == true
    Bar.singleton_class?.should == false
  end
end