# This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details # HACK: LLDB's python API doesn't afford anything helpful for getting at variadic template parameters. # We're forced to resort to parsing names as strings. def templateParams(s): depth = 0 start = s.find("<") + 1 result = [] for i, c in enumerate(s[start:], start): if c == "<": depth += 1 elif c == ">": if depth == 0: result.append(s[start:i].strip()) break depth -= 1 elif c == "," and depth == 0: result.append(s[start:i].strip()) start = i + 1 return result def getType(target, typeName): stars = 0 typeName = typeName.strip() while typeName.endswith("*"): stars += 1 typeName = typeName[:-1] if typeName.startswith("const "): typeName = typeName[6:] ty = target.FindFirstType(typeName.strip()) for _ in range(stars): ty = ty.GetPointerType() return ty def luau_variant_summary(valobj, internal_dict, options): return valobj.GetChildMemberWithName("type").GetSummary()[1:-1] class LuauVariantSyntheticChildrenProvider: node_names = ["type", "value"] def __init__(self, valobj, internal_dict): self.valobj = valobj self.type_index = None self.current_type = None self.type_params = [] self.stored_value = None def num_children(self): return len(self.node_names) def has_children(self): return True def get_child_index(self, name): try: return self.node_names.index(name) except ValueError: return -1 def get_child_at_index(self, index): try: node = self.node_names[index] except IndexError: return None if node == "type": if self.current_type: return self.valobj.CreateValueFromExpression( node, f'(const char*)"{self.current_type.GetDisplayTypeName()}"' ) else: return self.valobj.CreateValueFromExpression( node, '(const char*)""' ) elif node == "value": if self.stored_value is not None: if self.current_type is not None: return self.valobj.CreateValueFromData( node, self.stored_value.GetData(), self.current_type ) else: return self.valobj.CreateValueExpression( node, '(const char*)""' ) else: return self.valobj.CreateValueFromExpression( node, '(const char*)""' ) else: return None def update(self): self.type_index = self.valobj.GetChildMemberWithName( "typeId" ).GetValueAsSigned() self.type_params = templateParams( self.valobj.GetType().GetCanonicalType().GetName() ) if len(self.type_params) > self.type_index: self.current_type = getType( self.valobj.GetTarget(), self.type_params[self.type_index] ) if self.current_type: storage = self.valobj.GetChildMemberWithName("storage") self.stored_value = storage.Cast(self.current_type) else: self.stored_value = None else: self.current_type = None self.stored_value = None return False class DenseHashTableSyntheticChildrenProvider: def __init__(self, valobj, internal_dict): """this call should initialize the Python object using valobj as the variable to provide synthetic children for""" self.valobj = valobj self.update() def num_children(self): """this call should return the number of children that you want your object to have""" return self.capacity def get_child_index(self, name): """this call should return the index of the synthetic child whose name is given as argument""" try: if name.startswith("[") and name.endswith("]"): return int(name[1:-1]) else: return -1 except Exception as e: print("get_child_index exception", e) return -1 def get_child_at_index(self, index): """this call should return a new LLDB SBValue object representing the child at the index given as argument""" try: dataMember = self.valobj.GetChildMemberWithName("data") data = dataMember.GetPointeeData(index) return self.valobj.CreateValueFromData( f"[{index}]", data, dataMember.Dereference().GetType(), ) except Exception as e: print("get_child_at_index error", e) def update(self): """this call should be used to update the internal state of this Python object whenever the state of the variables in LLDB changes.[1] Also, this method is invoked before any other method in the interface.""" self.capacity = self.valobj.GetChildMemberWithName( "capacity" ).GetValueAsUnsigned() def has_children(self): """this call should return True if this object might have children, and False if this object can be guaranteed not to have children.[2]""" return True class DenseHashMapSyntheticChildrenProvider: fixed_names = ["count", "capacity"] def __init__(self, valobj, internal_dict): self.valobj = valobj self.values = [] self.count = 0 def num_children(self): return self.count + len(self.fixed_names) def get_child_index(self, name): try: if name in self.fixed_names: return self.fixed_names.index(name) for index, (key, _) in enumerate(self.values): if key == name: return index + len(self.fixed_names) return -1 except Exception as e: print("get_child_index exception", e, name) return -1 def get_child_at_index(self, index): try: if index < len(self.fixed_names): fixed_name = self.fixed_names[index] impl_child = self.valobj.GetValueForExpressionPath( f".impl.{fixed_name}") return self.valobj.CreateValueFromData(fixed_name, impl_child.GetData(), impl_child.GetType()) else: index -= len(self.fixed_names) pair = self.items[index] key = pair["key"] value = pair["value"] return self.valobj.CreateValueFromData( f"[{key}]", value.data, value.GetType(), ) except Exception as e: print("get_child_at_index error", e, index) def update(self): try: capacity = self.valobj.GetChildMemberWithName("impl").GetChildMemberWithName( "capacity" ).GetValueAsUnsigned() self.items = [] for index in range(0, capacity): child_pair = self.valobj.GetValueForExpressionPath( f".impl.data[{index}]") child_key_valobj = child_pair.GetChildMemberWithName("first") if child_key_valobj.TypeIsPointerType() and child_key_valobj.GetValueAsUnsigned() == 0: continue child_key = child_key_valobj.GetValue() if child_key is None: child_key = child_key_valobj.GetSummary() if child_key is None: child_key = f"<{index} ({child_key_valobj.GetTypeName()})>" child_value = child_pair.GetChildMemberWithName("second") self.items.append({"key": child_key, "value": child_value}) self.count = len(self.items) except Exception as e: print("update error", e) def has_children(self): return True class DenseHashSetSyntheticChildrenProvider: fixed_names = ["count", "capacity"] def __init__(self, valobj, internal_dict): self.valobj = valobj self.values = [] self.count = 0 def num_children(self): return self.count + len(self.fixed_names) def get_child_index(self, name): try: if name in self.fixed_names: return self.fixed_names.index(name) if name.startswith("[") and name.endswith("]"): return int(name[1:-1]) + len(self.fixed_names) return -1 except Exception as e: print("get_child_index exception", e, name) return -1 def get_child_at_index(self, index): try: if index < len(self.fixed_names): fixed_name = self.fixed_names[index] impl_child = self.valobj.GetValueForExpressionPath( f".impl.{fixed_name}") return self.valobj.CreateValueFromData(fixed_name, impl_child.GetData(), impl_child.GetType()) else: index -= len(self.fixed_names) value = self.items[index] return self.valobj.CreateValueFromData( f"[{index}]", value.data, value.GetType(), ) except Exception as e: print("get_child_at_index error", e, index) def update(self): try: capacity = self.valobj.GetChildMemberWithName("impl").GetChildMemberWithName( "capacity" ).GetValueAsUnsigned() self.items = [] for index in range(0, capacity): child_value = self.valobj.GetValueForExpressionPath( f".impl.data[{index}]") if child_value.TypeIsPointerType() and child_value.GetValueAsUnsigned() == 0: continue self.items.append(child_value) self.count = len(self.items) except Exception as e: print("update error", e) def has_children(self): return True def luau_symbol_summary(valobj, internal_dict, options): local = valobj.GetChildMemberWithName("local") global_ = valobj.GetChildMemberWithName( "global").GetChildMemberWithName("value") if local.GetValueAsUnsigned() != 0: return f'local {local.GetChildMemberWithName("name").GetChildMemberWithName("value").GetSummary()}' elif global_.GetValueAsUnsigned() != 0: return f"global {global_.GetSummary()}" else: return "???" class AstArraySyntheticChildrenProvider: def __init__(self, valobj, internal_dict): self.valobj = valobj def num_children(self): return self.size def get_child_index(self, name): try: if name.startswith("[") and name.endswith("]"): return int(name[1:-1]) else: return -1 except Exception as e: print("get_child_index error:", e) def get_child_at_index(self, index): try: dataMember = self.valobj.GetChildMemberWithName("data") data = dataMember.GetPointeeData(index) return self.valobj.CreateValueFromData( f"[{index}]", data, dataMember.Dereference().GetType() ) except Exception as e: print("get_child_index error:", e) def update(self): self.size = self.valobj.GetChildMemberWithName( "size").GetValueAsUnsigned() def has_children(self): return True def luau_typepath_property_summary(valobj, internal_dict, options): name = valobj.GetChildMemberWithName("name").GetSummary() result = "[" read_write = False try: fflag_valobj = valobj.GetFrame().GetValueForVariablePath( "FFlag::DebugLuauReadWriteProperties::value") read_write = fflag_valobj.GetValue() == "true" except Exception as e: print("luau_typepath_property_summary error:", e) if read_write: is_read = valobj.GetChildMemberWithName("isRead").GetValue() == "true" if is_read: result += "read " else: result += "write " result += name result += "]" return result