]> git.lizzy.rs Git - rust.git/blob - src/etc/gdb_rust_pretty_printing.py
Auto merge of #20613 - dgriffen:master, r=alexcrichton
[rust.git] / src / etc / gdb_rust_pretty_printing.py
1 # Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2 # file at the top-level directory of this distribution and at
3 # http://rust-lang.org/COPYRIGHT.
4 #
5 # Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 # http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 # <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 # option. This file may not be copied, modified, or distributed
9 # except according to those terms.
10
11 import gdb
12
13 #===============================================================================
14 # GDB Pretty Printing Module for Rust
15 #===============================================================================
16
17 def register_printers(objfile):
18   "Registers Rust pretty printers for the given objfile"
19   objfile.pretty_printers.append(rust_pretty_printer_lookup_function)
20
21 def rust_pretty_printer_lookup_function(val):
22   "Returns the correct Rust pretty printer for the given value if there is one"
23   type_code = val.type.code
24
25   if type_code == gdb.TYPE_CODE_STRUCT:
26     struct_kind = classify_struct(val.type)
27
28     if struct_kind == STRUCT_KIND_STR_SLICE:
29       return RustStringSlicePrinter(val)
30
31     if struct_kind == STRUCT_KIND_TUPLE:
32       return RustTuplePrinter(val)
33
34     if struct_kind == STRUCT_KIND_TUPLE_STRUCT:
35       return RustTupleStructPrinter(val, False)
36
37     if struct_kind == STRUCT_KIND_CSTYLE_VARIANT:
38       return RustCStyleEnumPrinter(val[get_field_at_index(val, 0)])
39
40     if struct_kind == STRUCT_KIND_TUPLE_VARIANT:
41       return RustTupleStructPrinter(val, True)
42
43     if struct_kind == STRUCT_KIND_STRUCT_VARIANT:
44       return RustStructPrinter(val, True)
45
46     return RustStructPrinter(val, False)
47
48   # Enum handling
49   if type_code == gdb.TYPE_CODE_UNION:
50     enum_members = list(val.type.fields())
51     enum_member_count = len(enum_members)
52
53     if enum_member_count == 0:
54       return RustStructPrinter(val, False)
55
56     if enum_member_count == 1:
57       first_variant_name = enum_members[0].name
58       if first_variant_name == None:
59         # This is a singleton enum
60         return rust_pretty_printer_lookup_function(val[enum_members[0]])
61       else:
62         assert first_variant_name.startswith("RUST$ENCODED$ENUM$")
63         # This is a space-optimized enum.
64         # This means this enum has only two states, and Rust uses one of the
65         # fields somewhere in the struct to determine which of the two states
66         # it's in. The location of the field is encoded in the name as something
67         # like RUST$ENCODED$ENUM$(num$)*name_of_zero_state
68         last_separator_index = first_variant_name.rfind("$")
69         start_index = len("RUST$ENCODED$ENUM$")
70         disr_field_indices = first_variant_name[start_index :
71                                               last_separator_index].split("$")
72         disr_field_indices = [int(index) for index in disr_field_indices]
73
74         sole_variant_val = val[enum_members[0]]
75         discriminant = sole_variant_val
76         for disr_field_index in disr_field_indices:
77           disr_field = get_field_at_index(discriminant, disr_field_index)
78           discriminant = discriminant[disr_field]
79
80         # If the discriminant field is a fat pointer we have to consider the
81         # first word as the true discriminant
82         if discriminant.type.code == gdb.TYPE_CODE_STRUCT:
83           discriminant = discriminant[get_field_at_index(discriminant, 0)]
84
85         if discriminant == 0:
86           null_variant_name = first_variant_name[last_separator_index + 1:]
87           return IdentityPrinter(null_variant_name)
88
89         return rust_pretty_printer_lookup_function(sole_variant_val)
90
91     # This is a regular enum, extract the discriminant
92     discriminant_name, discriminant_val = extract_discriminant_value(val)
93     return rust_pretty_printer_lookup_function(val[enum_members[discriminant_val]])
94
95   # No pretty printer has been found
96   return None
97
98 #=------------------------------------------------------------------------------
99 # Pretty Printer Classes
100 #=------------------------------------------------------------------------------
101
102 class RustStructPrinter:
103   def __init__(self, val, hide_first_field):
104     self.val = val
105     self.hide_first_field = hide_first_field
106
107   def to_string(self):
108     return self.val.type.tag
109
110   def children(self):
111     cs = []
112     for field in self.val.type.fields():
113       field_name = field.name
114       # Normally the field name is used as a key to access the field value,
115       # because that's also supported in older versions of GDB...
116       field_key = field_name
117       if field_name == None:
118         field_name = ""
119         # ... but for fields without a name (as in tuples), we have to fall back
120         # to the newer method of using the field object directly as key. In
121         # older versions of GDB, this will just fail.
122         field_key = field
123       name_value_tuple = ( field_name, self.val[field_key] )
124       cs.append( name_value_tuple )
125
126     if self.hide_first_field:
127       cs = cs[1:]
128
129     return cs
130
131 class RustTuplePrinter:
132   def __init__(self, val):
133     self.val = val
134
135   def to_string(self):
136     return None
137
138   def children(self):
139     cs = []
140     for field in self.val.type.fields():
141       cs.append( ("", self.val[field]) )
142
143     return cs
144
145   def display_hint(self):
146     return "array"
147
148 class RustTupleStructPrinter:
149   def __init__(self, val, hide_first_field):
150     self.val = val
151     self.hide_first_field = hide_first_field
152
153   def to_string(self):
154     return self.val.type.tag
155
156   def children(self):
157     cs = []
158     for field in self.val.type.fields():
159       cs.append( ("", self.val[field]) )
160
161     if self.hide_first_field:
162       cs = cs[1:]
163
164     return cs
165
166   def display_hint(self):
167     return "array"
168
169 class RustStringSlicePrinter:
170   def __init__(self, val):
171     self.val = val
172
173   def to_string(self):
174     slice_byte_len = self.val["length"]
175     return '"%s"' % self.val["data_ptr"].string(encoding = "utf-8",
176                                                 length = slice_byte_len)
177
178 class RustCStyleEnumPrinter:
179   def __init__(self, val):
180     assert val.type.code == gdb.TYPE_CODE_ENUM
181     self.val = val
182
183   def to_string(self):
184     return str(self.val)
185
186 class IdentityPrinter:
187   def __init__(self, string):
188     self.string = string
189
190   def to_string(self):
191     return self.string
192
193 STRUCT_KIND_REGULAR_STRUCT  = 0
194 STRUCT_KIND_TUPLE_STRUCT    = 1
195 STRUCT_KIND_TUPLE           = 2
196 STRUCT_KIND_TUPLE_VARIANT   = 3
197 STRUCT_KIND_STRUCT_VARIANT  = 4
198 STRUCT_KIND_CSTYLE_VARIANT  = 5
199 STRUCT_KIND_STR_SLICE       = 6
200
201 def classify_struct(type):
202   if type.tag == "&str":
203     return STRUCT_KIND_STR_SLICE
204
205   fields = list(type.fields())
206   field_count = len(fields)
207
208   if field_count == 0:
209     return STRUCT_KIND_REGULAR_STRUCT
210
211   if fields[0].name == "RUST$ENUM$DISR":
212     if field_count == 1:
213       return STRUCT_KIND_CSTYLE_VARIANT
214     elif fields[1].name == None:
215       return STRUCT_KIND_TUPLE_VARIANT
216     else:
217       return STRUCT_KIND_STRUCT_VARIANT
218
219   if fields[0].name == None:
220     if type.tag.startswith("("):
221       return STRUCT_KIND_TUPLE
222     else:
223       return STRUCT_KIND_TUPLE_STRUCT
224
225   return STRUCT_KIND_REGULAR_STRUCT
226
227 def extract_discriminant_value(enum_val):
228   assert enum_val.type.code == gdb.TYPE_CODE_UNION
229   for variant_descriptor in enum_val.type.fields():
230     variant_val = enum_val[variant_descriptor]
231     for field in variant_val.type.fields():
232       return (field.name, int(variant_val[field]))
233
234 def first_field(val):
235   for field in val.type.fields():
236     return field
237
238 def get_field_at_index(val, index):
239   i = 0
240   for field in val.type.fields():
241     if i == index:
242       return field
243     i += 1
244   return None