ruby: tweak inspect, autodereference pointers

develop
jj 2012-04-27 17:11:01 +02:00
parent 22628fe9bb
commit fcfd7062eb
3 changed files with 54 additions and 65 deletions

@ -100,13 +100,11 @@ the block specifies the member type. See ruby-memstruct.rb for more information.
Primitive type access is done through native methods in ruby.cpp (vector length, Primitive type access is done through native methods in ruby.cpp (vector length,
raw memory access, etc) raw memory access, etc)
MemHack::Pointers have a special behavior: they accept and forward any method to MemHack::Pointers are automatically dereferenced ; so a vector of pointer to
the pointed object. To retrieve the pointed object directly, use the _getv Units will yield Units directly. Null pointers yield the 'nil' value.
method. Null pointers resolve to 'nil'.
Beware, invalid pointers (!= 0) will crash the plugin and the game.
This allows to use code such as 'df.world.units.all[0].pos', with all[0] being This allows to use code such as 'df.world.units.all[0].pos', with 'all' being
really a Pointer (with no 'pos' method). really a vector of pointer.
Todo Todo

@ -1,11 +1,11 @@
module DFHack module DFHack
module MemHack module MemHack
INSPECT_SIZE_LIMIT=1024
class MemStruct class MemStruct
attr_accessor :_memaddr attr_accessor :_memaddr
def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end
def _get ; self ; end def _get ; self ; end
def _cpp_init ; end def _cpp_init ; end
def inspect ; _get.inspect ; end
end end
class Compound < MemStruct class Compound < MemStruct
@ -121,7 +121,7 @@ class Compound < MemStruct
out = "#<#{cn}" out = "#<#{cn}"
_fields_ancestors.each { |n, o, s| _fields_ancestors.each { |n, o, s|
out << ' ' if out.length != 0 and out[-1, 1] != ' ' out << ' ' if out.length != 0 and out[-1, 1] != ' '
if out.length > 1024 if out.length > INSPECT_SIZE_LIMIT
out << '...' out << '...'
break break
end end
@ -132,6 +132,8 @@ class Compound < MemStruct
def inspect_field(n, o, s) def inspect_field(n, o, s)
if s.kind_of?(BitField) and s._len == 1 if s.kind_of?(BitField) and s._len == 1
send(n) ? n.to_s : '' send(n) ? n.to_s : ''
elsif s.kind_of?(Pointer)
s._at(@_memaddr+o).inspect
elsif n == :_whole elsif n == :_whole
"_whole=0x#{_whole.to_s(16)}" "_whole=0x#{_whole.to_s(16)}"
else else
@ -237,7 +239,7 @@ class Pointer < MemStruct
DFHack.memory_read_int32(@_memaddr) & 0xffffffff DFHack.memory_read_int32(@_memaddr) & 0xffffffff
end end
def _getv def _get
addr = _getp addr = _getp
return if addr == 0 return if addr == 0
@_tg._at(addr)._get @_tg._at(addr)._get
@ -246,29 +248,24 @@ class Pointer < MemStruct
# XXX shaky... # XXX shaky...
def _set(v) def _set(v)
if v.kind_of?(Pointer) if v.kind_of?(Pointer)
DFHack.memory_write_int32(@_memaddr, v._getv) DFHack.memory_write_int32(@_memaddr, v._getp)
elsif v.kind_of?(MemStruct) elsif v.kind_of?(MemStruct)
DFHack.memory_write_int32(@_memaddr, v._memaddr) DFHack.memory_write_int32(@_memaddr, v._memaddr)
else else
_getv._set(v) _get._set(v)
end end
end end
# these ruby Object methods should be forwarded to the ptr
undef id
undef type
def method_missing(*a)
_getv.send(*a)
end
def respond_to?(q)
_getv.respond_to?(q)
end
def inspect def inspect
ptr = _getp
if ptr == 0
'NULL'
else
cn = (@_tg ? @_tg.class.name.sub(/^DFHack::/, '') : '') cn = (@_tg ? @_tg.class.name.sub(/^DFHack::/, '') : '')
cn = @_tg._glob if cn == 'MemHack::Global' cn = @_tg._glob if cn == 'MemHack::Global'
"#<Pointer #{cn} #{'0x%X' % _getp}>" "#<Pointer #{cn} #{'0x%X' % _getp}>"
end end
end
end end
class PointerAry < MemStruct class PointerAry < MemStruct
attr_accessor :_tglen, :_tg attr_accessor :_tglen, :_tg
@ -304,6 +301,25 @@ module IndexEnum
end end
end end
end end
module Enumerable
include ::Enumerable
def each ; (0...length).each { |i| yield self[i] } ; end
def inspect
enum = DFHack.const_get(_indexenum)::ENUM if _indexenum
out = '['
each { |i|
out << ', ' if out.length > 1
if out.length > INSPECT_SIZE_LIMIT
out << '...'
break
end
out << "#{enum[i]}=" if enum
out << i.inspect
}
out << ']'
end
def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end
end
class StaticArray < MemStruct class StaticArray < MemStruct
include IndexEnum include IndexEnum
attr_accessor :_tglen, :_length, :_indexenum, :_tg attr_accessor :_tglen, :_length, :_indexenum, :_tg
@ -336,16 +352,6 @@ class StaticArray < MemStruct
end end
include Enumerable include Enumerable
def each ; (0...length).each { |i| yield self[i] } ; end
def inspect
if _indexenum
e = DFHack.const_get(_indexenum)::ENUM
'[' + (0...length).map { |i| "#{e[i]}=#{self[i].inspect}" }.join(' ') + ']'
else
to_a.inspect
end
end
def flatten ; map { |e| e.respond_to?(:flatten) ? e.flatten : e }.flatten ; end
end end
class StaticString < MemStruct class StaticString < MemStruct
attr_accessor :_length attr_accessor :_length
@ -420,14 +426,6 @@ class StlVector32 < MemStruct
end end
include Enumerable include Enumerable
def each ; (0...length).each { |i| yield self[i] } ; end
def inspect
if _tg and _tg.kind_of?(Pointer)
length > 0 ? "[#{length}*#{self[0].inspect}]" : '[]'
else
to_a.inspect
end
end
# do a binary search in an ordered vector for a specific target attribute # do a binary search in an ordered vector for a specific target attribute
# ex: world.history.figures.binsearch(unit.hist_figure_id) # ex: world.history.figures.binsearch(unit.hist_figure_id)
def binsearch(target, field=:id) def binsearch(target, field=:id)
@ -555,15 +553,6 @@ class DfFlagarray < MemStruct
end end
include Enumerable include Enumerable
def each ; (0...length).each { |i| yield self[i] } ; end
def inspect
if _indexenum
e = DFHack.const_get(_indexenum)::ENUM
'[' + (0...length).map { |i| if self[i] ; e[i] || i ; end }.compact.join(' ') + ']'
else
to_a.inspect
end
end
end end
class DfArray < Compound class DfArray < Compound
attr_accessor :_tglen, :_tg attr_accessor :_tglen, :_tg
@ -594,8 +583,6 @@ class DfArray < Compound
end end
include Enumerable include Enumerable
def each ; (0...length).each { |i| yield self[i] } ; end
def inspect ; to_a.inspect ; end
end end
class DfLinkedList < Compound class DfLinkedList < Compound
attr_accessor :_tg attr_accessor :_tg
@ -608,27 +595,32 @@ class DfLinkedList < Compound
field(:_next, 8) { number 32, false } field(:_next, 8) { number 32, false }
def item def item
addr = _ptr # With the current xml structure, currently _tg designate
return if addr == 0 # the type of the 'next' and 'prev' fields, not 'item'.
@_tg._at(addr)._get # List head has item == NULL, so we can safely return nil.
#addr = _ptr
#return if addr == 0
#@_tg._at(addr)._get
end end
def item=(v) def item=(v)
addr = _ptr #addr = _ptr
raise 'null pointer' if addr == 0 #raise 'null pointer' if addr == 0
@_tg.at(addr)._set(v) #@_tg.at(addr)._set(v)
raise 'null pointer'
end end
def prev def prev
addr = _prev addr = _prev
return if addr == 0 return if addr == 0
_at(addr) @_tg._at(addr)._get
end end
def next def next
addr = _next addr = _next
return if addr == 0 return if addr == 0
_at(addr) @_tg._at(addr)._get
end end
include Enumerable include Enumerable
@ -639,8 +631,7 @@ class DfLinkedList < Compound
o = o.next o = o.next
end end
end end
def inspect ; "#<DfLinkedList #{item.inspect} prev=#{'0x%X' % _prev} next=#{'0x%X' % _next}>" ; end
def inspect ; "#<DfLinkedList prev=#{'0x%X' % _prev} next=#{'0x%X' % _next} #{item.inspect}>" ; end
end end
class Global < MemStruct class Global < MemStruct

@ -159,7 +159,7 @@ module DFHack
yl = xl[yb] yl = xl[yb]
(0...world.map.z_count_block).each { |z| (0...world.map.z_count_block).each { |z|
p = yl[z] p = yl[z]
yield p._getv if p._getp != 0 yield p if p
} }
} }
} }
@ -171,7 +171,7 @@ module DFHack
xl = world.map.block_index[xb] xl = world.map.block_index[xb]
(0...world.map.y_count_block).each { |yb| (0...world.map.y_count_block).each { |yb|
p = xl[yb][z] p = xl[yb][z]
yield p._getv if p._getp != 0 yield p if p
} }
} }
end end