mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
3b0e93bec9
# What's changed? Add program argument passing to scripts run using the Luau REPL! You can now pass `--program-args` (or shorthand `-a`) to the REPL which will treat all remaining arguments as arguments to pass to executed scripts. These values can be accessed through variadic argument expansion. You can read these values like so: ``` local args = {...} -- gets you an array of all the arguments ``` For example if we run the following script like `luau test.lua -a test1 test2 test3`: ``` -- test.lua print(...) ``` you should get the output: ``` test1 test2 test3 ``` ### Native Code Generation * Improve A64 lowering for vector operations by using vector instructions * Fix lowering issue in IR value location tracking! - A developer reported a divergence between code run in the VM and Native Code Generation which we have now fixed ### New Type Solver * Apply substitution to type families, and emit new constraints to reduce those further * More progress on reducing comparison (`lt/le`)type families * Resolve two major sources of cyclic types in the new solver ### Miscellaneous * Turned internal compiler errors (ICE's) into warnings and errors ------- 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: Vyacheslav Egorov <vegorov@roblox.com> --------- 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: David Cope <dcope@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
404 lines
13 KiB
Python
404 lines
13 KiB
Python
# This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
import lldb
|
|
|
|
# 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*)"<unknown type>"'
|
|
)
|
|
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*)"<unknown type>"'
|
|
)
|
|
else:
|
|
return self.valobj.CreateValueFromExpression(
|
|
node, '(const char*)"<no stored value>"'
|
|
)
|
|
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"]
|
|
max_expand_children = 100
|
|
max_expand_capacity = 1000
|
|
|
|
def __init__(self, valobj, internal_dict):
|
|
self.valobj = valobj
|
|
self.count = 0
|
|
self.capacity = 0
|
|
|
|
def num_children(self):
|
|
return min(self.max_expand_children, 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)
|
|
|
|
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)
|
|
|
|
empty_key_valobj = self.valobj.GetValueForExpressionPath(
|
|
f".impl.empty_key")
|
|
key_type = empty_key_valobj.GetType().GetCanonicalType().GetName()
|
|
skipped = 0
|
|
|
|
for slot in range(0, min(self.max_expand_capacity, self.capacity)):
|
|
slot_pair = self.valobj.GetValueForExpressionPath(
|
|
f".impl.data[{slot}]")
|
|
slot_key_valobj = slot_pair.GetChildMemberWithName("first")
|
|
|
|
eq_test_valobj = self.valobj.EvaluateExpression(
|
|
f"*(reinterpret_cast<const {key_type}*>({empty_key_valobj.AddressOf().GetValueAsUnsigned()})) == *(reinterpret_cast<const {key_type}*>({slot_key_valobj.AddressOf().GetValueAsUnsigned()}))")
|
|
if eq_test_valobj.GetValue() == "true":
|
|
continue
|
|
|
|
# Skip over previous occupied slots.
|
|
if index > skipped:
|
|
skipped += 1
|
|
continue
|
|
|
|
slot_key = slot_key_valobj.GetSummary()
|
|
if slot_key is None:
|
|
slot_key = slot_key_valobj.GetValue()
|
|
|
|
if slot_key is None:
|
|
slot_key = slot_key_valobj.GetValueAsSigned()
|
|
|
|
if slot_key is None:
|
|
slot_key = slot_key_valobj.GetValueAsUnsigned()
|
|
|
|
if slot_key is None:
|
|
slot_key = str(index)
|
|
|
|
slot_value_valobj = slot_pair.GetChildMemberWithName("second")
|
|
return self.valobj.CreateValueFromData(f"[{slot_key}]", slot_value_valobj.GetData(), slot_value_valobj.GetType())
|
|
|
|
except Exception as e:
|
|
print("get_child_at_index error", e, index)
|
|
|
|
def update(self):
|
|
try:
|
|
self.capacity = self.count = self.valobj.GetValueForExpressionPath(
|
|
".impl.capacity").GetValueAsUnsigned()
|
|
self.count = self.valobj.GetValueForExpressionPath(
|
|
".impl.count").GetValueAsUnsigned()
|
|
except Exception as e:
|
|
print("update error", e)
|
|
|
|
def has_children(self):
|
|
return True
|
|
|
|
|
|
class DenseHashSetSyntheticChildrenProvider:
|
|
fixed_names = ["count", "capacity"]
|
|
max_expand_children = 100
|
|
max_expand_capacity = 1000
|
|
|
|
def __init__(self, valobj, internal_dict):
|
|
self.valobj = valobj
|
|
self.count = 0
|
|
self.capacity = 0
|
|
|
|
def num_children(self):
|
|
return min(self.max_expand_children, 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)
|
|
|
|
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)
|
|
|
|
empty_key_valobj = self.valobj.GetValueForExpressionPath(
|
|
f".impl.empty_key")
|
|
key_type = empty_key_valobj.GetType().GetCanonicalType().GetName()
|
|
skipped = 0
|
|
|
|
for slot in range(0, min(self.max_expand_capacity, self.capacity)):
|
|
slot_valobj = self.valobj.GetValueForExpressionPath(
|
|
f".impl.data[{slot}]")
|
|
|
|
eq_test_valobj = self.valobj.EvaluateExpression(
|
|
f"*(reinterpret_cast<const {key_type}*>({empty_key_valobj.AddressOf().GetValueAsUnsigned()})) == *(reinterpret_cast<const {key_type}*>({slot_valobj.AddressOf().GetValueAsUnsigned()}))")
|
|
if eq_test_valobj.GetValue() == "true":
|
|
continue
|
|
|
|
# Skip over previous occupied slots.
|
|
if index > skipped:
|
|
skipped += 1
|
|
continue
|
|
|
|
return self.valobj.CreateValueFromData(f"[{index}]", slot_valobj.GetData(), slot_valobj.GetType())
|
|
|
|
except Exception as e:
|
|
print("get_child_at_index error", e, index)
|
|
|
|
def update(self):
|
|
try:
|
|
self.capacity = self.count = self.valobj.GetValueForExpressionPath(
|
|
".impl.capacity").GetValueAsUnsigned()
|
|
self.count = self.valobj.GetValueForExpressionPath(
|
|
".impl.count").GetValueAsUnsigned()
|
|
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::DebugLuauDeferredConstraintResolution::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
|