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