can extend Ruby by writing extensions in C. - An extension is a C program that you can use from Ruby. - RubyGems automatically compiles extensions on installation. WHAT ARE C EXTENSIONS?
extension allows changing an object’s class at runtime - Code at github.com/camertron/trans fi gure OUR GOAL class Foo; end class Bar; end Foo.new.transfigure_into!(Bar) # => #<Bar...>
end class Parent < Grandparent def foo = method(:foo).super_method.call end class Child < Parent def foo = method(:foo).super_method.call end Child.new.foo
from (irb):6:in `foo' from (irb):6:in `call' from (irb):6:in `foo' from (irb):6:in `call' from (irb):6:in `foo' from (irb):6:in `call' from (irb):6:in `foo' from (irb):6:in `call' ... 11884 levels... from <internal:kernel>:187:in `loop' from /Users/camertron/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/ irb-1.14.0/exe/irb:9:in `<top (required)>' from /Users/camertron/.asdf/installs/ruby/3.3.0/bin/irb:25:in `load' from /Users/camertron/.asdf/installs/ruby/3.3.0/bin/irb:25:in `<main>'
Bar end it "dynamically changes the object's class" do obj = Foo.new expect(obj).to be_a(Foo) obj.transfigure_into!(Bar) expect(obj).to be_a(Bar) end end
struct - nil is represented by a VALUE called Qnil - Ruby methods are de fi ned via the rb_define_method function. - Ruby methods de fi ned in C receive a VALUE called self as their fi rst argument - You can grab a reference to Ruby’s Object class via rb_cObject. - Native extensions de fi ne an initialization function that’s called when the extension is required THE RUBY C API
$> bundle exec rake spec Failures: 1) transfigure_into! dynamically changes the object's class Failure/Error: expect(obj).to be_a(Bar) expected #<Foo:0x000000010011f188> to be a kind of Bar # ./spec/transfigure_spec.rb:23:in `block (2 levels) in <top (required)>'
All Ruby objects have them in common. */ struct RUBY_ALIGNAS(SIZEOF_VALUE) RBasic { /** * Per-object flags... */ VALUE flags; /** * Class of an object. Every object has its class. Also, everything * is an object in Ruby... */ const VALUE klass; };
../../../../ext/transfigure/transfigure.c:17:25: error: cannot assign to non-static data member 'klass' with const-qualified type 'const VALUE' (aka 'const unsigned long') RBASIC(self)->klass = target_klass; ~~~~~~~~~~~~~~~~~~~ ^
objects have them in common. */ struct RUBY_ALIGNAS(SIZEOF_VALUE) RBasic { /** * Per-object flags... */ VALUE flags; /** * Class of an object. Every object has its class. Also, everything * is an object in Ruby... */ const VALUE klass; };
objects have them in common. */ struct RUBY_ALIGNAS(SIZEOF_VALUE) RBasic { /** * Per-object flags... */ VALUE flags; /** * Class of an object. Every object has its class. Also, everything * is an object in Ruby... */ const VALUE klass; };
have them in common. */ struct RUBY_ALIGNAS(SIZEOF_VALUE) RBasic { /** * Per-object flags... */ VALUE flags; /** * Class of an object. Every object has its class. Also, everything * is an object in Ruby... */ const VALUE klass; };
memory. - Accessing fi elds of a struct is just a bit of math. - Memory address of struct + byte o ff set = address of desired fi eld STRUCTS IN C FLAGS KLASS STRUCT RBASIC } unsigned long (32 bits) } unsigned long (32 bits) Address of klass is struct address + 32
are essentially no guardrails or rules for casting in C. - You can cast anything to anything else. CASTING IN C char *foo = "foo"; printf("%d", (int)foo);
klass member isn’t constant. - What if we de fi ne our own struct and cast to it? - Memory is laid out the same as the original struct. TRICKING THE COMPILER struct RUBY_ALIGNAS(SIZEOF_VALUE) TFRBasic { VALUE flags; VALUE klass; };