# Copyright 2014 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. import lldb def print_val(val, internal_dict): '''Prints the given value with Rust syntax''' type_class = val.GetType().GetTypeClass() if type_class == lldb.eTypeClassStruct: return print_struct_val(val, internal_dict) if type_class == lldb.eTypeClassUnion: return print_enum_val(val, internal_dict) if type_class == lldb.eTypeClassPointer: return print_pointer_val(val, internal_dict) if type_class == lldb.eTypeClassArray: return print_fixed_size_vec_val(val, internal_dict) return val.GetValue() #=-------------------------------------------------------------------------------------------------- # Type-Specialized Printing Functions #=-------------------------------------------------------------------------------------------------- def print_struct_val(val, internal_dict): '''Prints a struct, tuple, or tuple struct value with Rust syntax''' assert val.GetType().GetTypeClass() == lldb.eTypeClassStruct if is_vec_slice(val): return print_vec_slice_val(val, internal_dict) else: return print_struct_val_starting_from(0, val, internal_dict) def print_vec_slice_val(val, internal_dict): output = "&[" length = val.GetChildAtIndex(1).GetValueAsUnsigned() data_ptr_val = val.GetChildAtIndex(0) data_ptr_type = data_ptr_val.GetType() assert data_ptr_type.IsPointerType() element_type = data_ptr_type.GetPointeeType() element_type_size = element_type.GetByteSize() start_address = data_ptr_val.GetValueAsUnsigned() for i in range(length): address = start_address + i * element_type_size element_val = val.CreateValueFromAddress( val.GetName() + ("[%s]" % i), address, element_type ) output += print_val(element_val, internal_dict) if i != length - 1: output += ", " output += "]" return output def print_struct_val_starting_from(field_start_index, val, internal_dict): ''' Prints a struct, tuple, or tuple struct value with Rust syntax. Ignores any fields before field_start_index. ''' assert val.GetType().GetTypeClass() == lldb.eTypeClassStruct t = val.GetType() has_field_names = type_has_field_names(t) type_name = extract_type_name(t.GetName()) output = "" if not type_name.startswith("("): # this is a tuple, so don't print the type name output += type_name if has_field_names: output += " { \n" else: output += "(" num_children = val.num_children for child_index in range(field_start_index, num_children): if has_field_names: field_name = t.GetFieldAtIndex(child_index).GetName() output += field_name + ": " field_val = val.GetChildAtIndex(child_index) output += print_val(field_val, internal_dict) if child_index != num_children - 1: output += ", " if has_field_names: output += "\n" if has_field_names: output += "}" else: output += ")" return output def print_enum_val(val, internal_dict): '''Prints an enum value with Rust syntax''' assert val.GetType().GetTypeClass() == lldb.eTypeClassUnion if val.num_children == 1: # This is either an enum with just one variant, or it is an Option-like enum # where the discriminant is encoded in a non-nullable pointer field. We find # out which one it is by looking at the member name of the sole union # variant. If it starts with "RUST$ENCODED$ENUM$" then we have an # Option-like enum. first_variant_name = val.GetChildAtIndex(0).GetName() if first_variant_name and first_variant_name.startswith("RUST$ENCODED$ENUM$"): # This is an Option-like enum. The position of the discriminator field is # encoded in the name which has the format: # RUST$ENCODED$ENUM$$ last_separator_index = first_variant_name.rfind("$") if last_separator_index == -1: return "" % first_variant_name second_last_separator_index = first_variant_name.rfind("$", 0, last_separator_index) if second_last_separator_index == -1: return "" % first_variant_name # Extract index of the discriminator field try: disr_field_index = first_variant_name[second_last_separator_index + 1 : last_separator_index] disr_field_index = int(disr_field_index) except: return "" % first_variant_name # Read the discriminant disr_val = val.GetChildAtIndex(0).GetChildAtIndex(disr_field_index).GetValueAsUnsigned() if disr_val == 0: # Null case: Print the name of the null-variant null_variant_name = first_variant_name[last_separator_index + 1:] return null_variant_name else: # Non-null case: Interpret the data as a value of the non-null variant type return print_struct_val_starting_from(0, val.GetChildAtIndex(0), internal_dict) else: # This is just a regular uni-variant enum without discriminator field return print_struct_val_starting_from(0, val.GetChildAtIndex(0), internal_dict) # If we are here, this is a regular enum with more than one variant disr_val = val.GetChildAtIndex(0).GetChildMemberWithName("RUST$ENUM$DISR") disr_type = disr_val.GetType() if disr_type.GetTypeClass() != lldb.eTypeClassEnumeration: return "" variant_index = disr_val.GetValueAsUnsigned() return print_struct_val_starting_from(1, val.GetChildAtIndex(variant_index), internal_dict) def print_pointer_val(val, internal_dict): '''Prints a pointer value with Rust syntax''' assert val.GetType().IsPointerType() sigil = "&" type_name = extract_type_name(val.GetType().GetName()) if type_name and type_name[0:1] in ["&", "~", "*"]: sigil = type_name[0:1] return sigil + hex(val.GetValueAsUnsigned()) #print_val(val.Dereference(), internal_dict) def print_fixed_size_vec_val(val, internal_dict): assert val.GetType().GetTypeClass() == lldb.eTypeClassArray output = "[" for i in range(val.num_children): output += print_val(val.GetChildAtIndex(i), internal_dict) if i != val.num_children - 1: output += ", " output += "]" return output #=-------------------------------------------------------------------------------------------------- # Helper Functions #=-------------------------------------------------------------------------------------------------- unqualified_type_markers = frozenset(["(", "[", "&", "*"]) def extract_type_name(qualified_type_name): '''Extracts the type name from a fully qualified path''' if qualified_type_name[0] in unqualified_type_markers: return qualified_type_name end_of_search = qualified_type_name.find("<") if end_of_search < 0: end_of_search = len(qualified_type_name) index = qualified_type_name.rfind("::", 0, end_of_search) if index < 0: return qualified_type_name else: return qualified_type_name[index + 2:] def type_has_field_names(ty): '''Returns true of this is a type with field names (struct, struct-like enum variant)''' # This may also be an enum variant where the first field doesn't have a name but the rest has if ty.GetNumberOfFields() > 1: return ty.GetFieldAtIndex(1).GetName() != None else: return ty.GetFieldAtIndex(0).GetName() != None def is_vec_slice(val): ty = val.GetType() if ty.GetTypeClass() != lldb.eTypeClassStruct: return False if ty.GetNumberOfFields() != 2: return False if ty.GetFieldAtIndex(0).GetName() != "data_ptr": return False if ty.GetFieldAtIndex(1).GetName() != "length": return False type_name = extract_type_name(ty.GetName()).replace("&'static", "&").replace(" ", "") return type_name.startswith("&[") and type_name.endswith("]")