1# Xmethods for libstdc++.
2
3# Copyright (C) 2014-2022 Free Software Foundation, Inc.
4
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18import gdb
19import gdb.xmethod
20import re
21
22matcher_name_prefix = 'libstdc++::'
23
24def get_bool_type():
25    return gdb.lookup_type('bool')
26
27def get_std_size_type():
28    return gdb.lookup_type('std::size_t')
29
30class LibStdCxxXMethod(gdb.xmethod.XMethod):
31    def __init__(self, name, worker_class):
32        gdb.xmethod.XMethod.__init__(self, name)
33        self.worker_class = worker_class
34
35# Xmethods for std::array
36
37class ArrayWorkerBase(gdb.xmethod.XMethodWorker):
38    def __init__(self, val_type, size):
39        self._val_type = val_type
40        self._size = size
41
42    def null_value(self):
43        nullptr = gdb.parse_and_eval('(void *) 0')
44        return nullptr.cast(self._val_type.pointer()).dereference()
45
46class ArraySizeWorker(ArrayWorkerBase):
47    def __init__(self, val_type, size):
48        ArrayWorkerBase.__init__(self, val_type, size)
49
50    def get_arg_types(self):
51        return None
52
53    def get_result_type(self, obj):
54        return get_std_size_type()
55
56    def __call__(self, obj):
57        return self._size
58
59class ArrayEmptyWorker(ArrayWorkerBase):
60    def __init__(self, val_type, size):
61        ArrayWorkerBase.__init__(self, val_type, size)
62
63    def get_arg_types(self):
64        return None
65
66    def get_result_type(self, obj):
67        return get_bool_type()
68
69    def __call__(self, obj):
70        return (int(self._size) == 0)
71
72class ArrayFrontWorker(ArrayWorkerBase):
73    def __init__(self, val_type, size):
74        ArrayWorkerBase.__init__(self, val_type, size)
75
76    def get_arg_types(self):
77        return None
78
79    def get_result_type(self, obj):
80        return self._val_type
81
82    def __call__(self, obj):
83        if int(self._size) > 0:
84            return obj['_M_elems'][0]
85        else:
86            return self.null_value()
87
88class ArrayBackWorker(ArrayWorkerBase):
89    def __init__(self, val_type, size):
90        ArrayWorkerBase.__init__(self, val_type, size)
91
92    def get_arg_types(self):
93        return None
94
95    def get_result_type(self, obj):
96        return self._val_type
97
98    def __call__(self, obj):
99        if int(self._size) > 0:
100            return obj['_M_elems'][self._size - 1]
101        else:
102            return self.null_value()
103
104class ArrayAtWorker(ArrayWorkerBase):
105    def __init__(self, val_type, size):
106        ArrayWorkerBase.__init__(self, val_type, size)
107
108    def get_arg_types(self):
109        return get_std_size_type()
110
111    def get_result_type(self, obj, index):
112        return self._val_type
113
114    def __call__(self, obj, index):
115        if int(index) >= int(self._size):
116            raise IndexError('Array index "%d" should not be >= %d.' %
117                             ((int(index), self._size)))
118        return obj['_M_elems'][index]
119
120class ArraySubscriptWorker(ArrayWorkerBase):
121    def __init__(self, val_type, size):
122        ArrayWorkerBase.__init__(self, val_type, size)
123
124    def get_arg_types(self):
125        return get_std_size_type()
126
127    def get_result_type(self, obj, index):
128        return self._val_type
129
130    def __call__(self, obj, index):
131        if int(self._size) > 0:
132            return obj['_M_elems'][index]
133        else:
134            return self.null_value()
135
136class ArrayMethodsMatcher(gdb.xmethod.XMethodMatcher):
137    def __init__(self):
138        gdb.xmethod.XMethodMatcher.__init__(self,
139                                            matcher_name_prefix + 'array')
140        self._method_dict = {
141            'size': LibStdCxxXMethod('size', ArraySizeWorker),
142            'empty': LibStdCxxXMethod('empty', ArrayEmptyWorker),
143            'front': LibStdCxxXMethod('front', ArrayFrontWorker),
144            'back': LibStdCxxXMethod('back', ArrayBackWorker),
145            'at': LibStdCxxXMethod('at', ArrayAtWorker),
146            'operator[]': LibStdCxxXMethod('operator[]', ArraySubscriptWorker),
147        }
148        self.methods = [self._method_dict[m] for m in self._method_dict]
149
150    def match(self, class_type, method_name):
151        if not re.match('^std::(__\d+::)?array<.*>$', class_type.tag):
152            return None
153        method = self._method_dict.get(method_name)
154        if method is None or not method.enabled:
155            return None
156        try:
157            value_type = class_type.template_argument(0)
158            size = class_type.template_argument(1)
159        except:
160            return None
161        return method.worker_class(value_type, size)
162
163# Xmethods for std::deque
164
165class DequeWorkerBase(gdb.xmethod.XMethodWorker):
166    def __init__(self, val_type):
167        self._val_type = val_type
168        self._bufsize = 512 // val_type.sizeof or 1
169
170    def size(self, obj):
171        first_node = obj['_M_impl']['_M_start']['_M_node']
172        last_node = obj['_M_impl']['_M_finish']['_M_node']
173        cur = obj['_M_impl']['_M_finish']['_M_cur']
174        first = obj['_M_impl']['_M_finish']['_M_first']
175        return (last_node - first_node) * self._bufsize + (cur - first)
176
177    def index(self, obj, idx):
178        first_node = obj['_M_impl']['_M_start']['_M_node']
179        index_node = first_node + int(idx) // self._bufsize
180        return index_node[0][idx % self._bufsize]
181
182class DequeEmptyWorker(DequeWorkerBase):
183    def get_arg_types(self):
184        return None
185
186    def get_result_type(self, obj):
187        return get_bool_type()
188
189    def __call__(self, obj):
190        return (obj['_M_impl']['_M_start']['_M_cur'] ==
191                obj['_M_impl']['_M_finish']['_M_cur'])
192
193class DequeSizeWorker(DequeWorkerBase):
194    def get_arg_types(self):
195        return None
196
197    def get_result_type(self, obj):
198        return get_std_size_type()
199
200    def __call__(self, obj):
201        return self.size(obj)
202
203class DequeFrontWorker(DequeWorkerBase):
204    def get_arg_types(self):
205        return None
206
207    def get_result_type(self, obj):
208        return self._val_type
209
210    def __call__(self, obj):
211        return obj['_M_impl']['_M_start']['_M_cur'][0]
212
213class DequeBackWorker(DequeWorkerBase):
214    def get_arg_types(self):
215        return None
216
217    def get_result_type(self, obj):
218        return self._val_type
219
220    def __call__(self, obj):
221        if (obj['_M_impl']['_M_finish']['_M_cur'] ==
222            obj['_M_impl']['_M_finish']['_M_first']):
223            prev_node = obj['_M_impl']['_M_finish']['_M_node'] - 1
224            return prev_node[0][self._bufsize - 1]
225        else:
226            return obj['_M_impl']['_M_finish']['_M_cur'][-1]
227
228class DequeSubscriptWorker(DequeWorkerBase):
229    def get_arg_types(self):
230        return get_std_size_type()
231
232    def get_result_type(self, obj, subscript):
233        return self._val_type
234
235    def __call__(self, obj, subscript):
236        return self.index(obj, subscript)
237
238class DequeAtWorker(DequeWorkerBase):
239    def get_arg_types(self):
240        return get_std_size_type()
241
242    def get_result_type(self, obj, index):
243        return self._val_type
244
245    def __call__(self, obj, index):
246        deque_size = int(self.size(obj))
247        if int(index) >= deque_size:
248            raise IndexError('Deque index "%d" should not be >= %d.' %
249                             (int(index), deque_size))
250        else:
251           return self.index(obj, index)
252
253class DequeMethodsMatcher(gdb.xmethod.XMethodMatcher):
254    def __init__(self):
255        gdb.xmethod.XMethodMatcher.__init__(self,
256                                            matcher_name_prefix + 'deque')
257        self._method_dict = {
258            'empty': LibStdCxxXMethod('empty', DequeEmptyWorker),
259            'size': LibStdCxxXMethod('size', DequeSizeWorker),
260            'front': LibStdCxxXMethod('front', DequeFrontWorker),
261            'back': LibStdCxxXMethod('back', DequeBackWorker),
262            'operator[]': LibStdCxxXMethod('operator[]', DequeSubscriptWorker),
263            'at': LibStdCxxXMethod('at', DequeAtWorker)
264        }
265        self.methods = [self._method_dict[m] for m in self._method_dict]
266
267    def match(self, class_type, method_name):
268        if not re.match('^std::(__\d+::)?deque<.*>$', class_type.tag):
269            return None
270        method = self._method_dict.get(method_name)
271        if method is None or not method.enabled:
272            return None
273        return method.worker_class(class_type.template_argument(0))
274
275# Xmethods for std::forward_list
276
277class ForwardListWorkerBase(gdb.xmethod.XMethodMatcher):
278    def __init__(self, val_type, node_type):
279        self._val_type = val_type
280        self._node_type = node_type
281
282    def get_arg_types(self):
283        return None
284
285class ForwardListEmptyWorker(ForwardListWorkerBase):
286    def get_result_type(self, obj):
287        return get_bool_type()
288
289    def __call__(self, obj):
290        return obj['_M_impl']['_M_head']['_M_next'] == 0
291
292class ForwardListFrontWorker(ForwardListWorkerBase):
293    def get_result_type(self, obj):
294        return self._val_type
295
296    def __call__(self, obj):
297        node = obj['_M_impl']['_M_head']['_M_next'].cast(self._node_type)
298        val_address = node['_M_storage']['_M_storage'].address
299        return val_address.cast(self._val_type.pointer()).dereference()
300
301class ForwardListMethodsMatcher(gdb.xmethod.XMethodMatcher):
302    def __init__(self):
303        matcher_name = matcher_name_prefix + 'forward_list'
304        gdb.xmethod.XMethodMatcher.__init__(self, matcher_name)
305        self._method_dict = {
306            'empty': LibStdCxxXMethod('empty', ForwardListEmptyWorker),
307            'front': LibStdCxxXMethod('front', ForwardListFrontWorker)
308        }
309        self.methods = [self._method_dict[m] for m in self._method_dict]
310
311    def match(self, class_type, method_name):
312        if not re.match('^std::(__\d+::)?forward_list<.*>$', class_type.tag):
313            return None
314        method = self._method_dict.get(method_name)
315        if method is None or not method.enabled:
316            return None
317        val_type = class_type.template_argument(0)
318        node_type = gdb.lookup_type(str(class_type) + '::_Node').pointer()
319        return method.worker_class(val_type, node_type)
320
321# Xmethods for std::list
322
323class ListWorkerBase(gdb.xmethod.XMethodWorker):
324    def __init__(self, val_type, node_type):
325        self._val_type = val_type
326        self._node_type = node_type
327
328    def get_arg_types(self):
329        return None
330
331    def get_value_from_node(self, node):
332        node = node.dereference()
333        if node.type.fields()[1].name == '_M_data':
334            # C++03 implementation, node contains the value as a member
335            return node['_M_data']
336        # C++11 implementation, node stores value in __aligned_membuf
337        addr = node['_M_storage'].address
338        return addr.cast(self._val_type.pointer()).dereference()
339
340class ListEmptyWorker(ListWorkerBase):
341    def get_result_type(self, obj):
342        return get_bool_type()
343
344    def __call__(self, obj):
345        base_node = obj['_M_impl']['_M_node']
346        if base_node['_M_next'] == base_node.address:
347            return True
348        else:
349            return False
350
351class ListSizeWorker(ListWorkerBase):
352    def get_result_type(self, obj):
353        return get_std_size_type()
354
355    def __call__(self, obj):
356        begin_node = obj['_M_impl']['_M_node']['_M_next']
357        end_node = obj['_M_impl']['_M_node'].address
358        size = 0
359        while begin_node != end_node:
360            begin_node = begin_node['_M_next']
361            size += 1
362        return size
363
364class ListFrontWorker(ListWorkerBase):
365    def get_result_type(self, obj):
366        return self._val_type
367
368    def __call__(self, obj):
369        node = obj['_M_impl']['_M_node']['_M_next'].cast(self._node_type)
370        return self.get_value_from_node(node)
371
372class ListBackWorker(ListWorkerBase):
373    def get_result_type(self, obj):
374        return self._val_type
375
376    def __call__(self, obj):
377        prev_node = obj['_M_impl']['_M_node']['_M_prev'].cast(self._node_type)
378        return self.get_value_from_node(prev_node)
379
380class ListMethodsMatcher(gdb.xmethod.XMethodMatcher):
381    def __init__(self):
382        gdb.xmethod.XMethodMatcher.__init__(self,
383                                            matcher_name_prefix + 'list')
384        self._method_dict = {
385            'empty': LibStdCxxXMethod('empty', ListEmptyWorker),
386            'size': LibStdCxxXMethod('size', ListSizeWorker),
387            'front': LibStdCxxXMethod('front', ListFrontWorker),
388            'back': LibStdCxxXMethod('back', ListBackWorker)
389        }
390        self.methods = [self._method_dict[m] for m in self._method_dict]
391
392    def match(self, class_type, method_name):
393        if not re.match('^std::(__\d+::)?(__cxx11::)?list<.*>$', class_type.tag):
394            return None
395        method = self._method_dict.get(method_name)
396        if method is None or not method.enabled:
397            return None
398        val_type = class_type.template_argument(0)
399        node_type = gdb.lookup_type(str(class_type) + '::_Node').pointer()
400        return method.worker_class(val_type, node_type)
401
402# Xmethods for std::vector
403
404class VectorWorkerBase(gdb.xmethod.XMethodWorker):
405    def __init__(self, val_type):
406        self._val_type = val_type
407
408    def size(self, obj):
409        if self._val_type.code == gdb.TYPE_CODE_BOOL:
410            start = obj['_M_impl']['_M_start']['_M_p']
411            finish = obj['_M_impl']['_M_finish']['_M_p']
412            finish_offset = obj['_M_impl']['_M_finish']['_M_offset']
413            bit_size = start.dereference().type.sizeof * 8
414            return (finish - start) * bit_size + finish_offset
415        else:
416            return obj['_M_impl']['_M_finish'] - obj['_M_impl']['_M_start']
417
418    def get(self, obj, index):
419        if self._val_type.code == gdb.TYPE_CODE_BOOL:
420            start = obj['_M_impl']['_M_start']['_M_p']
421            bit_size = start.dereference().type.sizeof * 8
422            valp = start + index // bit_size
423            offset = index % bit_size
424            return (valp.dereference() & (1 << offset)) > 0
425        else:
426            return obj['_M_impl']['_M_start'][index]
427
428class VectorEmptyWorker(VectorWorkerBase):
429    def get_arg_types(self):
430        return None
431
432    def get_result_type(self, obj):
433        return get_bool_type()
434
435    def __call__(self, obj):
436        return int(self.size(obj)) == 0
437
438class VectorSizeWorker(VectorWorkerBase):
439    def get_arg_types(self):
440        return None
441
442    def get_result_type(self, obj):
443        return get_std_size_type()
444
445    def __call__(self, obj):
446        return self.size(obj)
447
448class VectorFrontWorker(VectorWorkerBase):
449    def get_arg_types(self):
450        return None
451
452    def get_result_type(self, obj):
453        return self._val_type
454
455    def __call__(self, obj):
456        return self.get(obj, 0)
457
458class VectorBackWorker(VectorWorkerBase):
459    def get_arg_types(self):
460        return None
461
462    def get_result_type(self, obj):
463        return self._val_type
464
465    def __call__(self, obj):
466        return self.get(obj, int(self.size(obj)) - 1)
467
468class VectorAtWorker(VectorWorkerBase):
469    def get_arg_types(self):
470        return get_std_size_type()
471
472    def get_result_type(self, obj, index):
473        return self._val_type
474
475    def __call__(self, obj, index):
476        size = int(self.size(obj))
477        if int(index) >= size:
478            raise IndexError('Vector index "%d" should not be >= %d.' %
479                             ((int(index), size)))
480        return self.get(obj, int(index))
481
482class VectorSubscriptWorker(VectorWorkerBase):
483    def get_arg_types(self):
484        return get_std_size_type()
485
486    def get_result_type(self, obj, subscript):
487        return self._val_type
488
489    def __call__(self, obj, subscript):
490        return self.get(obj, int(subscript))
491
492class VectorMethodsMatcher(gdb.xmethod.XMethodMatcher):
493    def __init__(self):
494        gdb.xmethod.XMethodMatcher.__init__(self,
495                                            matcher_name_prefix + 'vector')
496        self._method_dict = {
497            'size': LibStdCxxXMethod('size', VectorSizeWorker),
498            'empty': LibStdCxxXMethod('empty', VectorEmptyWorker),
499            'front': LibStdCxxXMethod('front', VectorFrontWorker),
500            'back': LibStdCxxXMethod('back', VectorBackWorker),
501            'at': LibStdCxxXMethod('at', VectorAtWorker),
502            'operator[]': LibStdCxxXMethod('operator[]',
503                                           VectorSubscriptWorker),
504        }
505        self.methods = [self._method_dict[m] for m in self._method_dict]
506
507    def match(self, class_type, method_name):
508        if not re.match('^std::(__\d+::)?vector<.*>$', class_type.tag):
509            return None
510        method = self._method_dict.get(method_name)
511        if method is None or not method.enabled:
512            return None
513        return method.worker_class(class_type.template_argument(0))
514
515# Xmethods for associative containers
516
517class AssociativeContainerWorkerBase(gdb.xmethod.XMethodWorker):
518    def __init__(self, unordered):
519        self._unordered = unordered
520
521    def node_count(self, obj):
522        if self._unordered:
523            return obj['_M_h']['_M_element_count']
524        else:
525            return obj['_M_t']['_M_impl']['_M_node_count']
526
527    def get_arg_types(self):
528        return None
529
530class AssociativeContainerEmptyWorker(AssociativeContainerWorkerBase):
531    def get_result_type(self, obj):
532        return get_bool_type()
533
534    def __call__(self, obj):
535        return int(self.node_count(obj)) == 0
536
537class AssociativeContainerSizeWorker(AssociativeContainerWorkerBase):
538    def get_result_type(self, obj):
539        return get_std_size_type()
540
541    def __call__(self, obj):
542        return self.node_count(obj)
543
544class AssociativeContainerMethodsMatcher(gdb.xmethod.XMethodMatcher):
545    def __init__(self, name):
546        gdb.xmethod.XMethodMatcher.__init__(self,
547                                            matcher_name_prefix + name)
548        self._name = name
549        self._method_dict = {
550            'size': LibStdCxxXMethod('size', AssociativeContainerSizeWorker),
551            'empty': LibStdCxxXMethod('empty',
552                                      AssociativeContainerEmptyWorker),
553        }
554        self.methods = [self._method_dict[m] for m in self._method_dict]
555
556    def match(self, class_type, method_name):
557        if not re.match('^std::(__\d+::)?%s<.*>$' % self._name, class_type.tag):
558            return None
559        method = self._method_dict.get(method_name)
560        if method is None or not method.enabled:
561            return None
562        unordered = 'unordered' in self._name
563        return method.worker_class(unordered)
564
565# Xmethods for std::unique_ptr
566
567class UniquePtrGetWorker(gdb.xmethod.XMethodWorker):
568    "Implements std::unique_ptr<T>::get() and std::unique_ptr<T>::operator->()"
569
570    def __init__(self, elem_type):
571        self._is_array = elem_type.code == gdb.TYPE_CODE_ARRAY
572        if self._is_array:
573            self._elem_type = elem_type.target()
574        else:
575            self._elem_type = elem_type
576
577    def get_arg_types(self):
578        return None
579
580    def get_result_type(self, obj):
581        return self._elem_type.pointer()
582
583    def _supports(self, method_name):
584        "operator-> is not supported for unique_ptr<T[]>"
585        return method_name == 'get' or not self._is_array
586
587    def __call__(self, obj):
588        impl_type = obj.dereference().type.fields()[0].type.tag
589        # Check for new implementations first:
590        if re.match('^std::(__\d+::)?__uniq_ptr_(data|impl)<.*>$', impl_type):
591            tuple_member = obj['_M_t']['_M_t']
592        elif re.match('^std::(__\d+::)?tuple<.*>$', impl_type):
593            tuple_member = obj['_M_t']
594        else:
595            return None
596        tuple_impl_type = tuple_member.type.fields()[0].type # _Tuple_impl
597        tuple_head_type = tuple_impl_type.fields()[1].type   # _Head_base
598        head_field = tuple_head_type.fields()[0]
599        if head_field.name == '_M_head_impl':
600            return tuple_member.cast(tuple_head_type)['_M_head_impl']
601        elif head_field.is_base_class:
602            return tuple_member.cast(head_field.type)
603        else:
604            return None
605
606class UniquePtrDerefWorker(UniquePtrGetWorker):
607    "Implements std::unique_ptr<T>::operator*()"
608
609    def __init__(self, elem_type):
610        UniquePtrGetWorker.__init__(self, elem_type)
611
612    def get_result_type(self, obj):
613        return self._elem_type
614
615    def _supports(self, method_name):
616        "operator* is not supported for unique_ptr<T[]>"
617        return not self._is_array
618
619    def __call__(self, obj):
620        return UniquePtrGetWorker.__call__(self, obj).dereference()
621
622class UniquePtrSubscriptWorker(UniquePtrGetWorker):
623    "Implements std::unique_ptr<T>::operator[](size_t)"
624
625    def __init__(self, elem_type):
626        UniquePtrGetWorker.__init__(self, elem_type)
627
628    def get_arg_types(self):
629        return get_std_size_type()
630
631    def get_result_type(self, obj, index):
632        return self._elem_type
633
634    def _supports(self, method_name):
635        "operator[] is only supported for unique_ptr<T[]>"
636        return self._is_array
637
638    def __call__(self, obj, index):
639        return UniquePtrGetWorker.__call__(self, obj)[index]
640
641class UniquePtrMethodsMatcher(gdb.xmethod.XMethodMatcher):
642    def __init__(self):
643        gdb.xmethod.XMethodMatcher.__init__(self,
644                                            matcher_name_prefix + 'unique_ptr')
645        self._method_dict = {
646            'get': LibStdCxxXMethod('get', UniquePtrGetWorker),
647            'operator->': LibStdCxxXMethod('operator->', UniquePtrGetWorker),
648            'operator*': LibStdCxxXMethod('operator*', UniquePtrDerefWorker),
649            'operator[]': LibStdCxxXMethod('operator[]', UniquePtrSubscriptWorker),
650        }
651        self.methods = [self._method_dict[m] for m in self._method_dict]
652
653    def match(self, class_type, method_name):
654        if not re.match('^std::(__\d+::)?unique_ptr<.*>$', class_type.tag):
655            return None
656        method = self._method_dict.get(method_name)
657        if method is None or not method.enabled:
658            return None
659        worker = method.worker_class(class_type.template_argument(0))
660        if worker._supports(method_name):
661            return worker
662        return None
663
664# Xmethods for std::shared_ptr
665
666class SharedPtrGetWorker(gdb.xmethod.XMethodWorker):
667    "Implements std::shared_ptr<T>::get() and std::shared_ptr<T>::operator->()"
668
669    def __init__(self, elem_type):
670        self._is_array = elem_type.code == gdb.TYPE_CODE_ARRAY
671        if self._is_array:
672            self._elem_type = elem_type.target()
673        else:
674            self._elem_type = elem_type
675
676    def get_arg_types(self):
677        return None
678
679    def get_result_type(self, obj):
680        return self._elem_type.pointer()
681
682    def _supports(self, method_name):
683        "operator-> is not supported for shared_ptr<T[]>"
684        return method_name == 'get' or not self._is_array
685
686    def __call__(self, obj):
687        return obj['_M_ptr']
688
689class SharedPtrDerefWorker(SharedPtrGetWorker):
690    "Implements std::shared_ptr<T>::operator*()"
691
692    def __init__(self, elem_type):
693        SharedPtrGetWorker.__init__(self, elem_type)
694
695    def get_result_type(self, obj):
696        return self._elem_type
697
698    def _supports(self, method_name):
699        "operator* is not supported for shared_ptr<T[]>"
700        return not self._is_array
701
702    def __call__(self, obj):
703        return SharedPtrGetWorker.__call__(self, obj).dereference()
704
705class SharedPtrSubscriptWorker(SharedPtrGetWorker):
706    "Implements std::shared_ptr<T>::operator[](size_t)"
707
708    def __init__(self, elem_type):
709        SharedPtrGetWorker.__init__(self, elem_type)
710
711    def get_arg_types(self):
712        return get_std_size_type()
713
714    def get_result_type(self, obj, index):
715        return self._elem_type
716
717    def _supports(self, method_name):
718        "operator[] is only supported for shared_ptr<T[]>"
719        return self._is_array
720
721    def __call__(self, obj, index):
722        # Check bounds if _elem_type is an array of known bound
723        m = re.match('.*\[(\d+)]$', str(self._elem_type))
724        if m and index >= int(m.group(1)):
725            raise IndexError('shared_ptr<%s> index "%d" should not be >= %d.' %
726                             (self._elem_type, int(index), int(m.group(1))))
727        return SharedPtrGetWorker.__call__(self, obj)[index]
728
729class SharedPtrUseCountWorker(gdb.xmethod.XMethodWorker):
730    "Implements std::shared_ptr<T>::use_count()"
731
732    def __init__(self, elem_type):
733        pass
734
735    def get_arg_types(self):
736        return None
737
738    def get_result_type(self, obj):
739        return gdb.lookup_type('long')
740
741    def _supports(self, method_name):
742        return True
743
744    def __call__(self, obj):
745        refcounts = obj['_M_refcount']['_M_pi']
746        return refcounts['_M_use_count'] if refcounts else 0
747
748class SharedPtrUniqueWorker(SharedPtrUseCountWorker):
749    "Implements std::shared_ptr<T>::unique()"
750
751    def __init__(self, elem_type):
752        SharedPtrUseCountWorker.__init__(self, elem_type)
753
754    def get_result_type(self, obj):
755        return gdb.lookup_type('bool')
756
757    def __call__(self, obj):
758        return SharedPtrUseCountWorker.__call__(self, obj) == 1
759
760class SharedPtrMethodsMatcher(gdb.xmethod.XMethodMatcher):
761    def __init__(self):
762        gdb.xmethod.XMethodMatcher.__init__(self,
763                                            matcher_name_prefix + 'shared_ptr')
764        self._method_dict = {
765            'get': LibStdCxxXMethod('get', SharedPtrGetWorker),
766            'operator->': LibStdCxxXMethod('operator->', SharedPtrGetWorker),
767            'operator*': LibStdCxxXMethod('operator*', SharedPtrDerefWorker),
768            'operator[]': LibStdCxxXMethod('operator[]', SharedPtrSubscriptWorker),
769            'use_count': LibStdCxxXMethod('use_count', SharedPtrUseCountWorker),
770            'unique': LibStdCxxXMethod('unique', SharedPtrUniqueWorker),
771        }
772        self.methods = [self._method_dict[m] for m in self._method_dict]
773
774    def match(self, class_type, method_name):
775        if not re.match('^std::(__\d+::)?shared_ptr<.*>$', class_type.tag):
776            return None
777        method = self._method_dict.get(method_name)
778        if method is None or not method.enabled:
779            return None
780        worker = method.worker_class(class_type.template_argument(0))
781        if worker._supports(method_name):
782            return worker
783        return None
784
785def register_libstdcxx_xmethods(locus):
786    gdb.xmethod.register_xmethod_matcher(locus, ArrayMethodsMatcher())
787    gdb.xmethod.register_xmethod_matcher(locus, ForwardListMethodsMatcher())
788    gdb.xmethod.register_xmethod_matcher(locus, DequeMethodsMatcher())
789    gdb.xmethod.register_xmethod_matcher(locus, ListMethodsMatcher())
790    gdb.xmethod.register_xmethod_matcher(locus, VectorMethodsMatcher())
791    gdb.xmethod.register_xmethod_matcher(
792        locus, AssociativeContainerMethodsMatcher('set'))
793    gdb.xmethod.register_xmethod_matcher(
794        locus, AssociativeContainerMethodsMatcher('map'))
795    gdb.xmethod.register_xmethod_matcher(
796        locus, AssociativeContainerMethodsMatcher('multiset'))
797    gdb.xmethod.register_xmethod_matcher(
798        locus, AssociativeContainerMethodsMatcher('multimap'))
799    gdb.xmethod.register_xmethod_matcher(
800        locus, AssociativeContainerMethodsMatcher('unordered_set'))
801    gdb.xmethod.register_xmethod_matcher(
802        locus, AssociativeContainerMethodsMatcher('unordered_map'))
803    gdb.xmethod.register_xmethod_matcher(
804        locus, AssociativeContainerMethodsMatcher('unordered_multiset'))
805    gdb.xmethod.register_xmethod_matcher(
806        locus, AssociativeContainerMethodsMatcher('unordered_multimap'))
807    gdb.xmethod.register_xmethod_matcher(locus, UniquePtrMethodsMatcher())
808    gdb.xmethod.register_xmethod_matcher(locus, SharedPtrMethodsMatcher())
809