Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-59866] Add rb_data_define() C function #3763

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Compatibility:
* Remove deprecated `Encoding#replicate` method (#3681, @rwstauner).
* Add `ObjectSpace::WeakMap#delete` (#3681, @andrykonchin).
* `Kernel#lambda` with now raises `ArgumentError` when given a non-lambda, non-literal block (#3681, @Th3-M4jor).
* Add `rb_data_define()` to define Data (#3681, @andrykonchin).

Performance:

Expand Down
14 changes: 14 additions & 0 deletions lib/cext/include/ruby/internal/intern/struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ RBIMPL_ATTR_NONNULL((2))
*/
VALUE rb_struct_define_without_accessor_under(VALUE outer, const char *class_name, VALUE super, rb_alloc_func_t alloc, ...);

#ifdef TRUFFLERUBY
VALUE rb_tr_data_define_va_list(VALUE super, va_list args);
#endif

/**
* Defines an anonymous data class.
*
Expand All @@ -252,7 +256,17 @@ VALUE rb_struct_define_without_accessor_under(VALUE outer, const char *class_nam
* @exception rb_eArgError Duplicated field name.
* @return The defined class.
*/
#ifdef TRUFFLERUBY
static inline VALUE rb_data_define(VALUE super, ...) {
va_list args;
va_start(args, super);
VALUE result = rb_tr_data_define_va_list(super == 0 ? Qnil : super, args);
va_end(args);
return result;
}
#else
VALUE rb_data_define(VALUE super, ...);
#endif

RBIMPL_SYMBOL_EXPORT_END()

Expand Down
2 changes: 1 addition & 1 deletion lib/cext/include/truffleruby/truffleruby-abi-version.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
// $RUBY_VERSION must be the same as TruffleRuby.LANGUAGE_VERSION.
// $ABI_NUMBER starts at 1 and is incremented for every ABI-incompatible change.

#define TRUFFLERUBY_ABI_VERSION "3.3.5.8"
#define TRUFFLERUBY_ABI_VERSION "3.3.5.9"

#endif
11 changes: 11 additions & 0 deletions lib/truffle/truffle/cext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1949,6 +1949,17 @@ def rb_struct_new_no_splat(klass, args)
klass.new(*args)
end

def rb_data_define_no_splat(klass, attrs)
klass ||= Data
Truffle::Type.rb_check_type(klass, Class)

unless klass <= Data
raise TypeError, 'invalid Class for rb_data_define(), expected Data or a subclass of Data'
end

klass.define(*attrs)
end

def yield_no_block
raise LocalJumpError
end
Expand Down
24 changes: 20 additions & 4 deletions spec/ruby/optional/capi/ext/struct_spec.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static VALUE struct_spec_rb_struct_aset(VALUE self, VALUE st, VALUE key, VALUE v
}

/* Only allow setting three attributes, should be sufficient for testing. */
static VALUE struct_spec_struct_define(VALUE self, VALUE name,
static VALUE struct_spec_rb_struct_define(VALUE self, VALUE name,
VALUE attr1, VALUE attr2, VALUE attr3) {

const char *a1 = StringValuePtr(attr1);
Expand All @@ -42,7 +42,7 @@ static VALUE struct_spec_struct_define(VALUE self, VALUE name,
}

/* Only allow setting three attributes, should be sufficient for testing. */
static VALUE struct_spec_struct_define_under(VALUE self, VALUE outer,
static VALUE struct_spec_rb_struct_define_under(VALUE self, VALUE outer,
VALUE name, VALUE attr1, VALUE attr2, VALUE attr3) {

const char *nm = StringValuePtr(name);
Expand All @@ -62,17 +62,33 @@ static VALUE struct_spec_rb_struct_size(VALUE self, VALUE st) {
return rb_struct_size(st);
}

/* Only allow setting three attributes, should be sufficient for testing. */
static VALUE struct_spec_rb_data_define(VALUE self, VALUE superclass,
VALUE attr1, VALUE attr2, VALUE attr3) {

const char *a1 = StringValuePtr(attr1);
const char *a2 = StringValuePtr(attr2);
const char *a3 = StringValuePtr(attr3);

if (superclass == Qnil) {
superclass = 0;
}

return rb_data_define(superclass, a1, a2, a3, NULL);
}

void Init_struct_spec(void) {
VALUE cls = rb_define_class("CApiStructSpecs", rb_cObject);
rb_define_method(cls, "rb_struct_aref", struct_spec_rb_struct_aref, 2);
rb_define_method(cls, "rb_struct_getmember", struct_spec_rb_struct_getmember, 2);
rb_define_method(cls, "rb_struct_s_members", struct_spec_rb_struct_s_members, 1);
rb_define_method(cls, "rb_struct_members", struct_spec_rb_struct_members, 1);
rb_define_method(cls, "rb_struct_aset", struct_spec_rb_struct_aset, 3);
rb_define_method(cls, "rb_struct_define", struct_spec_struct_define, 4);
rb_define_method(cls, "rb_struct_define_under", struct_spec_struct_define_under, 5);
rb_define_method(cls, "rb_struct_define", struct_spec_rb_struct_define, 4);
rb_define_method(cls, "rb_struct_define_under", struct_spec_rb_struct_define_under, 5);
rb_define_method(cls, "rb_struct_new", struct_spec_rb_struct_new, 4);
rb_define_method(cls, "rb_struct_size", struct_spec_rb_struct_size, 1);
rb_define_method(cls, "rb_data_define", struct_spec_rb_data_define, 4);
}

#ifdef __cplusplus
Expand Down
49 changes: 49 additions & 0 deletions spec/ruby/optional/capi/struct_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,52 @@
end
end
end

ruby_version_is "3.3" do
describe "C-API Data function" do
before :each do
@s = CApiStructSpecs.new
end

describe "rb_data_define" do
it "returns a subclass of Data class when passed nil as the first argument" do
klass = @s.rb_data_define(nil, "a", "b", "c")

klass.should.is_a? Class
klass.superclass.should == Data
end

it "returns a subclass of a class when passed as the first argument" do
superclass = Class.new(Data)
klass = @s.rb_data_define(superclass, "a", "b", "c")

klass.should.is_a? Class
klass.superclass.should == superclass
end

it "creates readers for the members" do
klass = @s.rb_data_define(nil, "a", "b", "c")
obj = klass.new(1, 2, 3)

obj.a.should == 1
obj.b.should == 2
obj.c.should == 3
end

it "returns the member names as Symbols" do
klass = @s.rb_data_define(nil, "a", "b", "c")
obj = klass.new(0, 0, 0)

obj.members.should == [:a, :b, :c]
end

it "raises an ArgumentError if arguments contain duplicate member name" do
-> { @s.rb_data_define(nil, "a", "b", "a") }.should raise_error(ArgumentError)
end

it "raises when first argument is not a class" do
-> { @s.rb_data_define([], "a", "b", "c") }.should raise_error(TypeError, "wrong argument type Array (expected Class)")
end
end
end
end
11 changes: 11 additions & 0 deletions src/main/c/cext/struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,14 @@ VALUE rb_struct_members(VALUE s) {
VALUE rb_struct_size(VALUE s) {
return RUBY_INVOKE(s, "size");
}

VALUE rb_tr_data_define_va_list(VALUE super, va_list args) {
VALUE ary = rb_ary_new();
int i = 0;
char *arg;
while ((arg = va_arg(args, char*)) != NULL) {
rb_ary_push(ary, rb_str_new_cstr(arg));
i++;
}
return RUBY_CEXT_INVOKE("rb_data_define_no_splat", super, ary);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* Also useful to split Ruby methods which do something like `if obj.is_a?(Foo) then foo else bar end`
* such as NoBorderImagePadded#index used by NoBorderImage#[] in the image-demo benchmarks. */
@GenerateUncached
@ReportPolymorphism // see commment above
@ReportPolymorphism // see comment above
public abstract class IsANode extends RubyBaseNode {

@NeverDefault
Expand Down
Loading