c_extensions/c_calls.rb
tests/_examples/c_extensions/c_calls.rb in the metorex repository.
# A C extension calling back into Ruby: methods by name, constants, Marshal,
# finalizers, the integer conversions, and classes and modules defined under
# a module.
require "tmpdir"
require "stringio"
require_relative "build_helper"
class Recorder
def initialize
@calls = []
end
attr_reader :calls
def record(first, second)
@calls << [first, second]
:recorded
end
def binding
:written_in_ruby
end
end
class Number
def initialize(value)
@value = value
end
def to_int
@value
end
end
module Outer
autoload :Loaded, File.join(__dir__, "autoloaded_class.rb")
end
directory = Dir.mktmpdir
require(build_extension("c_calls.c", "c_calls", directory))
calls = CCalls.new
p(calls.symbol_for(:name))
p(calls.same_id(:name, :name))
p(calls.same_id(:name, :other))
report { calls.symbol_for("name") }
recorder = Recorder.new
p(calls.call_two(recorder, :record, 1, "two"))
p(recorder.calls)
p(calls.call_none([3, 1, 2], :sort))
p(calls.call_none(recorder, :binding))
report { calls.call_none(Object.new, :binding) }
report { calls.call_none(1, :no_such_method) }
p(calls.constant(Object, :Comparable))
p(calls.constant(Outer, :Loaded))
report { calls.constant(Object, :NoSuchConstant) }
data = calls.dump("text", nil)
p(data == Marshal.dump("text"))
port = StringIO.new
calls.dump([1, 2], port)
p(port.string == Marshal.dump([1, 2]))
p(calls.load(data))
finalizer = proc { puts "finalizer ran" }
p(calls.define_finalizer(Object.new, finalizer).equal?(finalizer))
report { calls.define_finalizer(Object.new, 1) }
removed = Object.new
ObjectSpace.define_finalizer(removed, proc { puts "removed finalizer ran" })
p(calls.undefine_finalizer(removed).equal?(removed))
p([calls.to_long(42), calls.to_long(-7), calls.to_long(3.99), calls.to_long(Number.new(5))])
report { calls.to_long(nil) }
report { calls.to_long(Float::INFINITY) }
report { calls.to_long(2**64) }
report { calls.to_long("1") }
p([calls.quarter_unsigned_long(-1), calls.quarter_unsigned_long(2**63)])
report { calls.quarter_unsigned_long(2**64) }
p([calls.to_int(2**31 - 1), calls.to_int(-2**31)])
report { calls.to_int(2**31) }
report { calls.to_int(-2**31 - 1) }
p([calls.to_unsigned_int(2**32 - 1), calls.to_unsigned_int(-1)])
report { calls.to_unsigned_int(2**32) }
report { calls.to_unsigned_int(-2**31 - 1) }
p([calls.fix_to_int(-14), calls.fix_to_unsigned_int(42)])
module Holder
end
inner = calls.class_under(Holder, :Inner, Object)
p(inner)
p(calls.class_under(Holder, :Inner, Object).equal?(inner))
report { calls.class_under(Holder, :Inner, String) }
Holder.const_set(:Taken, 1)
report { calls.class_under(Holder, :Taken, Object) }
Parent = Class.new
p(calls.class_id_under(Holder, :ById, Parent))
p(Holder::ById.superclass)
p(calls.class_under(Outer, :Loaded, Object))
top = calls.module(:CTopModule)
p(top)
p(calls.module(:CTopModule).equal?(top))
p(calls.module_under(top, :Nested))
report { calls.module(:String) }
FileUtils.rm_rf(directory)
Output:
:name
true
false
TypeError: wrong argument type String (expected symbol)
:recorded
[[1, "two"]]
[1, 2, 3]
:written_in_ruby
RuntimeError: Cannot create Binding object for non-Ruby caller
NoMethodError: undefined method 'no_such_method' for an instance of Integer
Comparable
Outer::Loaded
NameError: uninitialized constant NoSuchConstant
true
true
"text"
true
ArgumentError: wrong type argument Integer (should be callable)
true
[42, -7, 3, 5]
TypeError: no implicit conversion from nil to integer
RangeError: float inf out of range of integer
RangeError: bignum too big to convert into 'long'
TypeError: no implicit conversion of String into Integer
[4611686018427387903, 2305843009213693952]
RangeError: bignum out of range of unsigned long
[2147483647, -2147483648]
RangeError: integer 2147483648 too big to convert to 'int'
RangeError: integer -2147483649 too small to convert to 'int'
[4294967295, 4294967295]
RangeError: integer 4294967296 too big to convert to 'unsigned int'
RangeError: integer -2147483649 too small to convert to 'unsigned int'
[-14, 42]
Holder::Inner
true
TypeError: superclass mismatch for class Holder::Inner (String is given but was Object)
TypeError: Holder::Taken is not a class (Integer)
Holder::ById
Parent
Outer::Loaded
CTopModule
true
CTopModule::Nested
TypeError: String is not a module (Class)
finalizer ran