mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-18 18:07:06 +00:00

The code in the documentation needed to change slightly for the port, so the documentation update is combined in the same commit.
302 lines
9.2 KiB
Python
302 lines
9.2 KiB
Python
|
|
# Assorted Pretty printers for wesnoth data structures in gdb
|
|
|
|
import gdb
|
|
import re
|
|
import itertools
|
|
|
|
|
|
import wesnoth_type_tools
|
|
import importlib
|
|
importlib.reload(wesnoth_type_tools)
|
|
from wesnoth_type_tools import strip_all_type, dereference_if_possible
|
|
|
|
|
|
class RecursionManager(object):
|
|
"""Keeps track of the levels of recursion and whether expansion should happen or not """
|
|
default = 2
|
|
curr = 0
|
|
|
|
# @classmethod
|
|
# def __init__(cls, val):
|
|
# cls.default = val
|
|
# cls.curr=0;
|
|
|
|
@classmethod
|
|
def get_level(cls):
|
|
return cls.default
|
|
|
|
@classmethod
|
|
def set_level(cls, val):
|
|
cls.curr = 0
|
|
if val >= 0:
|
|
cls.default = val
|
|
return cls.default
|
|
|
|
@classmethod
|
|
def reset(cls):
|
|
cls.curr = 0
|
|
return cls.default
|
|
|
|
@classmethod
|
|
def should_display(cls):
|
|
return cls.curr <= cls.default
|
|
|
|
@classmethod
|
|
def inc(cls):
|
|
cls.curr += 1
|
|
return cls.should_display()
|
|
|
|
@classmethod
|
|
def dec(cls):
|
|
if cls.curr > 0:
|
|
cls.curr -= 1
|
|
return cls.should_display()
|
|
|
|
|
|
#Printer for n_interned::t_interned
|
|
class T_InternedPrinter(object):
|
|
"""Print a t_interned_token<T>"""
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
#Returns either a string or an other convertible to string
|
|
return self.val['iter_']['first']['_M_dataplus']['_M_p']
|
|
|
|
def display_hint(self):
|
|
#one of 'string' 'array' 'map'
|
|
return 'string'
|
|
|
|
|
|
#Printer for n_token::t_token
|
|
class T_TokenPrinter(object):
|
|
"""Print a t_token"""
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
# Get the type.
|
|
type = self.val.type
|
|
|
|
# Get the type name.
|
|
type = strip_all_type(self.val)
|
|
|
|
#Return the underlying base class
|
|
baseclass = type.fields()[0].type
|
|
|
|
return self.val.cast(baseclass)
|
|
|
|
def display_hint(self):
|
|
#one of 'string' 'array' 'map'
|
|
return 'string'
|
|
|
|
class TstringPrinter(object):
|
|
"""Print a t_string"""
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
# Get the type name.
|
|
type = strip_all_type(self.val)
|
|
|
|
#Dereference pointers
|
|
lval = dereference_if_possible(self.val)
|
|
|
|
#Return the underlying base class
|
|
baseclass = type.fields()[0].type
|
|
|
|
shared = lval.cast(baseclass)['val_']
|
|
if shared == 0:
|
|
return 'NULL'
|
|
|
|
#Print the untranslated string or an error
|
|
try:
|
|
ret = shared.dereference()['val']['value_']['iter_']['first']
|
|
except RuntimeError as e:
|
|
return "wesnoth_gdb error invalid tstring"
|
|
return ret
|
|
|
|
def display_hint(self):
|
|
#one of 'string' 'array' 'map'
|
|
return 'string'
|
|
|
|
class AttributeValuePrinter(object):
|
|
"""Print an attribute_value"""
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return 'attribute_value'
|
|
|
|
def children(self):
|
|
# Get the type.
|
|
self.type = strip_all_type(self.val)
|
|
|
|
try:
|
|
#Dereference pointers
|
|
lval = dereference_if_possible(self.val)
|
|
|
|
attr = lval
|
|
attr_type = attr['type_']
|
|
|
|
class Atype:
|
|
EMPTY = 0
|
|
BOOL = 1
|
|
INT = 2
|
|
DOUBLE = 3
|
|
TSTRING = 4
|
|
TOKEN = 5
|
|
|
|
if attr_type == Atype.EMPTY:
|
|
yield "EMPTY", ""
|
|
elif attr_type == Atype.BOOL:
|
|
yield 'bool', 'true' if attr['bool_value_'] else 'false'
|
|
elif attr_type == Atype.INT:
|
|
yield 'int', 'int ' + ('%s' % attr['int_value_'])
|
|
elif attr_type == Atype.DOUBLE:
|
|
yield 'double', 'double ' + ('%s' % attr['double_value_'])
|
|
elif attr_type == Atype.TOKEN:
|
|
yield 'token', 'token ' + ('%s' % attr['token_value_'])
|
|
elif attr_type == Atype.TSTRING:
|
|
yield 't_string', 't_string ' + ('%s' % attr['t_string_value_'])
|
|
else:
|
|
yield 'unknown', "unknown type code = " + ('%d' % attr_type)
|
|
|
|
except RuntimeError as e:
|
|
yield 'error', "wesnoth_gdb error invalid %s" % self.val.type
|
|
|
|
def display_hint(self):
|
|
#one of 'string' 'array' 'map'
|
|
return 'string'
|
|
|
|
|
|
#This will be brittle as it depends on the underlying boost implementation, but its better than nothing
|
|
#With much help from http://stackoverflow.com/questions/2804641/pretty-printing-boostunordered-map-on-gdb
|
|
class BoostUnorderedMapPrinter(object):
|
|
""" Print a boost unordered map"""
|
|
|
|
class _iterator:
|
|
def __init__(self, val):
|
|
self.buckets = val['table_']['buckets_']
|
|
if self.buckets != 0:
|
|
t_key = val.type.template_argument(0)
|
|
t_val = val.type.template_argument(1)
|
|
self.bucket_count = val['table_']['bucket_count_']
|
|
self.current_bucket = 0
|
|
pair = "std::pair<%s const, %s>" % (t_key, t_val)
|
|
self.pair_pointer = gdb.lookup_type(pair).pointer()
|
|
self.base_pointer = gdb.lookup_type("boost::unordered_detail::value_base< %s >" % pair).pointer()
|
|
self.node_pointer = gdb.lookup_type("boost::unordered_detail::hash_node<std::allocator< %s >, boost::unordered_detail::ungrouped>" % pair).pointer()
|
|
self.node = self.buckets[self.current_bucket]['next_']
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next__(self):
|
|
if self.buckets == 0 or not RecursionManager.should_display():
|
|
raise StopIteration
|
|
while not self.node:
|
|
self.current_bucket += 1
|
|
if self.current_bucket >= self.bucket_count:
|
|
raise StopIteration
|
|
self.node = self.buckets[self.current_bucket]['next_']
|
|
|
|
iterator = self.node.cast(self.node_pointer).cast(self.base_pointer).cast(self.pair_pointer).dereference()
|
|
self.node = self.node['next_']
|
|
|
|
return ('%s' % iterator['first']), iterator['second']
|
|
|
|
def __init__(self, val):
|
|
self.val = val
|
|
self.buckets = val['table_']['buckets_']
|
|
self.descended = False
|
|
|
|
def __del__(self):
|
|
if self.descended:
|
|
RecursionManager.dec()
|
|
|
|
def children(self):
|
|
self.descended = True
|
|
RecursionManager.inc()
|
|
return self._iterator(self.val)
|
|
|
|
def to_string(self):
|
|
ret = "boost::unordered_map"
|
|
if self.buckets == 0:
|
|
ret += " EMPTY"
|
|
return ret
|
|
|
|
def display_hint(self):
|
|
return 'string'
|
|
|
|
|
|
class BoostUnorderedMapIteratorPrinter(object):
|
|
def __init__(self, val):
|
|
pair = val.type.template_argument(0).template_argument(0)
|
|
t_key = pair.template_argument(0)
|
|
t_val = pair.template_argument(1)
|
|
self.node = val['base_']['node_']
|
|
self.pair_pointer = pair.pointer()
|
|
self.base_pointer = gdb.lookup_type("boost::unordered_detail::value_base< %s >" % pair).pointer()
|
|
self.node_pointer = gdb.lookup_type("boost::unordered_detail::hash_node<std::allocator< %s >, boost::unordered_detail::ungrouped>" % pair).pointer()
|
|
self.bucket_pointer = gdb.lookup_type("boost::unordered_detail::hash_bucket<std::allocator< %s > >" % pair).pointer()
|
|
|
|
def to_string(self):
|
|
if not self.node:
|
|
return 'NULL'
|
|
iterator = self.node.cast(self.node_pointer).cast(self.base_pointer).cast(self.pair_pointer).dereference()
|
|
return iterator['second']
|
|
|
|
def display_hint(self):
|
|
return 'string'
|
|
|
|
|
|
|
|
class ConfigPrinter(object):
|
|
"""Print a config"""
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
# Get the type name.
|
|
self.type = strip_all_type(self.val)
|
|
|
|
#Dereference pointers
|
|
self.lval = dereference_if_possible(self.val)
|
|
|
|
def to_string(self):
|
|
return "config"
|
|
|
|
def children(self):
|
|
if RecursionManager.should_display():
|
|
#yield "invalid", self.val['invalid']
|
|
yield "values", self.lval['values_']
|
|
yield "children", self.lval['children_']
|
|
RecursionManager.inc()
|
|
try:
|
|
yield "ordered_children", self.lval['ordered_children']
|
|
except RuntimeError as e:
|
|
RecursionManager.dec()
|
|
else:
|
|
RecursionManager.dec()
|
|
|
|
else:
|
|
pass
|
|
|
|
def display_hint(self):
|
|
#one of 'string' 'array' 'map'
|
|
return 'string'
|
|
|
|
|
|
# register the pretty-printers
|
|
def add_printers(pretty_printers_dict):
|
|
pretty_printers_dict[re.compile('^n_interned::t_interned_token.*$')] = T_InternedPrinter
|
|
pretty_printers_dict[re.compile('^n_token::t_token$')] = T_TokenPrinter
|
|
pretty_printers_dict[re.compile('^t_string$')] = TstringPrinter
|
|
pretty_printers_dict[re.compile('^config::attribute_value$')] = AttributeValuePrinter
|
|
pretty_printers_dict[re.compile('^boost::unordered_map.*$')] = BoostUnorderedMapPrinter
|
|
pretty_printers_dict[re.compile('^boost::unordered_detail::hash_iterator\<std::allocator\<std::pair\<.*$')] = BoostUnorderedMapIteratorPrinter
|
|
pretty_printers_dict[re.compile('^config$')] = ConfigPrinter
|
|
|
|
return pretty_printers_dict
|