ruby: use rtti info to cast Item/etc subclasses

develop
jj 2012-04-18 18:46:33 +02:00
parent 3b54cf4657
commit 3ad3d1af46
3 changed files with 70 additions and 5 deletions

@ -128,6 +128,20 @@ sub render_global_class {
$seen_class{$name}++;
%seen_enum_name = ();
my $rtti_name = $type->getAttribute('original-name') ||
$type->getAttribute('type-name');
my $has_rtti = $parent;
if (!$parent and $type->getAttribute('ld:meta') eq 'class-type') {
for my $anytypename (keys %global_types) {
my $anytype = $global_types{$anytypename};
if ($anytype->getAttribute('ld:meta') eq 'class-type') {
my $anyparent = $anytype->getAttribute('inherits-from');
$has_rtti = 1 if ($anyparent and $anyparent eq $name);
}
}
}
my $rbparent = ($parent ? rb_ucase($parent) : 'Compound');
my $cppvar = "v_$cpp_var_counter";
@ -139,6 +153,7 @@ sub render_global_class {
push @lines_rb, "class $rbname < $rbparent";
indent_rb {
push @lines_rb, "rtti_classname '$rtti_name'" if $has_rtti;
render_struct_fields($type, "(*$cppvar)");
};
push @lines_rb, "end\n";

@ -74,6 +74,10 @@ class Compound < MemStruct
m.instance_eval(&b)
m.new
end
def rtti_classname(n)
# TODO store total size for allocate() ? what about non-virtual ones ?
DFHack.rtti_register(n, self)
end
end
def _set(h) ; h.each { |k, v| send("_#{k}=", v) } ; end
end
@ -348,8 +352,34 @@ class Global < MemStruct
def initialize(glob)
@_glob = glob
end
def _at(addr) ; g = DFHack::MemHack.const_get(@_glob) ; g.new._at(addr) ; end
def _at(addr)
g = DFHack::MemHack.const_get(@_glob)
g = DFHack.rtti_getclass(g, addr)
g.new._at(addr)
end
end
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
end
def self.rtti_readclassname(vptr)
@rtti_v2n ||= {}
@rtti_v2n[vptr] ||= get_rtti_classname(vptr)
end
end

@ -380,10 +380,29 @@ static VALUE rb_dfget_vtable(VALUE self, VALUE name)
return rb_uint2inum(addr);
}
static VALUE rb_dfget_rtti_classname(VALUE self, VALUE objptr)
// read the c++ class name from a vtable pointer, inspired from doReadClassName
// XXX virtual classes only! dark pointer arithmetic, use with caution !
static VALUE rb_dfget_rtti_classname(VALUE self, VALUE vptr)
{
void **ptr = (void**)rb_num2ulong(objptr);
return rb_str_new2(typeid(*ptr).name());
char *ptr = (char*)rb_num2ulong(vptr);
#ifdef WIN32
char *rtti = *(char**)(ptr - 0x4);
char *typeinfo = *(char**)(rtti + 0xC);
// skip the .?AV, trim @@ from end
return rb_str_new(typeinfo+0xc, strlen(typeinfo+0xc)-2);
#else
char *typeinfo = *(char**)(ptr - 0x4);
char *typestring = *(char**)(typeinfo + 0x4);
while (*typestring >= '0' && *typestring <= '9')
typestring++;
return rb_str_new2(typestring);
#endif
}
static VALUE rb_dfget_vtable_ptr(VALUE self, VALUE objptr)
{
// actually, rb_dfmemory_read_int32
return rb_uint2inum(*(uint32_t*)rb_num2ulong(objptr));
}
@ -391,7 +410,7 @@ static VALUE rb_dfget_rtti_classname(VALUE self, VALUE objptr)
// raw memory access
// used by the ruby class definitions
// WARNING: may cause game crash ! double-check your addresses !
// XXX may cause game crash ! double-check your addresses !
static VALUE rb_dfmalloc(VALUE self, VALUE len)
{
@ -583,6 +602,7 @@ static void ruby_bind_dfhack(void) {
rb_define_singleton_method(rb_cDFHack, "get_global_address", RUBY_METHOD_FUNC(rb_dfget_global_address), 1);
rb_define_singleton_method(rb_cDFHack, "get_vtable", RUBY_METHOD_FUNC(rb_dfget_vtable), 1);
rb_define_singleton_method(rb_cDFHack, "get_rtti_classname", RUBY_METHOD_FUNC(rb_dfget_rtti_classname), 1);
rb_define_singleton_method(rb_cDFHack, "get_vtable_ptr", RUBY_METHOD_FUNC(rb_dfget_vtable_ptr), 1);
rb_define_singleton_method(rb_cDFHack, "register_dfcommand", RUBY_METHOD_FUNC(rb_dfregister), 2);
rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1);
rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1);