diff --git a/plugins/ruby/codegen.pl b/plugins/ruby/codegen.pl index a406cb758..1baa15365 100755 --- a/plugins/ruby/codegen.pl +++ b/plugins/ruby/codegen.pl @@ -51,8 +51,7 @@ sub render_global_enum { my ($name, $type) = @_; my $rbname = rb_ucase($name); - # store constants in DFHack::EnumName and not in DFHack::MemHack::EnumName - push @lines_rb, "class ::DFHack::$rbname"; + push @lines_rb, "class $rbname"; %seen_enum_name = (); indent_rb { render_enum_fields($type); @@ -81,7 +80,7 @@ sub render_global_bitfield { my ($name, $type) = @_; my $rbname = rb_ucase($name); - push @lines_rb, "class $rbname < Compound"; + push @lines_rb, "class $rbname < MemHack::Compound"; indent_rb { render_bitfield_fields($type); }; @@ -142,7 +141,7 @@ sub render_global_class { } } - my $rbparent = ($parent ? rb_ucase($parent) : 'Compound'); + my $rbparent = ($parent ? rb_ucase($parent) : 'MemHack::Compound'); my $cppvar = "v_$cpp_var_counter"; $cpp_var_counter++; @@ -191,7 +190,7 @@ sub render_global_objects { push @lines_cpp, "void cpp_$sname(FILE *fout) {"; push @include_cpp, $sname; - push @lines_rb, "class $rbname < Compound"; + push @lines_rb, "class $rbname < MemHack::Compound"; indent_rb { for my $obj (@objects) { my $oname = $obj->getAttribute('name'); @@ -208,7 +207,6 @@ sub render_global_objects { }; push @lines_rb, "end"; - push @lines_rb, "module ::DFHack"; indent_rb { push @lines_rb, "Global = GlobalObjects.new._at(0)"; for my $obj (@global_objects) { @@ -216,7 +214,6 @@ sub render_global_objects { push @lines_rb, "def self.$obj=(v) ; Global.$obj = v ; end"; } }; - push @lines_rb, "end"; } @@ -325,8 +322,13 @@ sub render_item_pointer { my ($item, $cppvar) = @_; my $tg = $item->findnodes('child::ld:item')->[0]; - my $tglen = get_tglen($tg, $cppvar); - push @lines_rb, "pointer($tglen) {"; + my $ary = $item->getAttribute('is-array'); + if ($ary and $ary eq 'true') { + my $tglen = get_tglen($tg, $cppvar); + push @lines_rb, "pointer_ary($tglen) {"; + } else { + push @lines_rb, "pointer {"; + } indent_rb { render_item($tg, "${cppvar}[0]"); }; @@ -468,15 +470,13 @@ if ($output =~ /\.cpp$/) { print FH "}\n"; } else { - print FH "module DFHack\n"; - print FH "module MemHack\n"; if ($memstruct) { open MH, "<$memstruct"; print FH "$_" while(); close MH; } + print FH "module DFHack\n"; print FH "$_\n" for @lines_rb; print FH "end\n"; - print FH "end\n"; } close FH; diff --git a/plugins/ruby/ruby-memstruct.rb b/plugins/ruby/ruby-memstruct.rb index 465e9975e..f4796edef 100644 --- a/plugins/ruby/ruby-memstruct.rb +++ b/plugins/ruby/ruby-memstruct.rb @@ -1,6 +1,8 @@ +module DFHack +module MemHack class MemStruct attr_accessor :_memaddr - def _at(addr) ; @_memaddr = addr ; self ; end + def _at(addr) ; @_memaddr = addr ; dup ; end def _get ; self ; end end @@ -27,8 +29,11 @@ class Compound < MemStruct def bits(shift, len) BitField.new(shift, len) end - def pointer(tglen=nil) - Pointer.new(tglen, (yield if tglen)) + def pointer + Pointer.new((yield if block_given?)) + end + def pointer_ary(tglen) + PointerAry.new(tglen, yield) end def static_array(len, tglen) StaticArray.new(tglen, len, yield) @@ -80,6 +85,7 @@ class Compound < MemStruct end def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end end + class Number < MemStruct attr_accessor :_bits, :_signed def initialize(bits, signed) @@ -149,6 +155,23 @@ class BitField < MemStruct end class Pointer < MemStruct + attr_accessor :_tg + def initialize(tg) + @_tg = tg + end + + def _getp + DFHack.memory_read_int32(@_memaddr) & 0xffffffff + end + + # the pointer is invisible, forward all methods to the pointed object + def method_missing(*a) + addr = _getp + tg = (addr == 0 ? nil : @_tg._at(addr)._get) + tg.send(*a) + end +end +class PointerAry < MemStruct attr_accessor :_tglen, :_tg def initialize(tglen, tg) @_tglen = tglen @@ -159,10 +182,6 @@ class Pointer < MemStruct delta = (i != 0 ? i*@_tglen : 0) (DFHack.memory_read_int32(@_memaddr) & 0xffffffff) + delta end - def _setp(v) - DFHack.memory_write_int32(@_memaddr, v) - end - def _get ; self ; end def [](i) addr = _getp(i) @@ -174,11 +193,6 @@ class Pointer < MemStruct raise 'null pointer' if addr == 0 @_tg._at(addr)._set(v) end - - # the pointer is invisible, forward all methods to the pointed object - def method_missing(*a) - self[0].send(*a) - end end class StaticArray < MemStruct attr_accessor :_tglen, :_length, :_tg @@ -205,7 +219,7 @@ class StaticArray < MemStruct end include Enumerable - def each; (0...length).each { |i| yield self[i] }; end + def each ; (0...length).each { |i| yield self[i] } ; end end class StaticString < MemStruct attr_accessor :_length @@ -274,8 +288,9 @@ class StlVector32 < MemStruct end v end + include Enumerable - def each; (0...length).each { |i| yield self[i] }; end + def each ; (0...length).each { |i| yield self[i] } ; end end class StlVector16 < StlVector32 def length @@ -346,19 +361,41 @@ class StlDeque < MemStruct @_tglen = tglen @_tg = tg end + # TODO end class DfFlagarray < MemStruct # TODO end -class DfArray < MemStruct +class DfArray < Compound attr_accessor :_tglen, :_tg def initialize(tglen, tg) @_tglen = tglen @_tg = tg end - # TODO + field(:_length, 0) { number 32, false } + field(:_ptr, 4) { number 32, false } + + def length ; _length ; end + def size ; _length ; end + def _tgat(i) + @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length + end + def [](i) + i += _length if i < 0 + _tgat(i)._get + end + def []=(i, v) + i += _length if i < 0 + _tgat(i)._set(v) + end + def _set(a) + a.each_with_index { |v, i| self[i] = v } + end + + include Enumerable + def each ; (0...length).each { |i| yield self[i] } ; end end class DfLinkedList < MemStruct attr_accessor :_tg @@ -373,33 +410,60 @@ class Global < MemStruct @_glob = glob end def _at(addr) - g = DFHack::MemHack.const_get(@_glob) - g = DFHack.rtti_getclass(g, addr) + g = DFHack.const_get(@_glob) + g = DFHack.rtti_getclassat(g, addr) g.new._at(addr) end end +end # module MemHack -module ::DFHack - def self.rtti_register(cname, cls) - @rtti_n2c ||= {} - @rtti_class ||= {} - @rtti_n2c[cname] = cls - @rtti_class[cls] = true - end - # return the ruby class to use for a given address if rtti info is available - def self.rtti_getclass(cls, addr) - @rtti_n2c ||= {} - @rtti_class ||= {} - if addr != 0 and @rtti_class[cls] - @rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls - else - cls - end +# cpp rtti name -> rb class +@rtti_n2c = {} +@rtti_c2n = {} + +# vtableptr -> cpp rtti name (cache) +@rtti_n2v = {} +@rtti_v2n = {} + +def self.rtti_n2c ; @rtti_n2c ; end +def self.rtti_c2n ; @rtti_c2n ; end + +# register a ruby class with a cpp rtti class name +def self.rtti_register(cname, cls) + @rtti_n2c[cname] = cls + @rtti_c2n[cls] = cname +end + +# return the ruby class to use for the cpp object at address if rtti info is available +def self.rtti_getclassat(cls, addr) + if addr != 0 and @rtti_c2n[cls] + # rtti info exist for class => cpp object has a vtable + @rtti_n2c[rtti_readclassname(get_vtable_ptr(addr))] || cls + else + cls end +end - def self.rtti_readclassname(vptr) - @rtti_v2n ||= {} - @rtti_v2n[vptr] ||= get_rtti_classname(vptr) +# try to read the rtti classname from an object vtable pointer +def self.rtti_readclassname(vptr) + unless n = @rtti_v2n[vptr] + n = @rtti_v2n[vptr] = get_rtti_classname(vptr) + @rtti_n2v[n] = vptr end + n end + +# return the vtable pointer from the cpp rtti name +def self.rtti_getvtable(cname) + unless v = @rtti_n2v[cname] + v = get_vtable(cname) + @rtti_n2v[cname] = v + @rtti_v2n[v] = cname if v != 0 + end + v if v != 0 +end + +end + + diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 0e28087de..0b0df486f 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -370,14 +370,12 @@ static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) static VALUE rb_dfget_global_address(VALUE self, VALUE name) { - uint32_t addr = Core::getInstance().vinfo->getAddress(rb_string_value_ptr(&name)); - return rb_uint2inum(addr); + return rb_uint2inum(Core::getInstance().vinfo->getAddress(rb_string_value_ptr(&name))); } static VALUE rb_dfget_vtable(VALUE self, VALUE name) { - uint32_t addr = (uint32_t)Core::getInstance().vinfo->getVTable(rb_string_value_ptr(&name)); - return rb_uint2inum(addr); + return rb_uint2inum((uint32_t)Core::getInstance().vinfo->getVTable(rb_string_value_ptr(&name))); } // read the c++ class name from a vtable pointer, inspired from doReadClassName