]> git.lizzy.rs Git - rust.git/blob - src/etc/gdb_rust_pretty_printing.py
Added fix to LLDB formatter
[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         last_separator_index = first_variant_name.rfind("$")
65         start_index = len("RUST$ENCODED$ENUM$")
66         disr_field_indices = first_variant_name[start_index :
67                                               last_separator_index].split("$")
68         disr_field_indices = [int(index) for index in disr_field_indices]
69
70         sole_variant_val = val[enum_members[0]]
71         discriminant = sole_variant_val
72         for disr_field_index in disr_field_indices:
73           disr_field = get_field_at_index(discriminant, disr_field_index)
74           discriminant = discriminant[disr_field]
75
76         # If the discriminant field is a fat pointer we have to consider the
77         # first word as the true discriminant
78         if discriminant.type.code == gdb.TYPE_CODE_STRUCT:
79             discriminant = discriminant[get_field_at_index(discriminant, 0)]
80
81         if discriminant == 0:
82           null_variant_name = first_variant_name[last_separator_index + 1:]
83           return IdentityPrinter(null_variant_name)
84
85         return rust_pretty_printer_lookup_function(sole_variant_val)
86
87     # This is a regular enum, extract the discriminant
88     discriminant_name, discriminant_val = extract_discriminant_value(val)
89     return rust_pretty_printer_lookup_function(val[enum_members[discriminant_val]])
90
91   # No pretty printer has been found
92   return None
93
94 #=------------------------------------------------------------------------------
95 # Pretty Printer Classes
96 #=------------------------------------------------------------------------------
97
98 class RustStructPrinter:
99   def __init__(self, val, hide_first_field):
100     self.val = val
101     self.hide_first_field = hide_first_field
102
103   def to_string(self):
104     return self.val.type.tag
105
106   def children(self):
107     cs = []
108     for field in self.val.type.fields():
109       field_name = field.name
110       # Normally the field name is used as a key to access the field value,
111       # because that's also supported in older versions of GDB...
112       field_key = field_name
113       if field_name == None:
114         field_name = ""
115         # ... but for fields without a name (as in tuples), we have to fall back
116         # to the newer method of using the field object directly as key. In
117         # older versions of GDB, this will just fail.
118         field_key = field
119       name_value_tuple = ( field_name, self.val[field_key] )
120       cs.append( name_value_tuple )
121
122     if self.hide_first_field:
123       cs = cs[1:]
124
125     return cs
126
127 class RustTuplePrinter:
128   def __init__(self, val):
129     self.val = val
130
131   def to_string(self):
132     return None
133
134   def children(self):
135     cs = []
136     for field in self.val.type.fields():
137       cs.append( ("", self.val[field]) )
138
139     return cs
140
141   def display_hint(self):
142     return "array"
143
144 class RustTupleStructPrinter:
145   def __init__(self, val, hide_first_field):
146     self.val = val
147     self.hide_first_field = hide_first_field
148
149   def to_string(self):
150     return self.val.type.tag
151
152   def children(self):
153     cs = []
154     for field in self.val.type.fields():
155       cs.append( ("", self.val[field]) )
156
157     if self.hide_first_field:
158       cs = cs[1:]
159
160     return cs
161
162   def display_hint(self):
163     return "array"
164
165 class RustStringSlicePrinter:
166   def __init__(self, val):
167     self.val = val
168
169   def to_string(self):
170     slice_byte_len = self.val["length"]
171     return '"%s"' % self.val["data_ptr"].string(encoding = "utf-8",
172                                                 length = slice_byte_len)
173
174 class RustCStyleEnumPrinter:
175   def __init__(self, val):
176     assert val.type.code == gdb.TYPE_CODE_ENUM
177     self.val = val
178
179   def to_string(self):
180     return str(self.val)
181
182 class IdentityPrinter:
183   def __init__(self, string):
184     self.string = string
185
186   def to_string(self):
187     return self.string
188
189 STRUCT_KIND_REGULAR_STRUCT  = 0
190 STRUCT_KIND_TUPLE_STRUCT    = 1
191 STRUCT_KIND_TUPLE           = 2
192 STRUCT_KIND_TUPLE_VARIANT   = 3
193 STRUCT_KIND_STRUCT_VARIANT  = 4
194 STRUCT_KIND_CSTYLE_VARIANT  = 5
195 STRUCT_KIND_STR_SLICE       = 6
196
197 def classify_struct(type):
198   if type.tag == "&str":
199     return STRUCT_KIND_STR_SLICE
200
201   fields = list(type.fields())
202   field_count = len(fields)
203
204   if field_count == 0:
205     return STRUCT_KIND_REGULAR_STRUCT
206
207   if fields[0].name == "RUST$ENUM$DISR":
208     if field_count == 1:
209       return STRUCT_KIND_CSTYLE_VARIANT
210     elif fields[1].name == None:
211       return STRUCT_KIND_TUPLE_VARIANT
212     else:
213       return STRUCT_KIND_STRUCT_VARIANT
214
215   if fields[0].name == None:
216     if type.tag.startswith("("):
217       return STRUCT_KIND_TUPLE
218     else:
219       return STRUCT_KIND_TUPLE_STRUCT
220
221   return STRUCT_KIND_REGULAR_STRUCT
222
223 def extract_discriminant_value(enum_val):
224   assert enum_val.type.code == gdb.TYPE_CODE_UNION
225   for variant_descriptor in enum_val.type.fields():
226     variant_val = enum_val[variant_descriptor]
227     for field in variant_val.type.fields():
228       return (field.name, int(variant_val[field]))
229
230 def first_field(val):
231   for field in val.type.fields():
232     return field
233
234 def get_field_at_index(val, index):
235   i = 0
236   for field in val.type.fields():
237     if i == index:
238       return field
239     i += 1
240   return None