luau/tools/lldb_formatters.py

398 lines
12 KiB
Python
Raw Normal View History

2022-03-11 16:55:02 +00:00
# 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
2022-03-11 16:55:02 +00:00
result = []
for i, c in enumerate(s[start:], start):
if c == "<":
2022-03-11 16:55:02 +00:00
depth += 1
elif c == ">":
2022-03-11 16:55:02 +00:00
if depth == 0:
result.append(s[start:i].strip())
2022-03-11 16:55:02 +00:00
break
depth -= 1
elif c == "," and depth == 0:
result.append(s[start:i].strip())
2022-03-11 16:55:02 +00:00
start = i + 1
return result
2022-03-11 16:55:02 +00:00
def getType(target, typeName):
stars = 0
typeName = typeName.strip()
while typeName.endswith("*"):
2022-03-11 16:55:02 +00:00
stars += 1
typeName = typeName[:-1]
if typeName.startswith("const "):
2022-03-11 16:55:02 +00:00
typeName = typeName[6:]
ty = target.FindFirstType(typeName.strip())
for _ in range(stars):
ty = ty.GetPointerType()
return ty
2022-03-11 16:55:02 +00:00
def luau_variant_summary(valobj, internal_dict, options):
return valobj.GetChildMemberWithName("type").GetSummary()[1:-1]
2022-03-11 16:55:02 +00:00
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()}"'
)
2022-03-11 16:55:02 +00:00
else:
return self.valobj.CreateValueFromExpression(
node, '(const char*)"<unknown type>"'
)
2022-03-11 16:55:02 +00:00
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
)
2022-03-11 16:55:02 +00:00
else:
return self.valobj.CreateValueExpression(
node, '(const char*)"<unknown type>"'
)
2022-03-11 16:55:02 +00:00
else:
return self.valobj.CreateValueFromExpression(
node, '(const char*)"<no stored value>"'
)
2022-03-11 16:55:02 +00:00
else:
return None
def update(self):
self.type_index = self.valobj.GetChildMemberWithName(
"typeId"
).GetValueAsSigned()
self.type_params = templateParams(
self.valobj.GetType().GetCanonicalType().GetName()
)
2022-03-11 16:55:02 +00:00
if len(self.type_params) > self.type_index:
self.current_type = getType(
self.valobj.GetTarget(), self.type_params[self.type_index]
)
2022-03-11 16:55:02 +00:00
if self.current_type:
storage = self.valobj.GetChildMemberWithName("storage")
2022-05-06 01:03:43 +01:00
self.stored_value = storage.Cast(self.current_type)
2022-03-11 16:55:02 +00:00
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
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
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")
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
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):
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
self.size = self.valobj.GetChildMemberWithName(
"size").GetValueAsUnsigned()
def has_children(self):
return True
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
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