2022-09-05 00:53:46 -04:00
|
|
|
module RubyVM::MJIT
|
|
|
|
# Every class under this namespace is a pointer. Even if the type is
|
|
|
|
# immediate, it shouldn't be dereferenced until `*` is called.
|
|
|
|
module CPointer
|
|
|
|
# Note: We'd like to avoid alphabetic method names to avoid a conflict
|
|
|
|
# with member methods. to_i and to_s are considered an exception.
|
|
|
|
class Struct
|
|
|
|
# @param name [String]
|
|
|
|
# @param sizeof [Integer]
|
|
|
|
# @param members [Hash{ Symbol => [Integer, RubyVM::MJIT::CType::*] }]
|
|
|
|
def initialize(addr, sizeof, members)
|
|
|
|
@addr = addr
|
|
|
|
@sizeof = sizeof
|
|
|
|
@members = members
|
|
|
|
end
|
|
|
|
|
|
|
|
# Get a raw address
|
|
|
|
def to_i
|
|
|
|
@addr
|
|
|
|
end
|
|
|
|
|
|
|
|
# Serialized address for generated code
|
|
|
|
def to_s
|
|
|
|
"0x#{@addr.to_s(16)}"
|
|
|
|
end
|
|
|
|
|
|
|
|
# Pointer diff
|
|
|
|
def -(struct)
|
|
|
|
raise ArgumentError if self.class != struct.class
|
|
|
|
(@addr - struct.to_i) / @sizeof
|
|
|
|
end
|
|
|
|
|
|
|
|
# Primitive API that does no automatic dereference
|
|
|
|
# TODO: remove this?
|
|
|
|
# @param member [Symbol]
|
|
|
|
def [](member)
|
|
|
|
offset, type = @members.fetch(member)
|
|
|
|
type.new(@addr + offset / 8)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
# @param member [Symbol]
|
|
|
|
# @param value [Object]
|
|
|
|
def []=(member, value)
|
|
|
|
offset, type = @members.fetch(member)
|
|
|
|
type[@addr + offset / 8] = value
|
|
|
|
end
|
|
|
|
|
|
|
|
# @param sizeof [Integer]
|
|
|
|
# @param members [Hash{ Symbol => [Integer, RubyVM::MJIT::CType::*] }]
|
|
|
|
def self.define(sizeof, members)
|
|
|
|
Class.new(self) do
|
|
|
|
# Return the size of this type
|
|
|
|
define_singleton_method(:sizeof) { sizeof }
|
|
|
|
|
|
|
|
define_method(:initialize) do |addr = nil|
|
|
|
|
if addr.nil? # TODO: get rid of this feature later
|
|
|
|
addr = Fiddle.malloc(sizeof)
|
|
|
|
end
|
|
|
|
super(addr, sizeof, members)
|
|
|
|
end
|
|
|
|
|
|
|
|
members.each do |member, (offset, type, to_ruby)|
|
|
|
|
# Intelligent API that does automatic dereference
|
|
|
|
define_method(member) do
|
|
|
|
value = self[member]
|
|
|
|
if value.respond_to?(:*)
|
|
|
|
value = value.*
|
|
|
|
end
|
|
|
|
if to_ruby
|
|
|
|
value = C.to_ruby(value)
|
|
|
|
end
|
|
|
|
value
|
|
|
|
end
|
|
|
|
|
|
|
|
define_method("#{member}=") do |value|
|
|
|
|
self[member] = value
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Note: We'd like to avoid alphabetic method names to avoid a conflict
|
|
|
|
# with member methods. to_i is considered an exception.
|
|
|
|
class Union
|
|
|
|
# @param _name [String] To be used when it starts defining a union pointer class
|
|
|
|
# @param sizeof [Integer]
|
|
|
|
# @param members [Hash{ Symbol => RubyVM::MJIT::CType::* }]
|
|
|
|
def initialize(addr, sizeof, members)
|
|
|
|
@addr = addr
|
|
|
|
@sizeof = sizeof
|
|
|
|
@members = members
|
|
|
|
end
|
|
|
|
|
|
|
|
# Get a raw address
|
|
|
|
def to_i
|
|
|
|
@addr
|
|
|
|
end
|
|
|
|
|
|
|
|
# Move addr to access this pointer like an array
|
|
|
|
def +(index)
|
|
|
|
raise ArgumentError unless index.is_a?(Integer)
|
|
|
|
self.class.new(@addr + index * @sizeof)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Pointer diff
|
|
|
|
def -(union)
|
|
|
|
raise ArgumentError if self.class != union.class
|
|
|
|
(@addr - union.instance_variable_get(:@addr)) / @sizeof
|
|
|
|
end
|
|
|
|
|
|
|
|
# @param sizeof [Integer]
|
|
|
|
# @param members [Hash{ Symbol => RubyVM::MJIT::CType::* }]
|
|
|
|
def self.define(sizeof, members)
|
|
|
|
Class.new(self) do
|
|
|
|
# Return the size of this type
|
|
|
|
define_singleton_method(:sizeof) { sizeof }
|
|
|
|
|
|
|
|
define_method(:initialize) do |addr|
|
|
|
|
super(addr, sizeof, members)
|
|
|
|
end
|
|
|
|
|
|
|
|
members.each do |member, type|
|
|
|
|
# Intelligent API that does automatic dereference
|
|
|
|
define_method(member) do
|
|
|
|
value = type.new(@addr)
|
|
|
|
if value.respond_to?(:*)
|
|
|
|
value = value.*
|
|
|
|
end
|
|
|
|
value
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Immediate
|
|
|
|
# @param addr [Integer]
|
|
|
|
# @param size [Integer]
|
|
|
|
# @param pack [String]
|
|
|
|
def initialize(addr, size, pack)
|
|
|
|
@addr = addr
|
|
|
|
@size = size
|
|
|
|
@pack = pack
|
|
|
|
end
|
|
|
|
|
|
|
|
# Get a raw address
|
|
|
|
def to_i
|
|
|
|
@addr
|
|
|
|
end
|
|
|
|
|
|
|
|
# Move addr to addess this pointer like an array
|
|
|
|
def +(index)
|
|
|
|
Immediate.new(@addr + index * @size, @size, @pack)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Dereference
|
|
|
|
def *
|
|
|
|
self[0]
|
|
|
|
end
|
|
|
|
|
|
|
|
# Array access
|
|
|
|
def [](index)
|
|
|
|
return nil if @addr == 0
|
|
|
|
Fiddle::Pointer.new(@addr + index * @size)[0, @size].unpack1(@pack)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Array set
|
|
|
|
def []=(index, value)
|
|
|
|
Fiddle::Pointer.new(@addr + index * @size)[0, @size] = [value].pack(@pack)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Serialized address for generated code. Used for embedding things like body->iseq_encoded.
|
|
|
|
def to_s
|
|
|
|
"0x#{Integer(@addr).to_s(16)}"
|
|
|
|
end
|
|
|
|
|
|
|
|
# @param fiddle_type [Integer] Fiddle::TYPE_*
|
|
|
|
def self.define(fiddle_type)
|
|
|
|
size = Fiddle::PackInfo::SIZE_MAP.fetch(fiddle_type)
|
2022-09-11 02:39:32 -04:00
|
|
|
pack = Fiddle::PackInfo::PACK_MAP.fetch(fiddle_type)
|
2022-09-05 00:53:46 -04:00
|
|
|
|
|
|
|
Class.new(self) do
|
|
|
|
define_method(:initialize) do |addr|
|
|
|
|
super(addr, size, pack)
|
|
|
|
end
|
|
|
|
|
|
|
|
define_singleton_method(:size) do
|
|
|
|
size
|
|
|
|
end
|
|
|
|
|
|
|
|
# Type-level []=: Used by struct fields
|
|
|
|
define_singleton_method(:[]=) do |addr, value|
|
|
|
|
Fiddle::Pointer.new(addr)[0, size] = [value].pack(pack)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# -Fiddle::TYPE_CHAR Immediate with special handling of true/false
|
|
|
|
class Bool < Immediate.define(-Fiddle::TYPE_CHAR)
|
|
|
|
# Dereference
|
|
|
|
def *
|
|
|
|
return nil if @addr == 0
|
|
|
|
super != 0
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.[]=(addr, value)
|
|
|
|
super(addr, value ? 1 : 0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class Pointer
|
|
|
|
attr_reader :type
|
|
|
|
|
|
|
|
# @param addr [Integer]
|
|
|
|
# @param type [Class] RubyVM::MJIT::CType::*
|
|
|
|
def initialize(addr, type)
|
|
|
|
@addr = addr
|
|
|
|
@type = type
|
|
|
|
end
|
|
|
|
|
|
|
|
# Move addr to addess this pointer like an array
|
|
|
|
def +(index)
|
|
|
|
raise ArgumentError unless index.is_a?(Integer)
|
|
|
|
Pointer.new(@addr + index * Fiddle::SIZEOF_VOIDP, @type)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Dereference
|
|
|
|
def *
|
|
|
|
return nil if dest_addr == 0
|
|
|
|
@type.new(dest_addr)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Array access
|
|
|
|
def [](index)
|
|
|
|
(self + index).*
|
|
|
|
end
|
|
|
|
|
|
|
|
# Array set
|
|
|
|
# @param index [Integer]
|
|
|
|
# @param value [Integer, RubyVM::MJIT::CPointer::Struct] an address itself or an object that return an address with to_i
|
|
|
|
def []=(index, value)
|
|
|
|
Fiddle::Pointer.new(@addr + index * Fiddle::SIZEOF_VOIDP)[0, Fiddle::SIZEOF_VOIDP] =
|
2022-09-11 02:39:32 -04:00
|
|
|
[value.to_i].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
|
2022-09-05 00:53:46 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def dest_addr
|
2022-09-11 02:39:32 -04:00
|
|
|
Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_VOIDP].unpack1(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
|
2022-09-05 00:53:46 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.define(block)
|
|
|
|
Class.new(self) do
|
|
|
|
define_method(:initialize) do |addr|
|
|
|
|
super(addr, block.call)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Type-level []=: Used by struct fields
|
|
|
|
# @param addr [Integer]
|
|
|
|
# @param value [Integer, RubyVM::MJIT::CPointer::Struct] an address itself, or an object that return an address with to_i
|
|
|
|
define_singleton_method(:[]=) do |addr, value|
|
|
|
|
value = value.to_i
|
2022-09-11 02:39:32 -04:00
|
|
|
Fiddle::Pointer.new(addr)[0, Fiddle::SIZEOF_VOIDP] = [value].pack(Fiddle::PackInfo::PACK_MAP[Fiddle::TYPE_VOIDP])
|
2022-09-05 00:53:46 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class BitField
|
|
|
|
# @param addr [Integer]
|
|
|
|
# @param width [Integer]
|
|
|
|
# @param offset [Integer]
|
|
|
|
def initialize(addr, width, offset)
|
|
|
|
@addr = addr
|
|
|
|
@width = width
|
|
|
|
@offset = offset
|
|
|
|
end
|
|
|
|
|
|
|
|
# Dereference
|
|
|
|
def *
|
|
|
|
if @width != 1
|
|
|
|
raise NotImplementedError.new("Non-1 width is not implemented yet")
|
|
|
|
end
|
|
|
|
byte = Fiddle::Pointer.new(@addr)[0, Fiddle::SIZEOF_CHAR].unpack('c').first
|
|
|
|
bit = 1 & (byte >> @offset)
|
|
|
|
bit == 1
|
|
|
|
end
|
|
|
|
|
|
|
|
# @param width [Integer]
|
|
|
|
# @param offset [Integer]
|
|
|
|
def self.define(width, offset)
|
|
|
|
Class.new(self) do
|
|
|
|
define_method(:initialize) do |addr|
|
|
|
|
super(addr, width, offset)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Give a name to a dynamic CPointer class to see it on inspect
|
|
|
|
def self.with_class_name(prefix, name, cache: false, &block)
|
|
|
|
return block.call if name.empty?
|
|
|
|
|
|
|
|
# Use a cached result only if cache: true
|
|
|
|
class_name = "#{prefix}_#{name}"
|
|
|
|
klass =
|
|
|
|
if cache && self.const_defined?(class_name)
|
|
|
|
self.const_get(class_name)
|
|
|
|
else
|
|
|
|
block.call
|
|
|
|
end
|
|
|
|
|
|
|
|
# Give it a name unless it's already defined
|
|
|
|
unless self.const_defined?(class_name)
|
|
|
|
self.const_set(class_name, klass)
|
|
|
|
end
|
|
|
|
|
|
|
|
klass
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|