Skip to content

Commit

Permalink
Add Class#attached_object method
Browse files Browse the repository at this point in the history
  • Loading branch information
andrykonchin committed Jan 24, 2024
1 parent eedda85 commit b41ec83
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Compatibility:
* Make `Coverage.start` and `Coverage.result` accept parameters (#3149, @mtortonesi, @andrykonchin).
* Implement `rb_check_funcall()` (@eregon).
* Implement `MatchData#{byteoffset,deconstruct,deconstruct_keys}` from Ruby 3.2 (#3039, @rwstauner).
* Implement `Class#attached_object` method (#3039, @andrykonchin).

Performance:

Expand Down
8 changes: 4 additions & 4 deletions spec/ruby/core/class/attached_object_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
it "raises TypeError if the class is not a singleton class" do
a = Class.new

-> { a.attached_object }.should raise_error(TypeError)
-> { a.attached_object }.should raise_error(TypeError, /is not a singleton class/)
end

it "raises TypeError for special singleton classes" do
-> { nil.singleton_class.attached_object }.should raise_error(TypeError)
-> { true.singleton_class.attached_object }.should raise_error(TypeError)
-> { false.singleton_class.attached_object }.should raise_error(TypeError)
-> { nil.singleton_class.attached_object }.should raise_error(TypeError, /`NilClass' is not a singleton class/)
-> { true.singleton_class.attached_object }.should raise_error(TypeError, /`TrueClass' is not a singleton class/)
-> { false.singleton_class.attached_object }.should raise_error(TypeError, /`FalseClass' is not a singleton class/)
end
end
end
4 changes: 0 additions & 4 deletions spec/tags/core/class/attached_object_tags.txt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,12 @@ public RubyException typeErrorCantCreateInstanceOfSingletonClass(Node currentNod
return typeError("can't create instance of singleton class", currentNode, null);
}

@TruffleBoundary
public RubyException typeErrorNotASingletonClass(Node currentNode, RubyClass rubyClass) {
String className = rubyClass.fields.getName();
return typeError(StringUtils.format("`%s' is not a singleton class", className), currentNode, null);
}

@TruffleBoundary
public RubyException superclassMismatch(String name, Node currentNode) {
return typeError("superclass mismatch for class " + name, currentNode);
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/org/truffleruby/core/klass/ClassNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,22 @@ RubyClass newSingletonInstance(RubyClass rubyClass) {
}
}

@CoreMethod(names = "attached_object")
public abstract static class AttachedObjectNode extends CoreMethodArrayArgumentsNode {

@Specialization(guards = "rubyClass.isSingleton")
Object attachedObject(RubyClass rubyClass) {
return rubyClass.attached;
}

@Specialization(guards = "!rubyClass.isSingleton")
Object attachedObjectOfNonSingletonClass(RubyClass rubyClass) {
throw new RaiseException(
getContext(),
getContext().getCoreExceptions().typeErrorNotASingletonClass(this, rubyClass));
}
}

// Worth always splitting to have monomorphic #__allocate__ and #initialize,
// Worth always inlining as the field accesses and initializations are optimized when the allocation is visible,
// and a non-inlined call to #__allocate__ would allocate the arguments Object[] which is about the same number of
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/truffleruby/core/klass/RubyClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public final class RubyClass extends RubyModule implements ObjectGraphNode {
public final boolean isSingleton;
/** If this is an object's metaclass, then nonSingletonClass is the logical class of the object. */
public final RubyClass nonSingletonClass;
/** Ruby object which this class is a metaclass/singleton class of */
public final RubyDynamicObject attached;
/* a RubyClass or nil for BasicObject */
public final Object superclass;
Expand All @@ -57,6 +58,7 @@ public RubyClass(
Object superclass) {
super(classClass, language.classShape, language, sourceSection, lexicalParent, givenBaseName);
assert !isSingleton || givenBaseName == null;
assert isSingleton == (attached != null);
this.isSingleton = isSingleton;
this.attached = attached;
this.nonSingletonClass = computeNonSingletonClass(isSingleton, superclass);
Expand All @@ -83,7 +85,6 @@ public RubyClass(
fields.afterConstructed();
}


/** Special constructor to build the 'Class' RubyClass itself. */
RubyClass(RubyLanguage language, Shape classShape) {
super(language, classShape, "constructor only for the class Class");
Expand Down
1 change: 0 additions & 1 deletion test/mri/excludes/TestClass.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,3 @@
exclude :test_subclass_gc, "NoMethodError: undefined method `subclasses' for #<Class:0x25fd48>"
exclude :test_subclass_gc_stress, "NoMethodError: undefined method `subclasses' for #<Class:0x11c93f8>"
exclude :test_subclasses, "NoMethodError: undefined method `subclasses' for #<Class:0x11c9b48>"
exclude :test_attached_object, "NoMethodError: undefined method `attached_object' for #<Class:#<#<Class:0x197b18>:0x197b28>>"

0 comments on commit b41ec83

Please sign in to comment.