]> git.lizzy.rs Git - rust.git/commitdiff
debuginfo: Create common debugger pretty printer module.
authorMichael Woerister <michaelwoerister@posteo>
Sat, 9 May 2015 14:48:36 +0000 (16:48 +0200)
committerMichael Woerister <michaelwoerister@posteo>
Sat, 30 May 2015 18:06:08 +0000 (20:06 +0200)
GDB and LLDB pretty printers have some common functionality
and also access some common information, such as the layout of
standard library types. So far, this information has been
duplicated in the two pretty printing python modules. This
commit introduces a common module used by both debuggers.

mk/debuggers.mk
src/etc/debugger_pretty_printers_common.py [new file with mode: 0644]
src/etc/gdb_rust_pretty_printing.py
src/etc/lldb_rust_formatters.py
src/test/debuginfo/gdb-pretty-std.rs [deleted file]
src/test/debuginfo/option-like-enum.rs
src/test/debuginfo/pretty-std.rs [new file with mode: 0644]

index e6a2174f84a82dab0104b0520535561d298aa692..aa7b62e13b86c77b76d8e3504a1e3c18641f9bbb 100644 (file)
@@ -15,7 +15,8 @@
 
 ## GDB ##
 DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB=gdb_load_rust_pretty_printers.py \
-                                 gdb_rust_pretty_printing.py
+                                 gdb_rust_pretty_printing.py \
+                                 debugger_pretty_printers_common.py
 DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB_ABS=\
     $(foreach script,$(DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB), \
         $(CFG_SRC_DIR)src/etc/$(script))
@@ -27,7 +28,8 @@ DEBUGGER_BIN_SCRIPTS_GDB_ABS=\
 
 
 ## LLDB ##
-DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB=lldb_rust_formatters.py
+DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB=lldb_rust_formatters.py \
+                                  debugger_pretty_printers_common.py
 DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB_ABS=\
     $(foreach script,$(DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB), \
         $(CFG_SRC_DIR)src/etc/$(script))
diff --git a/src/etc/debugger_pretty_printers_common.py b/src/etc/debugger_pretty_printers_common.py
new file mode 100644 (file)
index 0000000..6e667b3
--- /dev/null
@@ -0,0 +1,328 @@
+# Copyright 2015 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 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+"""
+This module provides an abstraction layer over common Rust pretty printing
+functionality needed by both GDB and LLDB.
+"""
+
+import re
+
+# Type codes that indicate the kind of type as it appears in DWARF debug
+# information. This code alone is not sufficient to determine the Rust type.
+# For example structs, tuples, fat pointers, or enum variants will all have
+# DWARF_TYPE_CODE_STRUCT.
+DWARF_TYPE_CODE_STRUCT = 1
+DWARF_TYPE_CODE_UNION  = 2
+DWARF_TYPE_CODE_PTR    = 3
+DWARF_TYPE_CODE_ARRAY  = 4
+DWARF_TYPE_CODE_ENUM   = 5
+
+# These constants specify the most specific kind of type that could be
+# determined for a given value.
+TYPE_KIND_UNKNOWN           = -1
+TYPE_KIND_EMPTY             = 0
+TYPE_KIND_SLICE             = 1
+TYPE_KIND_REGULAR_STRUCT    = 2
+TYPE_KIND_TUPLE             = 3
+TYPE_KIND_TUPLE_STRUCT      = 4
+TYPE_KIND_CSTYLE_VARIANT    = 5
+TYPE_KIND_TUPLE_VARIANT     = 6
+TYPE_KIND_STRUCT_VARIANT    = 7
+TYPE_KIND_STR_SLICE         = 8
+TYPE_KIND_STD_VEC           = 9
+TYPE_KIND_STD_STRING        = 10
+TYPE_KIND_REGULAR_ENUM      = 11
+TYPE_KIND_COMPRESSED_ENUM   = 12
+TYPE_KIND_SINGLETON_ENUM    = 13
+TYPE_KIND_CSTYLE_ENUM       = 14
+TYPE_KIND_PTR               = 15
+TYPE_KIND_FIXED_SIZE_VEC    = 16
+
+ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$"
+ENUM_DISR_FIELD_NAME = "RUST$ENUM$DISR"
+
+# Slice related constants
+SLICE_FIELD_NAME_DATA_PTR = "data_ptr"
+SLICE_FIELD_NAME_LENGTH = "length"
+SLICE_FIELD_NAMES = [SLICE_FIELD_NAME_DATA_PTR, SLICE_FIELD_NAME_LENGTH]
+
+# std::Vec<> related constants
+STD_VEC_FIELD_NAME_DATA_PTR = "ptr"
+STD_VEC_FIELD_NAME_LENGTH = "len"
+STD_VEC_FIELD_NAME_CAPACITY = "cap"
+STD_VEC_FIELD_NAMES = [STD_VEC_FIELD_NAME_DATA_PTR,
+                       STD_VEC_FIELD_NAME_LENGTH,
+                       STD_VEC_FIELD_NAME_CAPACITY]
+
+# std::String related constants
+STD_STRING_FIELD_NAMES = ["vec"]
+
+
+class Type(object):
+    """
+    This class provides a common interface for type-oriented operations.
+    Sub-classes are supposed to wrap a debugger-specific type-object and
+    provide implementations for the abstract methods in this class.
+    """
+
+    def __init__(self):
+        self.__type_kind = None
+
+    def get_unqualified_type_name(self):
+        """
+        Implementations of this method should return the unqualified name of the
+        type-object they are wrapping. Some examples:
+
+        'int' -> 'int'
+        'std::vec::Vec<std::string::String>' -> 'Vec<std::string::String>'
+        '&std::option::Option<std::string::String>' -> '&std::option::Option<std::string::String>'
+
+        As you can see, type arguments stay fully qualified.
+        """
+        raise NotImplementedError("Override this method")
+
+    def get_dwarf_type_kind(self):
+        """
+        Implementations of this method should return the correct
+        DWARF_TYPE_CODE_* value for the wrapped type-object.
+        """
+        raise NotImplementedError("Override this method")
+
+    def get_fields(self):
+        """
+        Implementations of this method should return a list of field-objects of
+        this type. For Rust-enums (i.e. with DWARF_TYPE_CODE_UNION) these field-
+        objects represent the variants of the enum. Field-objects must have a
+        `name` attribute that gives their name as specified in DWARF.
+        """
+        assert ((self.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT) or
+                (self.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION))
+        raise NotImplementedError("Override this method")
+
+    def get_wrapped_value(self):
+        """
+        Returns the debugger-specific type-object wrapped by this object. This
+        is sometimes needed for doing things like pointer-arithmetic in GDB.
+        """
+        raise NotImplementedError("Override this method")
+
+    def get_type_kind(self):
+        """This method returns the TYPE_KIND_* value for this type-object."""
+        if self.__type_kind is None:
+            dwarf_type_code = self.get_dwarf_type_kind()
+
+            if dwarf_type_code == DWARF_TYPE_CODE_STRUCT:
+                self.__type_kind = self.__classify_struct()
+            elif dwarf_type_code == DWARF_TYPE_CODE_UNION:
+                self.__type_kind = self.__classify_union()
+            elif dwarf_type_code == DWARF_TYPE_CODE_PTR:
+                self.__type_kind = TYPE_KIND_PTR
+            elif dwarf_type_code == DWARF_TYPE_CODE_ARRAY:
+                self.__type_kind = TYPE_KIND_FIXED_SIZE_VEC
+            else:
+                self.__type_kind = TYPE_KIND_UNKNOWN
+        return self.__type_kind
+
+    def __classify_struct(self):
+        assert self.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT
+
+        unqualified_type_name = self.get_unqualified_type_name()
+
+        # STR SLICE
+        if unqualified_type_name == "&str":
+            return TYPE_KIND_STR_SLICE
+
+        # REGULAR SLICE
+        if (unqualified_type_name.startswith("&[") and
+            unqualified_type_name.endswith("]") and
+            self.__conforms_to_field_layout(SLICE_FIELD_NAMES)):
+            return TYPE_KIND_SLICE
+
+        fields = self.get_fields()
+        field_count = len(fields)
+
+        # EMPTY STRUCT
+        if field_count == 0:
+            return TYPE_KIND_EMPTY
+
+        # STD VEC
+        if (unqualified_type_name.startswith("Vec<") and
+            self.__conforms_to_field_layout(STD_VEC_FIELD_NAMES)):
+            return TYPE_KIND_STD_VEC
+
+        # STD STRING
+        if (unqualified_type_name.startswith("String") and
+            self.__conforms_to_field_layout(STD_STRING_FIELD_NAMES)):
+            return TYPE_KIND_STD_STRING
+
+        # ENUM VARIANTS
+        if fields[0].name == ENUM_DISR_FIELD_NAME:
+            if field_count == 1:
+                return TYPE_KIND_CSTYLE_VARIANT
+            elif self.__all_fields_conform_to_tuple_field_naming(1):
+                return TYPE_KIND_TUPLE_VARIANT
+            else:
+                return TYPE_KIND_STRUCT_VARIANT
+
+        # TUPLE
+        if self.__all_fields_conform_to_tuple_field_naming(0):
+            if unqualified_type_name.startswith("("):
+                return TYPE_KIND_TUPLE
+            else:
+                return TYPE_KIND_TUPLE_STRUCT
+
+        # REGULAR STRUCT
+        return TYPE_KIND_REGULAR_STRUCT
+
+
+    def __classify_union(self):
+        assert self.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION
+
+        union_members = self.get_fields()
+        union_member_count = len(union_members)
+        if union_member_count == 0:
+            return TYPE_KIND_EMPTY
+        elif union_member_count == 1:
+            first_variant_name = union_members[0].name
+            if first_variant_name is None:
+                return TYPE_KIND_SINGLETON_ENUM
+            else:
+                assert first_variant_name.startswith(ENCODED_ENUM_PREFIX)
+                return TYPE_KIND_COMPRESSED_ENUM
+        else:
+            return TYPE_KIND_REGULAR_ENUM
+
+
+    def __conforms_to_field_layout(self, expected_fields):
+        actual_fields = self.get_fields()
+        actual_field_count = len(actual_fields)
+
+        if actual_field_count != len(expected_fields):
+            return False
+
+        for i in range(0, actual_field_count):
+            if actual_fields[i].name != expected_fields[i]:
+                return False
+
+        return True
+
+    def __all_fields_conform_to_tuple_field_naming(self, start_index):
+        fields = self.get_fields()
+        field_count = len(fields)
+
+        for i in range(start_index, field_count):
+            field_name = fields[i].name
+            if (field_name is None) or (re.match(r"__\d+$", field_name) is None):
+                return False
+        return True
+
+
+class Value(object):
+    """
+    This class provides a common interface for value-oriented operations.
+    Sub-classes are supposed to wrap a debugger-specific value-object and
+    provide implementations for the abstract methods in this class.
+    """
+    def __init__(self, ty):
+        self.type = ty
+
+    def get_child_at_index(self, index):
+        """Returns the value of the field, array element or variant at the given index"""
+        raise NotImplementedError("Override this method")
+
+    def as_integer(self):
+        """
+        Try to convert the wrapped value into a Python integer. This should
+        always succeed for values that are pointers or actual integers.
+        """
+        raise NotImplementedError("Override this method")
+
+    def get_wrapped_value(self):
+        """
+        Returns the debugger-specific value-object wrapped by this object. This
+        is sometimes needed for doing things like pointer-arithmetic in GDB.
+        """
+        raise NotImplementedError("Override this method")
+
+
+class EncodedEnumInfo(object):
+    """
+    This class provides facilities for handling enum values with compressed
+    encoding where a non-null field in one variant doubles as the discriminant.
+    """
+
+    def __init__(self, enum_val):
+        assert enum_val.type.get_type_kind() == TYPE_KIND_COMPRESSED_ENUM
+        variant_name = enum_val.type.get_fields()[0].name
+        last_separator_index = variant_name.rfind("$")
+        start_index = len(ENCODED_ENUM_PREFIX)
+        indices_substring = variant_name[start_index:last_separator_index].split("$")
+        self.__enum_val = enum_val
+        self.__disr_field_indices = [int(index) for index in indices_substring]
+        self.__null_variant_name = variant_name[last_separator_index + 1:]
+
+    def is_null_variant(self):
+        ty = self.__enum_val.type
+        sole_variant_val = self.__enum_val.get_child_at_index(0)
+        discriminant_val = sole_variant_val
+        for disr_field_index in self.__disr_field_indices:
+            discriminant_val = discriminant_val.get_child_at_index(disr_field_index)
+
+        # If the discriminant field is a fat pointer we have to consider the
+        # first word as the true discriminant
+        if discriminant_val.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT:
+            discriminant_val = discriminant_val.get_child_at_index(0)
+
+        return discriminant_val.as_integer() == 0
+
+    def get_non_null_variant_val(self):
+        return self.__enum_val.get_child_at_index(0)
+
+    def get_null_variant_name(self):
+        return self.__null_variant_name
+
+
+def get_discriminant_value_as_integer(enum_val):
+    assert enum_val.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION
+    # we can take any variant here because the discriminant has to be the same
+    # for all of them.
+    variant_val = enum_val.get_child_at_index(0)
+    disr_val = variant_val.get_child_at_index(0)
+    return disr_val.as_integer()
+
+
+def extract_length_ptr_and_cap_from_std_vec(vec_val):
+    assert vec_val.type.get_type_kind() == TYPE_KIND_STD_VEC
+    length_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_LENGTH)
+    ptr_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_DATA_PTR)
+    cap_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_CAPACITY)
+
+    length = vec_val.get_child_at_index(length_field_index).as_integer()
+    vec_ptr_val = vec_val.get_child_at_index(ptr_field_index)
+    capacity = vec_val.get_child_at_index(cap_field_index).as_integer()
+
+    unique_ptr_val = vec_ptr_val.get_child_at_index(0)
+    data_ptr = unique_ptr_val.get_child_at_index(0)
+    assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR
+    return (length, data_ptr, capacity)
+
+def extract_length_and_ptr_from_slice(slice_val):
+    assert (slice_val.type.get_type_kind() == TYPE_KIND_SLICE or
+            slice_val.type.get_type_kind() == TYPE_KIND_STR_SLICE)
+
+    length_field_index = SLICE_FIELD_NAMES.index(SLICE_FIELD_NAME_LENGTH)
+    ptr_field_index = SLICE_FIELD_NAMES.index(SLICE_FIELD_NAME_DATA_PTR)
+
+    length = slice_val.get_child_at_index(length_field_index).as_integer()
+    data_ptr = slice_val.get_child_at_index(ptr_field_index)
+
+    assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR
+    return (length, data_ptr)
index fc4d15a5168db5ac561280790c8b9bb2f20c6487..f93f3490215d187ee186719024cadf31f72abdbe 100755 (executable)
 
 import gdb
 import re
+import debugger_pretty_printers_common as rustpp
 
 #===============================================================================
 # GDB Pretty Printing Module for Rust
 #===============================================================================
 
+class GdbType(rustpp.Type):
 
-def register_printers(objfile):
-    "Registers Rust pretty printers for the given objfile"
-    objfile.pretty_printers.append(rust_pretty_printer_lookup_function)
+    def __init__(self, ty):
+        super(GdbType, self).__init__()
+        self.ty = ty
+        self.fields = None
 
+    def get_unqualified_type_name(self):
+        tag = self.ty.tag
 
-def rust_pretty_printer_lookup_function(val):
-    "Returns the correct Rust pretty printer for the given value if there is one"
-    type_code = val.type.code
+        if tag is None:
+            return tag
 
-    if type_code == gdb.TYPE_CODE_STRUCT:
-        struct_kind = classify_struct(val.type)
+        return tag.replace("&'static ", "&")
 
-        if struct_kind == STRUCT_KIND_SLICE:
-            return RustSlicePrinter(val)
+    def get_dwarf_type_kind(self):
+        if self.ty.code == gdb.TYPE_CODE_STRUCT:
+            return rustpp.DWARF_TYPE_CODE_STRUCT
 
-        if struct_kind == STRUCT_KIND_STR_SLICE:
-            return RustStringSlicePrinter(val)
+        if self.ty.code == gdb.TYPE_CODE_UNION:
+            return rustpp.DWARF_TYPE_CODE_UNION
 
-        if struct_kind == STRUCT_KIND_STD_VEC:
-            return RustStdVecPrinter(val)
+        if self.ty.code == gdb.TYPE_CODE_PTR:
+            return rustpp.DWARF_TYPE_CODE_PTR
 
-        if struct_kind == STRUCT_KIND_STD_STRING:
-            return RustStdStringPrinter(val)
+        if self.ty.code == gdb.TYPE_CODE_ENUM:
+            return rustpp.DWARF_TYPE_CODE_ENUM
 
-        if struct_kind == STRUCT_KIND_TUPLE:
-            return RustTuplePrinter(val)
+    def get_fields(self):
+        assert ((self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT) or
+                (self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION))
+        if self.fields is None:
+            self.fields = list(self.ty.fields())
+        return self.fields
 
-        if struct_kind == STRUCT_KIND_TUPLE_STRUCT:
-            return RustTupleStructPrinter(val, False)
+    def get_wrapped_value(self):
+        return self.ty
 
-        if struct_kind == STRUCT_KIND_CSTYLE_VARIANT:
-            return RustCStyleEnumPrinter(val[get_field_at_index(val, 0)])
 
-        if struct_kind == STRUCT_KIND_TUPLE_VARIANT:
-            return RustTupleStructPrinter(val, True)
+class GdbValue(rustpp.Value):
+    def __init__(self, gdb_val):
+        super(GdbValue, self).__init__(GdbType(gdb_val.type))
+        self.gdb_val = gdb_val
+        self.children = {}
 
-        if struct_kind == STRUCT_KIND_STRUCT_VARIANT:
-            return RustStructPrinter(val, True)
+    def get_child_at_index(self, index):
+        child = self.children.get(index)
+        if child is None:
+            gdb_field = get_field_at_index(self.gdb_val, index)
+            child = GdbValue(self.gdb_val[gdb_field])
+            self.children[index] = child
+        return child
 
-        return RustStructPrinter(val, False)
+    def as_integer(self):
+        return int(self.gdb_val)
 
-    # Enum handling
-    if type_code == gdb.TYPE_CODE_UNION:
-        enum_members = list(val.type.fields())
-        enum_member_count = len(enum_members)
+    def get_wrapped_value(self):
+        return self.gdb_val
 
-        if enum_member_count == 0:
-            return RustStructPrinter(val, False)
 
-        if enum_member_count == 1:
-            first_variant_name = enum_members[0].name
-            if first_variant_name is None:
-                # This is a singleton enum
-                return rust_pretty_printer_lookup_function(val[enum_members[0]])
-            else:
-                assert first_variant_name.startswith("RUST$ENCODED$ENUM$")
-                # This is a space-optimized enum.
-                # This means this enum has only two states, and Rust uses one
-                # of the fields somewhere in the struct to determine which of
-                # the two states it's in. The location of the field is encoded
-                # in the name as something like
-                # RUST$ENCODED$ENUM$(num$)*name_of_zero_state
-                last_separator_index = first_variant_name.rfind("$")
-                start_index = len("RUST$ENCODED$ENUM$")
-                disr_field_indices = first_variant_name[start_index:last_separator_index].split("$")
-                disr_field_indices = [int(index) for index in disr_field_indices]
-
-                sole_variant_val = val[enum_members[0]]
-                discriminant = sole_variant_val
-                for disr_field_index in disr_field_indices:
-                    disr_field = get_field_at_index(discriminant, disr_field_index)
-                    discriminant = discriminant[disr_field]
-
-                # If the discriminant field is a fat pointer we have to consider the
-                # first word as the true discriminant
-                if discriminant.type.code == gdb.TYPE_CODE_STRUCT:
-                    discriminant = discriminant[get_field_at_index(discriminant, 0)]
-
-                if discriminant == 0:
-                    null_variant_name = first_variant_name[last_separator_index + 1:]
-                    return IdentityPrinter(null_variant_name)
-
-                return rust_pretty_printer_lookup_function(sole_variant_val)
+def register_printers(objfile):
+    """Registers Rust pretty printers for the given objfile"""
+    objfile.pretty_printers.append(rust_pretty_printer_lookup_function)
 
-        # This is a regular enum, extract the discriminant
-        discriminant_name, discriminant_val = extract_discriminant_value(val)
-        return rust_pretty_printer_lookup_function(val[enum_members[discriminant_val]])
 
-    # No pretty printer has been found
-    return None
+def rust_pretty_printer_lookup_function(gdb_val):
+    """
+    Returns the correct Rust pretty printer for the given value
+    if there is one
+    """
 
-#=------------------------------------------------------------------------------
-# Pretty Printer Classes
-#=------------------------------------------------------------------------------
+    val = GdbValue(gdb_val)
+    type_kind = val.type.get_type_kind()
 
+    if (type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT or
+        type_kind == rustpp.TYPE_KIND_EMPTY):
+        return RustStructPrinter(val,
+                                 omit_first_field = False,
+                                 omit_type_name = False,
+                                 is_tuple_like = False)
 
-class RustStructPrinter:
-    def __init__(self, val, hide_first_field):
-        self.val = val
-        self.hide_first_field = hide_first_field
+    if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT:
+        return RustStructPrinter(val,
+                                 omit_first_field = True,
+                                 omit_type_name = False,
+                                 is_tuple_like = False)
 
-    def to_string(self):
-        return self.val.type.tag
+    if type_kind == rustpp.TYPE_KIND_SLICE:
+        return RustSlicePrinter(val)
 
-    def children(self):
-        cs = []
-        for field in self.val.type.fields():
-            field_name = field.name
-            # Normally the field name is used as a key to access the field
-            # value, because that's also supported in older versions of GDB...
-            field_key = field_name
-            if field_name is None:
-                field_name = ""
-                # ... but for fields without a name (as in tuples), we have to
-                # fall back to the newer method of using the field object
-                # directly as key. In older versions of GDB, this will just
-                # fail.
-                field_key = field
-            name_value_tuple = (field_name, self.val[field_key])
-            cs.append(name_value_tuple)
-
-        if self.hide_first_field:
-            cs = cs[1:]
+    if type_kind == rustpp.TYPE_KIND_STR_SLICE:
+        return RustStringSlicePrinter(val)
 
-        return cs
+    if type_kind == rustpp.TYPE_KIND_STD_VEC:
+        return RustStdVecPrinter(val)
 
+    if type_kind == rustpp.TYPE_KIND_STD_STRING:
+        return RustStdStringPrinter(val)
 
-class RustTuplePrinter:
-    def __init__(self, val):
-        self.val = val
+    if type_kind == rustpp.TYPE_KIND_TUPLE:
+        return RustStructPrinter(val,
+                                 omit_first_field = False,
+                                 omit_type_name = True,
+                                 is_tuple_like = True)
 
-    def to_string(self):
-        return None
+    if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT:
+        return RustStructPrinter(val,
+                                 omit_first_field = False,
+                                 omit_type_name = False,
+                                 is_tuple_like = True)
 
-    def children(self):
-        cs = []
-        for field in self.val.type.fields():
-            cs.append(("", self.val[field]))
+    if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT:
+        return RustCStyleVariantPrinter(val.get_child_at_index(0))
 
-        return cs
+    if type_kind == rustpp.TYPE_KIND_TUPLE_VARIANT:
+        return RustStructPrinter(val,
+                                 omit_first_field = True,
+                                 omit_type_name = False,
+                                 is_tuple_like = True)
 
-    def display_hint(self):
-        return "array"
+    if type_kind == rustpp.TYPE_KIND_SINGLETON_ENUM:
+        variant = get_field_at_index(gdb_val, 0)
+        return rust_pretty_printer_lookup_function(gdb_val[variant])
 
+    if type_kind == rustpp.TYPE_KIND_REGULAR_ENUM:
+        # This is a regular enum, extract the discriminant
+        discriminant_val = rustpp.get_discriminant_value_as_integer(val)
+        variant = get_field_at_index(gdb_val, discriminant_val)
+        return rust_pretty_printer_lookup_function(gdb_val[variant])
+
+    if type_kind == rustpp.TYPE_KIND_COMPRESSED_ENUM:
+        encoded_enum_info = rustpp.EncodedEnumInfo(val)
+        if encoded_enum_info.is_null_variant():
+            return IdentityPrinter(encoded_enum_info.get_null_variant_name())
 
-class RustTupleStructPrinter:
-    def __init__(self, val, hide_first_field):
-        self.val = val
-        self.hide_first_field = hide_first_field
+        non_null_val = encoded_enum_info.get_non_null_variant_val()
+        return rust_pretty_printer_lookup_function(non_null_val.get_wrapped_value())
+
+    # No pretty printer has been found
+    return None
+
+
+#=------------------------------------------------------------------------------
+# Pretty Printer Classes
+#=------------------------------------------------------------------------------
+class RustStructPrinter:
+    def __init__(self, val, omit_first_field, omit_type_name, is_tuple_like):
+        self.__val = val
+        self.__omit_first_field = omit_first_field
+        self.__omit_type_name = omit_type_name
+        self.__is_tuple_like = is_tuple_like
 
     def to_string(self):
-        return self.val.type.tag
+        if self.__omit_type_name:
+            return None
+        return self.__val.type.get_unqualified_type_name()
 
     def children(self):
         cs = []
-        for field in self.val.type.fields():
-            cs.append(("", self.val[field]))
+        wrapped_value = self.__val.get_wrapped_value()
+
+        for field in self.__val.type.get_fields():
+            field_value = wrapped_value[field.name]
+            if self.__is_tuple_like:
+                cs.append(("", field_value))
+            else:
+                cs.append((field.name, field_value))
 
-        if self.hide_first_field:
+        if self.__omit_first_field:
             cs = cs[1:]
 
         return cs
 
     def display_hint(self):
-        return "array"
+        if self.__is_tuple_like:
+            return "array"
+        else:
+            return ""
+
 
 class RustSlicePrinter:
     def __init__(self, val):
-        self.val = val
+        self.__val = val
 
     def display_hint(self):
         return "array"
 
     def to_string(self):
-        length = int(self.val["length"])
-        return self.val.type.tag + ("(len: %i)" % length)
+        (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val)
+        return (self.__val.type.get_unqualified_type_name() +
+                ("(len: %i)" % length))
 
     def children(self):
         cs = []
-        length = int(self.val["length"])
-        data_ptr = self.val["data_ptr"]
-        assert data_ptr.type.code == gdb.TYPE_CODE_PTR
-        pointee_type = data_ptr.type.target()
+        (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val)
+        assert data_ptr.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR
+        raw_ptr = data_ptr.get_wrapped_value()
 
         for index in range(0, length):
-            cs.append((str(index), (data_ptr + index).dereference()))
+            cs.append((str(index), (raw_ptr + index).dereference()))
 
         return cs
 
+
 class RustStringSlicePrinter:
     def __init__(self, val):
-        self.val = val
+        self.__val = val
 
     def to_string(self):
-        slice_byte_len = self.val["length"]
-        return '"%s"' % self.val["data_ptr"].string(encoding="utf-8", length=slice_byte_len)
+        (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val)
+        raw_ptr = data_ptr.get_wrapped_value()
+        return '"%s"' % raw_ptr.string(encoding="utf-8", length=length)
+
 
 class RustStdVecPrinter:
     def __init__(self, val):
-        self.val = val
+        self.__val = val
 
     def display_hint(self):
         return "array"
 
     def to_string(self):
-        length = int(self.val["len"])
-        cap = int(self.val["cap"])
-        return self.val.type.tag + ("(len: %i, cap: %i)" % (length, cap))
+        (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val)
+        return (self.__val.type.get_unqualified_type_name() +
+                ("(len: %i, cap: %i)" % (length, cap)))
 
     def children(self):
         cs = []
-        (length, data_ptr) = extract_length_and_data_ptr_from_std_vec(self.val)
-        pointee_type = data_ptr.type.target()
-
+        (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val)
+        gdb_ptr = data_ptr.get_wrapped_value()
         for index in range(0, length):
-            cs.append((str(index), (data_ptr + index).dereference()))
+            cs.append((str(index), (gdb_ptr + index).dereference()))
         return cs
 
+
 class RustStdStringPrinter:
     def __init__(self, val):
-        self.val = val
+        self.__val = val
 
     def to_string(self):
-        (length, data_ptr) = extract_length_and_data_ptr_from_std_vec(self.val["vec"])
-        return '"%s"' % data_ptr.string(encoding="utf-8", length=length)
+        vec = self.__val.get_child_at_index(0)
+        (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec)
+        return '"%s"' % data_ptr.get_wrapped_value().string(encoding="utf-8",
+                                                            length=length)
 
 
-class RustCStyleEnumPrinter:
+class RustCStyleVariantPrinter:
     def __init__(self, val):
-        assert val.type.code == gdb.TYPE_CODE_ENUM
-        self.val = val
+        assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ENUM
+        self.__val = val
 
     def to_string(self):
-        return str(self.val)
+        return str(self.__val.get_wrapped_value())
 
 
 class IdentityPrinter:
@@ -259,91 +278,11 @@ class IdentityPrinter:
     def to_string(self):
         return self.string
 
-STRUCT_KIND_REGULAR_STRUCT  = 0
-STRUCT_KIND_TUPLE_STRUCT    = 1
-STRUCT_KIND_TUPLE           = 2
-STRUCT_KIND_TUPLE_VARIANT   = 3
-STRUCT_KIND_STRUCT_VARIANT  = 4
-STRUCT_KIND_CSTYLE_VARIANT  = 5
-STRUCT_KIND_SLICE           = 6
-STRUCT_KIND_STR_SLICE       = 7
-STRUCT_KIND_STD_VEC         = 8
-STRUCT_KIND_STD_STRING      = 9
-
-
-def classify_struct(type):
-    # print("\nclassify_struct: tag=%s\n" % type.tag)
-    if type.tag == "&str":
-        return STRUCT_KIND_STR_SLICE
-
-    if type.tag.startswith("&[") and type.tag.endswith("]"):
-        return STRUCT_KIND_SLICE
-
-    fields = list(type.fields())
-    field_count = len(fields)
-
-    if field_count == 0:
-        return STRUCT_KIND_REGULAR_STRUCT
-
-    if (field_count == 3 and
-        fields[0].name == "ptr" and
-        fields[1].name == "len" and
-        fields[2].name == "cap" and
-        type.tag.startswith("Vec<")):
-        return STRUCT_KIND_STD_VEC
-
-    if (field_count == 1 and
-        fields[0].name == "vec" and
-        type.tag == "String"):
-        return STRUCT_KIND_STD_STRING
-
-    if fields[0].name == "RUST$ENUM$DISR":
-        if field_count == 1:
-            return STRUCT_KIND_CSTYLE_VARIANT
-        elif all_fields_conform_to_tuple_field_naming(fields, 1):
-            return STRUCT_KIND_TUPLE_VARIANT
-        else:
-            return STRUCT_KIND_STRUCT_VARIANT
-
-    if all_fields_conform_to_tuple_field_naming(fields, 0):
-        if type.tag.startswith("("):
-            return STRUCT_KIND_TUPLE
-        else:
-            return STRUCT_KIND_TUPLE_STRUCT
 
-    return STRUCT_KIND_REGULAR_STRUCT
-
-
-def extract_discriminant_value(enum_val):
-    assert enum_val.type.code == gdb.TYPE_CODE_UNION
-    for variant_descriptor in enum_val.type.fields():
-        variant_val = enum_val[variant_descriptor]
-        for field in variant_val.type.fields():
-            return (field.name, int(variant_val[field]))
-
-
-def first_field(val):
-    for field in val.type.fields():
-        return field
-
-def get_field_at_index(val, index):
+def get_field_at_index(gdb_val, index):
     i = 0
-    for field in val.type.fields():
+    for field in gdb_val.type.fields():
         if i == index:
             return field
         i += 1
     return None
-
-def all_fields_conform_to_tuple_field_naming(fields, start_index):
-    for i in range(start_index, len(fields)):
-        if (fields[i].name is None) or (re.match(r"__\d+$", fields[i].name) is None):
-            return False
-    return True
-
-def extract_length_and_data_ptr_from_std_vec(vec_val):
-    length = int(vec_val["len"])
-    vec_ptr_val = vec_val["ptr"]
-    unique_ptr_val = vec_ptr_val[first_field(vec_ptr_val)]
-    data_ptr = unique_ptr_val[first_field(unique_ptr_val)]
-    assert data_ptr.type.code == gdb.TYPE_CODE_PTR
-    return (length, data_ptr)
index 2aaa158875817bbca6dd9fe368222d29fa014f95..c22a60abf3f7662be6aa814dc7a27ffb8c25105a 100644 (file)
 
 import lldb
 import re
+import debugger_pretty_printers_common as rustpp
 
-def print_val(val, internal_dict):
-    '''Prints the given value with Rust syntax'''
-    type_class = val.GetType().GetTypeClass()
+#===============================================================================
+# LLDB Pretty Printing Module for Rust
+#===============================================================================
 
-    if type_class == lldb.eTypeClassStruct:
-        return print_struct_val(val, internal_dict)
+class LldbType(rustpp.Type):
 
-    if type_class == lldb.eTypeClassUnion:
-        return print_enum_val(val, internal_dict)
+    def __init__(self, ty):
+        super(LldbType, self).__init__()
+        self.ty = ty
+        self.fields = None
 
-    if type_class == lldb.eTypeClassPointer:
-        return print_pointer_val(val, internal_dict)
+    def get_unqualified_type_name(self):
+        qualified_name = self.ty.GetName()
 
-    if type_class == lldb.eTypeClassArray:
-        return print_fixed_size_vec_val(val, internal_dict)
+        if qualified_name is None:
+            return qualified_name
 
-    return val.GetValue()
+        return extract_type_name(qualified_name).replace("&'static ", "&")
 
+    def get_dwarf_type_kind(self):
+        type_class = self.ty.GetTypeClass()
 
-#=--------------------------------------------------------------------------------------------------
-# Type-Specialized Printing Functions
-#=--------------------------------------------------------------------------------------------------
+        if type_class == lldb.eTypeClassStruct:
+            return rustpp.DWARF_TYPE_CODE_STRUCT
+
+        if type_class == lldb.eTypeClassUnion:
+            return rustpp.DWARF_TYPE_CODE_UNION
+
+        if type_class == lldb.eTypeClassPointer:
+            return rustpp.DWARF_TYPE_CODE_PTR
+
+        if type_class == lldb.eTypeClassArray:
+            return rustpp.DWARF_TYPE_CODE_ARRAY
+
+        if type_class == lldb.eTypeClassEnumeration:
+            return rustpp.DWARF_TYPE_CODE_ENUM
+
+        return None
+
+    def get_fields(self):
+        assert ((self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT) or
+                (self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION))
+        if self.fields is None:
+            self.fields = list(self.ty.fields)
+        return self.fields
+
+    def get_wrapped_value(self):
+        return self.ty
+
+
+class LldbValue(rustpp.Value):
+    def __init__(self, lldb_val):
+        ty = lldb_val.type
+        wty = LldbType(ty)
+        super(LldbValue, self).__init__(wty)
+        self.lldb_val = lldb_val
+        self.children = {}
+
+    def get_child_at_index(self, index):
+        child = self.children.get(index)
+        if child is None:
+            lldb_field = self.lldb_val.GetChildAtIndex(index)
+            child = LldbValue(lldb_field)
+            self.children[index] = child
+        return child
+
+    def as_integer(self):
+        return self.lldb_val.GetValueAsUnsigned()
+
+    def get_wrapped_value(self):
+        return self.lldb_val
+
+
+def print_val(lldb_val, internal_dict):
+    val = LldbValue(lldb_val)
+    type_kind = val.type.get_type_kind()
 
-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 (type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT or
+        type_kind == rustpp.TYPE_KIND_EMPTY):
+        return print_struct_val(val,
+                                internal_dict,
+                                omit_first_field = False,
+                                omit_type_name = False,
+                                is_tuple_like = False)
 
-    if is_vec_slice(val):
+    if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT:
+        return print_struct_val(val,
+                                internal_dict,
+                                omit_first_field = True,
+                                omit_type_name = False,
+                                is_tuple_like = False)
+
+    if type_kind == rustpp.TYPE_KIND_SLICE:
         return print_vec_slice_val(val, internal_dict)
-    elif is_std_vec(val):
+
+    if type_kind == rustpp.TYPE_KIND_STR_SLICE:
+        return print_str_slice_val(val, internal_dict)
+
+    if type_kind == rustpp.TYPE_KIND_STD_VEC:
         return print_std_vec_val(val, internal_dict)
-    else:
-        return print_struct_val_starting_from(0, val, internal_dict)
 
+    if type_kind == rustpp.TYPE_KIND_STD_STRING:
+        return print_std_string_val(val, internal_dict)
+
+    if type_kind == rustpp.TYPE_KIND_TUPLE:
+        return print_struct_val(val,
+                                internal_dict,
+                                omit_first_field = False,
+                                omit_type_name = True,
+                                is_tuple_like = True)
+
+    if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT:
+        return print_struct_val(val,
+                                internal_dict,
+                                omit_first_field = False,
+                                omit_type_name = False,
+                                is_tuple_like = True)
+
+    if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT:
+        return val.type.get_unqualified_type_name()
+
+    if type_kind == rustpp.TYPE_KIND_TUPLE_VARIANT:
+        return print_struct_val(val,
+                                internal_dict,
+                                omit_first_field = True,
+                                omit_type_name = False,
+                                is_tuple_like = True)
+
+    if type_kind == rustpp.TYPE_KIND_SINGLETON_ENUM:
+        return print_val(lldb_val.GetChildAtIndex(0), internal_dict)
+
+    if type_kind == rustpp.TYPE_KIND_PTR:
+        return print_pointer_val(val, internal_dict)
+
+    if type_kind == rustpp.TYPE_KIND_FIXED_SIZE_VEC:
+        return print_fixed_size_vec_val(val, internal_dict)
+
+    if type_kind == rustpp.TYPE_KIND_REGULAR_ENUM:
+        # This is a regular enum, extract the discriminant
+        discriminant_val = rustpp.get_discriminant_value_as_integer(val)
+        return print_val(lldb_val.GetChildAtIndex(discriminant_val), internal_dict)
+
+    if type_kind == rustpp.TYPE_KIND_COMPRESSED_ENUM:
+        encoded_enum_info = rustpp.EncodedEnumInfo(val)
+        if encoded_enum_info.is_null_variant():
+            return encoded_enum_info.get_null_variant_name()
 
-def print_struct_val_starting_from(field_start_index, val, internal_dict):
+        non_null_val = encoded_enum_info.get_non_null_variant_val()
+        return print_val(non_null_val.get_wrapped_value(), internal_dict)
+
+    # No pretty printer has been found
+    return lldb_val.GetValue()
+
+
+#=--------------------------------------------------------------------------------------------------
+# Type-Specialized Printing Functions
+#=--------------------------------------------------------------------------------------------------
+
+def print_struct_val(val, internal_dict, omit_first_field, omit_type_name, is_tuple_like):
     '''
     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()
-    type_name = extract_type_name(t.GetName())
-    num_children = val.num_children
-
-    if (num_children - field_start_index) == 0:
-        # The only field of this struct is the enum discriminant
-        return type_name
+    assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT
 
-    is_tuple_like = type_is_tuple_like(t)
+    if omit_type_name:
+        type_name = ""
+    else:
+        type_name = val.type.get_unqualified_type_name()
 
     if is_tuple_like:
         template = "%(type_name)s(%(body)s)"
@@ -70,20 +189,18 @@ def print_struct_val_starting_from(field_start_index, val, internal_dict):
         template = "%(type_name)s {\n%(body)s\n}"
         separator = ", \n"
 
-    if type_name.startswith("("):
-        # this is a tuple, so don't print the type name
-        type_name = ""
+    fields = val.type.get_fields()
 
     def render_child(child_index):
         this = ""
         if not is_tuple_like:
-            field_name = t.GetFieldAtIndex(child_index).GetName()
+            field_name = fields[child_index].name
             this += field_name + ": "
 
-        field_val = val.GetChildAtIndex(child_index)
+        field_val = val.get_child_at_index(child_index)
 
-        if not field_val.IsValid():
-            field = t.GetFieldAtIndex(child_index)
+        if not field_val.get_wrapped_value().IsValid():
+            field = fields[child_index]
             # LLDB is not good at handling zero-sized values, so we have to help
             # it a little
             if field.GetType().GetByteSize() == 0:
@@ -91,95 +208,38 @@ def print_struct_val_starting_from(field_start_index, val, internal_dict):
             else:
                 return this + "<invalid value>"
 
-        return this + print_val(field_val, internal_dict)
+        return this + print_val(field_val.get_wrapped_value(), internal_dict)
+
+    if omit_first_field:
+        field_start_index = 1
+    else:
+        field_start_index = 0
 
-    body = separator.join([render_child(idx) for idx in range(field_start_index, num_children)])
+    body = separator.join([render_child(idx) for idx in range(field_start_index, len(fields))])
 
     return template % {"type_name": type_name,
                        "body": body}
 
-
-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$<index of discriminator field>$<name of null variant>
-            last_separator_index = first_variant_name.rfind("$")
-            if last_separator_index == -1:
-                return "<invalid enum encoding: %s>" % first_variant_name
-
-            start_index = len("RUST$ENCODED$ENUM$")
-
-            # Extract indices of the discriminator field
-            try:
-                disr_field_indices = first_variant_name[start_index:last_separator_index].split("$")
-                disr_field_indices = [int(index) for index in disr_field_indices]
-            except:
-                return "<invalid enum encoding: %s>" % first_variant_name
-
-            # Read the discriminant
-            disr_val = val.GetChildAtIndex(0)
-            for index in disr_field_indices:
-                disr_val = disr_val.GetChildAtIndex(index)
-
-            # If the discriminant field is a fat pointer we have to consider the
-            # first word as the true discriminant
-            if disr_val.GetType().GetTypeClass() == lldb.eTypeClassStruct:
-                disr_val = disr_val.GetChildAtIndex(0)
-
-            if disr_val.GetValueAsUnsigned() == 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 "<Invalid enum value encountered: Discriminator is not an enum>"
-
-    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()
+    assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR
     sigil = "&"
-    type_name = extract_type_name(val.GetType().GetName())
-    if type_name and type_name[0:1] in ["&", "~", "*"]:
+    type_name = val.type.get_unqualified_type_name()
+    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)
+    return sigil + hex(val.as_integer())
 
 
 def print_fixed_size_vec_val(val, internal_dict):
-    assert val.GetType().GetTypeClass() == lldb.eTypeClassArray
+    assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ARRAY
+    lldb_val = val.get_wrapped_value()
 
     output = "["
 
-    for i in range(val.num_children):
-        output += print_val(val.GetChildAtIndex(i), internal_dict)
-        if i != val.num_children - 1:
+    for i in range(lldb_val.num_children):
+        output += print_val(lldb_val.GetChildAtIndex(i), internal_dict)
+        if i != lldb_val.num_children - 1:
             output += ", "
 
     output += "]"
@@ -187,39 +247,38 @@ def print_fixed_size_vec_val(val, internal_dict):
 
 
 def print_vec_slice_val(val, internal_dict):
-    length = val.GetChildAtIndex(1).GetValueAsUnsigned()
-
-    data_ptr_val = val.GetChildAtIndex(0)
-    data_ptr_type = data_ptr_val.GetType()
-
-    return "&[%s]" % print_array_of_values(val.GetName(),
-                                           data_ptr_val,
+    (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(val)
+    return "&[%s]" % print_array_of_values(val.get_wrapped_value().GetName(),
+                                           data_ptr,
                                            length,
                                            internal_dict)
 
 
 def print_std_vec_val(val, internal_dict):
-    length = val.GetChildAtIndex(1).GetValueAsUnsigned()
-
-    # Vec<> -> Unique<> -> NonZero<> -> *T
-    data_ptr_val = val.GetChildAtIndex(0).GetChildAtIndex(0).GetChildAtIndex(0)
-    data_ptr_type = data_ptr_val.GetType()
-
-    return "vec![%s]" % print_array_of_values(val.GetName(),
-                                              data_ptr_val,
+    (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(val)
+    return "vec![%s]" % print_array_of_values(val.get_wrapped_value().GetName(),
+                                              data_ptr,
                                               length,
                                               internal_dict)
 
+def print_str_slice_val(val, internal_dict):
+    (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(val)
+    return read_utf8_string(data_ptr, length)
+
+def print_std_string_val(val, internal_dict):
+    vec = val.get_child_at_index(0)
+    (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec)
+    return read_utf8_string(data_ptr, length)
+
 #=--------------------------------------------------------------------------------------------------
 # Helper Functions
 #=--------------------------------------------------------------------------------------------------
 
-unqualified_type_markers = frozenset(["(", "[", "&", "*"])
-
+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:
+    if qualified_type_name[0] in UNQUALIFIED_TYPE_MARKERS:
         return qualified_type_name
 
     end_of_search = qualified_type_name.find("<")
@@ -232,72 +291,34 @@ def extract_type_name(qualified_type_name):
     else:
         return qualified_type_name[index + 2:]
 
-
-def type_is_tuple_like(ty):
-    '''Returns true of this is a type with field names (struct, struct-like enum variant)'''
-    for field in ty.fields:
-        if field.GetName() == "RUST$ENUM$DISR":
-            # Ignore the enum discriminant field if there is one.
-            continue
-        if (field.GetName() is None) or (re.match(r"__\d+$", field.GetName()) is None):
-            return False
-    return True
-
-
-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("]")
-
-def is_std_vec(val):
-    ty = val.GetType()
-    if ty.GetTypeClass() != lldb.eTypeClassStruct:
-        return False
-
-    if ty.GetNumberOfFields() != 3:
-        return False
-
-    if ty.GetFieldAtIndex(0).GetName() != "ptr":
-        return False
-
-    if ty.GetFieldAtIndex(1).GetName() != "len":
-        return False
-
-    if ty.GetFieldAtIndex(2).GetName() != "cap":
-        return False
-
-    return ty.GetName().startswith("collections::vec::Vec<")
-
-
 def print_array_of_values(array_name, data_ptr_val, length, internal_dict):
     '''Prints a contigous memory range, interpreting it as values of the
        pointee-type of data_ptr_val.'''
 
-    data_ptr_type = data_ptr_val.GetType()
-    assert data_ptr_type.IsPointerType()
+    data_ptr_type = data_ptr_val.type
+    assert data_ptr_type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR
 
-    element_type = data_ptr_type.GetPointeeType()
+    element_type = data_ptr_type.get_wrapped_value().GetPointeeType()
     element_type_size = element_type.GetByteSize()
 
-    start_address = data_ptr_val.GetValueAsUnsigned()
+    start_address = data_ptr_val.as_integer()
+    raw_value = data_ptr_val.get_wrapped_value()
 
     def render_element(i):
         address = start_address + i * element_type_size
-        element_val = data_ptr_val.CreateValueFromAddress(array_name + ("[%s]" % i),
-                                                          address,
-                                                          element_type)
+        element_val = raw_value.CreateValueFromAddress(array_name + ("[%s]" % i),
+                                                       address,
+                                                       element_type)
         return print_val(element_val, internal_dict)
 
     return ', '.join([render_element(i) for i in range(length)])
+
+
+def read_utf8_string(ptr_val, byte_count):
+    error = lldb.SBError()
+    process = ptr_val.get_wrapped_value().GetProcess()
+    data = process.ReadMemory(ptr_val.as_integer(), byte_count, error)
+    if error.Success():
+        return '"%s"' % data.decode(encoding='UTF-8')
+    else:
+        return '<error: %s>' % error.GetCString()
diff --git a/src/test/debuginfo/gdb-pretty-std.rs b/src/test/debuginfo/gdb-pretty-std.rs
deleted file mode 100644 (file)
index c42f7f1..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2013-2015 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// ignore-windows failing on win32 bot
-// ignore-freebsd: gdb package too new
-// ignore-lldb
-// ignore-android: FIXME(#10381)
-// compile-flags:-g
-// min-gdb-version 7.7
-
-// gdb-command: run
-
-// gdb-command: print slice
-// gdb-check:$1 = &[i32](len: 4) = {0, 1, 2, 3}
-
-// gdb-command: print vec
-// gdb-check:$2 = Vec<u64>(len: 4, cap: [...]) = {4, 5, 6, 7}
-
-// gdb-command: print str_slice
-// gdb-check:$3 = "IAMA string slice!"
-
-// gdb-command: print string
-// gdb-check:$4 = "IAMA string!"
-
-// gdb-command: print some
-// gdb-check:$5 = Some = {8}
-
-// gdb-command: print none
-// gdb-check:$6 = None
-
-#![allow(unused_variables)]
-
-fn main() {
-
-    // &[]
-    let slice: &[i32] = &[0, 1, 2, 3];
-
-    // Vec
-    let vec = vec![4u64, 5, 6, 7];
-
-    // &str
-    let str_slice = "IAMA string slice!";
-
-    // String
-    let string = "IAMA string!".to_string();
-
-    // Option
-    let some = Some(8i16);
-    let none: Option<i64> = None;
-
-    zzz(); // #break
-}
-
-fn zzz() { () }
index 65a83088d805102cdc156f37a9a80dd4b9465905..fba21d9a553ba80d7c341ac83170f7e274fd1a29 100644 (file)
@@ -67,7 +67,7 @@
 // lldb-check:[...]$5 = Void
 
 // lldb-command:print some_str
-// lldb-check:[...]$6 = Some(&str { data_ptr: [...], length: 3 })
+// lldb-check:[...]$6 = Some("abc")
 
 // lldb-command:print none_str
 // lldb-check:[...]$7 = None
diff --git a/src/test/debuginfo/pretty-std.rs b/src/test/debuginfo/pretty-std.rs
new file mode 100644 (file)
index 0000000..5766217
--- /dev/null
@@ -0,0 +1,87 @@
+// Copyright 2013-2015 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-windows failing on win32 bot
+// ignore-freebsd: gdb package too new
+// ignore-android: FIXME(#10381)
+// compile-flags:-g
+// min-gdb-version 7.7
+// min-lldb-version: 310
+
+// === GDB TESTS ===================================================================================
+
+// gdb-command: run
+
+// gdb-command: print slice
+// gdb-check:$1 = &[i32](len: 4) = {0, 1, 2, 3}
+
+// gdb-command: print vec
+// gdb-check:$2 = Vec<u64>(len: 4, cap: [...]) = {4, 5, 6, 7}
+
+// gdb-command: print str_slice
+// gdb-check:$3 = "IAMA string slice!"
+
+// gdb-command: print string
+// gdb-check:$4 = "IAMA string!"
+
+// gdb-command: print some
+// gdb-check:$5 = Some = {8}
+
+// gdb-command: print none
+// gdb-check:$6 = None
+
+
+// === LLDB TESTS ==================================================================================
+
+// lldb-command: run
+
+// lldb-command: print slice
+// lldb-check:[...]$0 = &[0, 1, 2, 3]
+
+// lldb-command: print vec
+// lldb-check:[...]$1 = vec![4, 5, 6, 7]
+
+// lldb-command: print str_slice
+// lldb-check:[...]$2 = "IAMA string slice!"
+
+// lldb-command: print string
+// lldb-check:[...]$3 = "IAMA string!"
+
+// lldb-command: print some
+// lldb-check:[...]$4 = Some(8)
+
+// lldb-command: print none
+// lldb-check:[...]$5 = None
+
+
+#![allow(unused_variables)]
+
+fn main() {
+
+    // &[]
+    let slice: &[i32] = &[0, 1, 2, 3];
+
+    // Vec
+    let vec = vec![4u64, 5, 6, 7];
+
+    // &str
+    let str_slice = "IAMA string slice!";
+
+    // String
+    let string = "IAMA string!".to_string();
+
+    // Option
+    let some = Some(8i16);
+    let none: Option<i64> = None;
+
+    zzz(); // #break
+}
+
+fn zzz() { () }