| Server IP : 209.209.40.120 / Your IP : 216.73.217.112 Web Server : Microsoft-IIS/10.0 System : Windows NT NEWWWW 10.0 build 17763 (Windows Server 2019) i586 User : NEWWWW$ ( 0) PHP Version : 8.3.30 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : C:/Program Files/Android/Android Studio/bin/lldb/helpers/renderers/ |
Upload File : |
from __future__ import annotations
import re
from typing import List, Tuple, Union, Sequence
from jb_declarative_formatters import *
from jb_declarative_formatters.type_name_template import TypeNameTemplate
from jb_declarative_formatters.type_viz_expression import TypeVizFormatOptions, TypeVizCondition
from jb_declarative_formatters.type_viz_item_nodes import TypeVizItemExecCodeBlockTypeNode, \
TypeVizItemItemCodeBlockTypeNode, TypeVizItemIfCodeBlockTypeNode, TypeVizItemElseCodeBlockTypeNode, \
TypeVizItemElseIfCodeBlockTypeNode, TypeVizItemLoopCodeBlockTypeNode, TypeVizItemBreakCodeBlockTypeNode, \
TypeVizItemVariableTypeNode
from renderers.jb_lldb_builtin_formatters import StructChildrenProvider
from renderers.jb_lldb_declarative_formatters_options import *
from renderers.jb_lldb_format import overlay_child_format, update_value_dynamic_state, overlay_summary_format
from renderers.jb_lldb_format_specs import *
from renderers.jb_lldb_logging import get_suppress_errors
from renderers.jb_lldb_utils import *
g_cache_variables_initialized: dict[str, bool] = dict()
class NatVisDescriptor(AbstractVisDescriptor):
def __init__(self, candidates: List[Tuple[TypeViz, TypeVizName]], name_template: TypeNameTemplate):
self.type_name_template = name_template
self.viz_candidates = [(viz, viz_name, _match_type_viz_template(viz_name.type_name_template, name_template)) for
viz, viz_name in candidates]
def output_summary(self, value_non_synth: lldb.SBValue, stream: Stream):
for name_viz_pair in self.viz_candidates:
viz, type_viz_name, matches = name_viz_pair
old_context = set_global_intrinsics_context(IntrinsicsContext(
global_intrinsic_scope=viz.global_intrinsics, type_intrinsic_scope=viz.type_intrinsics))
try:
log("Trying visualizer for type '{}'...", str(type_viz_name))
if not _check_include_exclude_view_condition(viz, value_non_synth):
continue
if not viz.summaries:
log('No user provided summary found, return default...')
return self.output_summary_from_children(value_non_synth, stream)
# try to choose candidate from ordered display string expressions
success = _find_first_good_node(_process_summary_node, viz.summaries, value_non_synth,
matches, stream)
if success is not None:
return
except EvaluateError:
continue
finally:
set_global_intrinsics_context(old_context)
log("No matching display string candidate found, fallback to default")
return self.output_summary_from_children(value_non_synth, stream)
def output_summary_from_children(self, value_non_synth, stream):
children_provider = self.prepare_children(value_non_synth)
num_children = children_provider.num_children()
stream.output("{")
if stream.length > get_max_string_length():
stream.output('...')
elif num_children == 0:
stream.output('...')
else:
for child_index in range(num_children):
child: lldb.SBValue = children_provider.get_child_at_index(child_index)
child_non_synth = child.GetNonSyntheticValue()
child_name = child_non_synth.GetName() or ''
if child_name == RAW_VIEW_ITEM_NAME:
continue
if child_index != 0:
stream.output(", ")
if child_index > 2 or stream.length > get_max_string_length():
stream.output("...")
break
stream.output(child_name)
stream.output("=")
if stream.length > get_max_string_length():
stream.output("...")
break
stream.output_object(child_non_synth)
stream.output("}")
def prepare_children(self, value_non_synth: lldb.SBValue):
value_name = value_non_synth.GetName()
value_type_name = value_non_synth.GetType().GetName()
log("Initial retrieving children of value named '{}' of type '{}'...", value_name, value_type_name)
viz = None
providers = None
start_indexes = None
level = get_recursion_level()
if level >= g_max_recursion_level - 1:
log("Natvis visualizer for type '{}' of value '{}' has been ignored: "
"recursion level exceeds the maximum supported limit of {}",
value_type_name, value_name, g_max_recursion_level)
else:
for name_viz_pair in self.viz_candidates:
viz, type_viz_name, matches = name_viz_pair
try:
if not _check_include_exclude_view_condition(viz, value_non_synth):
continue
else:
old_context = set_global_intrinsics_context(
IntrinsicsContext(
global_intrinsic_scope=viz.global_intrinsics, type_intrinsic_scope=viz.type_intrinsics))
try:
set_recursion_level(level + 1)
providers, start_indexes = _try_update_child_providers(
value_non_synth, viz, type_viz_name,
self.type_name_template)
finally:
set_recursion_level(level)
set_global_intrinsics_context(old_context)
except EvaluateError as error:
log("Error occurred: {}", error)
continue
break
if providers is None:
log("No child provider found for '{}'", value_non_synth.GetType().GetName())
return StructChildrenProvider(value_non_synth)
return NatVisChildrenProvider(value_non_synth, viz, providers, start_indexes)
class NatVisChildrenProvider(AbstractChildrenProvider):
def __init__(self, value_non_synth: lldb.SBValue, viz, providers, child_providers_start_indexes):
self.viz: TypeViz = viz
self.child_providers: list[AbstractChildrenProvider] = providers
self.child_providers_start_indexes = child_providers_start_indexes
self.format_spec = value_non_synth.GetFormat()
def num_children(self):
if self.child_providers:
return sum(child_prov.num_children() for child_prov in self.child_providers)
else:
return 0
def has_children(self):
has_children = self.viz and bool(self.viz.item_providers)
return has_children
def get_child_index(self, name):
if not self.child_providers:
return INVALID_CHILD_INDEX
for prov in self.child_providers:
# noinspection PyBroadException
try:
index = prov.get_child_index(name)
except Exception:
# some unexpected error happened
if not get_suppress_errors():
raise
return INVALID_CHILD_INDEX
if index != INVALID_CHILD_INDEX:
return index
return INVALID_CHILD_INDEX
def get_child_at_index(self, index):
if not self.child_providers:
return None
child_provider, relative_index = self._find_child_provider(index)
if not child_provider:
return None
# noinspection PyBroadException
try:
child: lldb.SBValue = child_provider.get_child_at_index(relative_index)
if child is not None:
# apply inheritable formatting from parent value
overlay_child_format(child, self.format_spec)
return child
except Exception:
# some unexpected error happened
if not get_suppress_errors():
raise
return None
def _find_child_provider(self, index):
# TODO: binary search, not linear
for i, start_idx in enumerate(self.child_providers_start_indexes):
if start_idx > index:
# return previous provider
prov_index = i - 1
break
else:
# last provider
prov_index = len(self.child_providers) - 1
if prov_index == -1:
return None, index
prov = self.child_providers[prov_index]
child_start_idx = self.child_providers_start_indexes[prov_index]
return prov, (index - child_start_idx)
def _match_type_viz_template(type_viz_type_name_template: TypeNameTemplate,
type_name_template: TypeNameTemplate) -> Tuple[str, ...]:
wildcard_matches: list[TypeNameTemplate] = []
if not type_viz_type_name_template.match(type_name_template, wildcard_matches):
raise Exception("Inconsistent type matching: can't match template {} with {}"
.format(type_name_template, type_viz_type_name_template))
wildcard_matches_as_str = _fix_wildcard_matches(wildcard_matches)
return tuple(wildcard_matches_as_str)
def optional_node_processor(fn):
def wrapped(node, *args, **kwargs):
assert isinstance(node, TypeVizItemOptionalNodeMixin)
try:
return fn(node, *args, **kwargs)
except EvaluateError:
if not node.optional:
raise
except Exception:
raise
return None
return wrapped
def _evaluate_interpolated_string_to_stream(stream: Stream,
interp_string: TypeVizInterpolatedString,
ctx_val: lldb.SBValue,
wildcards=None,
context=None):
max_stream_length = get_max_string_length()
nested_stream = stream.create_nested()
for (s, expr) in interp_string.parts_list:
if nested_stream.length > max_stream_length:
break
nested_stream.output(s)
if expr is not None:
if nested_stream.length > max_stream_length:
break
_eval_display_string_expression(nested_stream, ctx_val, expr, wildcards, context)
stream.output(str(nested_stream))
return True
def _evaluate_interpolated_string(interp_string: TypeVizInterpolatedString, ctx_val, wildcards=None, context=None):
target = ctx_val.GetTarget()
is64bit: bool = target.GetAddressByteSize() == 8
stream = Stream(is64bit, get_recursion_level())
_evaluate_interpolated_string_to_stream(stream, interp_string, ctx_val, wildcards, context)
return str(stream)
def _check_include_exclude_view_condition(viz: TypeViz, value_non_synth: lldb.SBValue) -> bool:
if viz.include_view_id != 0:
if get_custom_view_id(value_non_synth.GetFormat()) != viz.include_view_id:
log("IncludeView condition is not satisfied '{}'...", str(viz.include_view))
return False
if viz.exclude_view_id != 0:
if get_custom_view_id(value_non_synth.GetFormat()) == viz.exclude_view_id:
log("ExcludeView condition is not satisfied '{}'...", str(viz.exclude_view))
return False
return True
@optional_node_processor
def _process_summary_node(summary: TypeVizSummary, ctx_val: lldb.SBValue, wildcards, stream: Stream):
# ctx_val is NonSynthetic
if summary.condition:
if not _process_node_condition(summary.condition, ctx_val, wildcards):
return None
if not _evaluate_interpolated_string_to_stream(stream, summary.value, ctx_val, wildcards):
return None
return True
def _fix_wildcard_matches(matches: list[TypeNameTemplate]) -> list[str]:
# remove breaking type prefixes from typenames
def _remove_type_prefix(typename: str) -> str:
prefix_list = ['struct ', 'class ']
for prefix in prefix_list:
if typename.startswith(prefix):
typename = typename[len(prefix):]
return typename
return [_remove_type_prefix(str(t)) for t in matches]
def _try_update_child_providers(val_obj_non_synth: lldb.SBValue, viz: TypeViz, type_viz_name: TypeVizName,
type_name_template: TypeNameTemplate):
log("Trying visualizer for type '{}'...", str(type_viz_name))
wildcard_matches = _match_type_viz_template(type_viz_name.type_name_template, type_name_template)
child_providers = _build_child_providers(viz.item_providers, val_obj_non_synth,
wildcard_matches,
viz.hide_raw_view) if viz.item_providers is not None else None
child_providers_start_indexes = None
if child_providers:
start_idx = 0
child_providers_start_indexes = []
for prov in child_providers:
child_providers_start_indexes.append(start_idx)
start_idx += prov.num_children()
return child_providers, child_providers_start_indexes
def _check_condition(val, condition, context=None):
res = eval_expression(val, '(bool)(' + condition + ')', None, context)
if not res.GetValueAsUnsigned():
return False
return True
TEMPLATE_REGEX = re.compile(r'\$T([1-9][0-9]*)')
def _resolve_wildcards(expr: str, wildcards: Sequence[str]):
expr_len = len(expr)
i = 0
s = StringIO()
while i < expr_len:
m = TEMPLATE_REGEX.search(expr, i)
if m is None:
s.write(expr[i:])
break
s.write(m.string[i:m.start()])
wildcard_idx = int(m.group(1)) - 1
try:
replacement = wildcards[wildcard_idx]
except IndexError:
replacement = m.string[m.start():m.end()]
s.write(replacement)
i = m.end()
if i < expr_len and replacement and replacement[-1] == '>' and expr[i] == '>':
# write extra space between >>
s.write(' ')
return s.getvalue()
def _resolve_wildcards_in_interpolated_string(interp_string: TypeVizInterpolatedString, wildcards):
parts_list = []
for part in interp_string.parts_list:
expr = part[1]
if expr is None:
parts_list.append((part[0], None))
continue
text = _resolve_wildcards(expr.text, wildcards)
options = expr.view_options
array_size = _resolve_wildcards(options.array_size, wildcards) if options.array_size else None
format_spec = options.format_spec
view_spec = options.view_spec
expr = TypeVizExpression(text, array_size, format_spec, view_spec)
parts_list.append((part[0], expr))
return TypeVizInterpolatedString(parts_list)
def _convert_format_flags(format_flags: TypeVizFormatFlags) -> int:
flags = 0
for from_, to in TYPE_VIZ_FORMAT_FLAGS_TO_LLDB_FORMAT_MAP.items():
if format_flags & from_:
flags |= to
return flags
def _apply_value_formatting(val: lldb.SBValue, format_spec: TypeVizFormatSpec, format_flags: TypeVizFormatFlags,
size: Optional[int], format_view_spec: int):
fmt = lldb.eFormatDefault
# both format_spec and format_view_spec can't be set simultaneously
if format_spec is not None:
fmt = TYPE_VIZ_FORMAT_SPEC_TO_LLDB_FORMAT_MAP.get(format_spec, lldb.eFormatDefault)
elif format_view_spec != 0:
fmt = format_view_spec << 20
if format_flags:
fmt |= _convert_format_flags(format_flags)
val_root = get_root_value(val)
if size is not None:
fmt |= eFormatAsArray
val_root.SetFormatAsArraySize(size)
# noinspection PyArgumentList
val_root.SetFormat(fmt)
if fmt & eFormatNoDerived:
val.SetPreferDynamicValue(lldb.eNoDynamicValues)
return val
def _eval_display_string_expression(stream: Stream, ctx, expr, wildcards, context):
if stream.level >= g_max_recursion_level:
return
expr_text = _resolve_wildcards(expr.text, wildcards) if wildcards else expr.text
opts = expr.view_options
result = eval_expression(ctx, expr_text, None, context)
result_non_synth = result.GetNonSyntheticValue()
err = result_non_synth.GetError()
if err.Fail():
stream.output("???")
return
array_size_expr = opts.array_size
if array_size_expr is not None and wildcards:
array_size_expr = _resolve_wildcards(array_size_expr, wildcards)
size = _eval_expression_result_array_size(ctx, array_size_expr) if array_size_expr is not None else None
result = _apply_value_formatting(result, opts.format_spec, opts.format_flags, size,
opts.view_spec_id)
# parent value size formatting is not ignored only for summaries
overlay_summary_format(result, ctx)
stream.output_object(result_non_synth)
def _process_node_condition(condition: TypeVizCondition,
ctx_val: lldb.SBValue, wildcards, index_str: str = None) -> bool:
if condition.include_view_id != 0:
if get_custom_view_id(ctx_val.GetFormat()) != condition.include_view_id:
return False
if condition.exclude_view_id != 0:
if get_custom_view_id(ctx_val.GetFormat()) == condition.exclude_view_id:
return False
if condition.condition:
processed_condition = _resolve_wildcards(condition.condition, wildcards)
if index_str:
processed_condition = processed_condition.replace('$i', index_str)
if not _check_condition(ctx_val, processed_condition, None):
return False
return True
def _eval_expression_result_array_size(ctx, size_expr):
size_value = eval_expression(ctx, size_expr, None)
size = size_value.GetValueAsSigned()
if not isinstance(size, int):
raise EvaluateError('Size value must be of integer type')
return size
@optional_node_processor
def _node_processor_display_value(node: Union[
TypeVizItemConditionalNodeMixin, TypeVizItemFormattedExpressionNodeMixin, Optional[TypeVizItemNamedNodeMixin]],
ctx_val: lldb.SBValue, wildcards: tuple):
if node.condition:
if not _process_node_condition(node.condition, ctx_val, wildcards):
return None
expression = _resolve_wildcards(node.expr.text, wildcards)
name = node.name if isinstance(node, TypeVizItemNamedNodeMixin) else None
value = eval_expression(ctx_val, expression, name)
opts: TypeVizFormatOptions = node.expr.view_options
array_size_expr = opts.array_size
if array_size_expr is not None and wildcards:
array_size_expr = _resolve_wildcards(array_size_expr, wildcards)
size = _eval_expression_result_array_size(ctx_val, array_size_expr) if array_size_expr is not None else None
value = _apply_value_formatting(value, opts.format_spec, opts.format_flags, size, opts.view_spec_id)
return value
class SingleItemProvider(AbstractChildrenProvider):
def __init__(self, value):
self.value = value
def num_children(self):
return 1
def get_child_index(self, name):
if self.value.GetName() == name:
return 0
return INVALID_CHILD_INDEX
def get_child_at_index(self, index):
assert index == 0
return self.value
RAW_VIEW_ITEM_NAME = "Raw View"
class RawViewItemProvider(AbstractChildrenProvider):
def __init__(self, value: lldb.SBValue):
address = value.GetLoadAddress()
child = value.CreateValueFromAddress(RAW_VIEW_ITEM_NAME, address, value.GetType())
set_value_format(child, eFormatRawView)
self.value = child
def num_children(self):
return 1
def get_child_index(self, name):
if name == RAW_VIEW_ITEM_NAME:
return 0
return INVALID_CHILD_INDEX
def get_child_at_index(self, index):
assert index == 0
return self.value
def _process_item_provider_single(item_provider: TypeVizItemProviderSingle, val: lldb.SBValue, wildcards: tuple):
item_value = _node_processor_display_value(item_provider, val, wildcards)
if not item_value:
return None
return SingleItemProvider(item_value)
class ExpandedItemProvider(AbstractChildrenProvider):
def __init__(self, value):
self.value = value
def num_children(self):
num = self.value.GetNumChildren()
if num != 0 and self.get_child_index(RAW_VIEW_ITEM_NAME) != INVALID_CHILD_INDEX:
return num - 1
return num
def get_child_index(self, name):
return self.value.GetIndexOfChildWithName(name)
def get_child_at_index(self, index):
result: lldb.SBValue = self.value.GetChildAtIndex(index)
update_value_dynamic_state(result)
return result if result.GetNonSyntheticValue().GetName() != RAW_VIEW_ITEM_NAME else None
def _process_item_provider_expanded(item_provider, val, wildcards):
item_value: lldb.SBValue = _node_processor_display_value(item_provider, val, wildcards)
if not item_value:
return None
return ExpandedItemProvider(item_value)
def _find_first_good_node(node_proc, nodes, *args, **kwargs) -> Optional[lldb.SBValue]:
for node in nodes:
item_value = node_proc(node, *args, **kwargs)
if item_value is not None:
return item_value
return None
@optional_node_processor
def _node_processor_size(size_node, ctx_val, wildcards):
assert isinstance(size_node, TypeVizItemSizeTypeNode)
if size_node.condition:
if not _process_node_condition(size_node.condition, ctx_val, wildcards):
return None
expression = size_node.text
expression = _resolve_wildcards(expression, wildcards)
value = eval_expression(ctx_val, expression, None)
result_value = value.GetValueAsSigned()
if not isinstance(result_value, int):
raise EvaluateError('Size value must be of integer type')
return result_value
def _node_processor_array_items_value_pointer(value_pointer_node, ctx_val, wildcards):
assert isinstance(value_pointer_node, TypeVizItemValuePointerTypeNode)
if value_pointer_node.condition:
if not _process_node_condition(value_pointer_node.condition, ctx_val, wildcards):
return None
expr = value_pointer_node.expr
expression = expr.text
opts: TypeVizFormatOptions = expr.view_options
expression = _resolve_wildcards(expression, wildcards)
value = eval_expression(ctx_val, expression, None)
array_size_expr = opts.array_size
if array_size_expr is not None and wildcards:
array_size_expr = _resolve_wildcards(array_size_expr, wildcards)
size = _eval_expression_result_array_size(ctx_val, array_size_expr) if array_size_expr is not None else None
value = _apply_value_formatting(value, opts.format_spec, opts.format_flags, size, opts.view_spec_id)
return value
class ArrayItemsProvider(AbstractChildrenProvider):
def __init__(self, size, value_pointer, elem_type):
self.size = size
self.value_pointer = value_pointer
self.elem_type = elem_type
self.elem_byte_size = elem_type.GetByteSize()
def num_children(self):
return self.size
def get_child_index(self, name):
try:
return int(name.lstrip('[').rstrip(']'))
except ValueError:
return INVALID_CHILD_INDEX
def get_child_at_index(self, index):
child_name = "[{}]".format(index)
offset = index * self.elem_byte_size
return self.value_pointer.CreateChildAtOffset(child_name, offset, self.elem_type)
@optional_node_processor
def _node_processor_array_items(array_items_node, ctx_val, wildcards):
assert isinstance(array_items_node, TypeVizItemProviderArrayItems)
if array_items_node.condition:
if not _process_node_condition(array_items_node.condition, ctx_val, wildcards):
return None
size = _find_first_good_node(_node_processor_size, array_items_node.size_nodes, ctx_val, wildcards)
# ???
if size is None:
raise EvaluateError('No valid Size node found')
value_pointer_value = _find_first_good_node(_node_processor_array_items_value_pointer,
array_items_node.value_pointer_nodes, ctx_val, wildcards)
# ???
if value_pointer_value is None:
raise EvaluateError('No valid ValuePointerType node found')
value_pointer_type = value_pointer_value.GetNonSyntheticValue().GetType()
if value_pointer_type.IsPointerType():
elem_type = value_pointer_type.GetPointeeType()
elif value_pointer_type.IsArrayType():
elem_type = value_pointer_type.GetArrayElementType()
value_pointer_value = value_pointer_value.GetNonSyntheticValue().AddressOf()
else:
raise EvaluateError('Value pointer is not of pointer or array type ({})'.format(str(value_pointer_type)))
return ArrayItemsProvider(size, value_pointer_value, elem_type)
def _process_item_provider_array_items(item_provider, val, wildcards):
return _node_processor_array_items(item_provider, val, wildcards)
LIST_ITEM_SUBSCRIPT_INDEX_REGEX = re.compile(r'\s*\[\s*\$i\s*]\s*')
g_cache_subscript_is_missing = dict[str, dict[str, str]]()
def _trying_eval_list_item_indexed_value(expression: str,
ctx_val: lldb.SBValue,
idx_str: str,
name: str) -> (Optional[lldb.SBValue], Optional[str]):
try:
expanded_expression = expression.replace('$i', idx_str)
value: lldb.SBValue = eval_expression(ctx_val, expanded_expression, name)
return value, None
except EvaluateError as evaluate_error:
type_doesnt_have_subscript = evaluate_error.args and \
evaluate_error.args[0] and \
evaluate_error.args[0].endswith('does not provide a subscript operator')
if not type_doesnt_have_subscript:
raise
log("Subscript operator is missing. Wa are trying to get children via natvis. "
"At the moment, we only support the simple syntax 'member[$i]'")
res = LIST_ITEM_SUBSCRIPT_INDEX_REGEX.search(expression)
if res is None:
log("We can't find expression '[$i]' for evaluate in '{}'", expression)
raise EvaluateError("We can't find expression '[$i]' for evaluate")
another_occurrence = LIST_ITEM_SUBSCRIPT_INDEX_REGEX.search(expression, pos=res.end())
if another_occurrence is not None:
log("There are more than one expression '[$i]' in '{}'", expression)
raise EvaluateError("More than one expression '[$i]'")
child_item_expression = expression[:res.start()]
return None, child_item_expression
def _trying_get_indexed_item_value(expression: str,
ctx_val: lldb.SBValue,
idx: int, idx_str: str,
name: str) -> lldb.SBValue:
type_name = ctx_val.type.name
if (dict_with_expr := g_cache_subscript_is_missing.get(type_name, None)) is None:
g_cache_subscript_is_missing[type_name] = dict[str, str]()
child_item_expression = dict_with_expr.get(expression, None) if dict_with_expr else None
already_calculated = child_item_expression is not None
if not already_calculated:
value, child_item_expression = _trying_eval_list_item_indexed_value(expression, ctx_val, idx_str, name)
if value:
return value
value: lldb.SBValue = eval_expression(ctx_val, child_item_expression, name)
if not already_calculated:
child_item_type_name = value.type.name
# noinspection PyUnusedLocal
force_evaluate_children_count = value.num_children
if child_item_type_name not in IndexListItemsProvider.types_with_index_list_items:
log("IndexListItems expression '{}' doesn't have subscript operator "
"and doesn't have implementation in natvis type '{}'", expression, child_item_type_name)
raise EvaluateError("IndexListItems item doesn't have subscript operator")
g_cache_subscript_is_missing[type_name][expression] = child_item_expression
return value.GetChildAtIndex(idx)
def _node_processor_index_list_items_value_node(idx: int, name: str,
index_list_value_node: TypeVizItemIndexNodeTypeNode,
ctx_val: lldb.SBValue, wildcards):
idx_str = str(idx)
if index_list_value_node.condition:
if not _process_node_condition(index_list_value_node.condition, ctx_val, wildcards, idx_str):
return None
expression = index_list_value_node.expr.text
expression = _resolve_wildcards(expression, wildcards)
opts: TypeVizFormatOptions = index_list_value_node.expr.view_options
value = _trying_get_indexed_item_value(expression, ctx_val, idx, idx_str, name)
array_size_expr = opts.array_size
if array_size_expr is not None and wildcards:
array_size_expr = _resolve_wildcards(array_size_expr, wildcards)
size = _eval_expression_result_array_size(ctx_val, array_size_expr) if array_size_expr is not None else None
value = _apply_value_formatting(value, opts.format_spec, opts.format_flags, size, opts.view_spec_id)
return value
class IndexListItemsProvider(AbstractChildrenProvider):
types_with_index_list_items: set[str] = set[str]()
def __init__(self, size, index_list_node, ctx_val, wildcards):
self.size = size
self.index_list_node = index_list_node
self.ctx_val = ctx_val
self.wildcards = wildcards
self.types_with_index_list_items.add(ctx_val.type.name)
def num_children(self):
return self.size
def get_child_index(self, name):
try:
return int(name.lstrip('[').rstrip(']'))
except ValueError:
return INVALID_CHILD_INDEX
def get_child_at_index(self, index):
name = "[{}]".format(index)
value = None
for value_node_node in self.index_list_node.value_node_nodes:
value = _node_processor_index_list_items_value_node(index, name, value_node_node, self.ctx_val,
self.wildcards)
if value:
break
# TODO: show some error value on None
return value
@optional_node_processor
def _node_processor_index_list_items(index_list_node, ctx_val, wildcards):
assert isinstance(index_list_node, TypeVizItemProviderIndexListItems)
if index_list_node.condition:
if not _process_node_condition(index_list_node.condition, ctx_val, wildcards):
return None
size = _find_first_good_node(_node_processor_size, index_list_node.size_nodes, ctx_val, wildcards)
# ????
if size is None:
raise EvaluateError('No valid Size node found')
return IndexListItemsProvider(size, index_list_node, ctx_val, wildcards)
def _process_item_provider_index_list_items(item_provider, val, wildcards):
return _node_processor_index_list_items(item_provider, val, wildcards)
def _is_valid_node_ptr(node):
if node is None:
return False
if not node.TypeIsPointerType():
return False
return True
def _get_ptr_value(node):
val = node.GetNonSyntheticValue()
return val.GetValueAsUnsigned() if _is_valid_node_ptr(val) else 0
class NodesProvider(object):
def __init__(self):
self.cache = []
self.has_more = False
self.names = None
self.name2index = None
class CustomItemsProvider(AbstractChildrenProvider):
def __init__(self, nodes_provider, value_expression, value_opts, wildcards):
assert isinstance(nodes_provider, NodesProvider)
self.nodes_cache = nodes_provider.cache
self.has_more = nodes_provider.has_more
self.custom_names = nodes_provider.names
self.custom_name_to_index = nodes_provider.name2index
self.value_expression = value_expression
self.value_opts = value_opts
self.wildcards = wildcards
self.size = len(self.nodes_cache)
def num_children(self):
return self.size
def get_child_index(self, name):
if self.custom_name_to_index:
return self.custom_name_to_index.get(name, INVALID_CHILD_INDEX)
try:
return int(name.lstrip('[').rstrip(']'))
except ValueError:
return INVALID_CHILD_INDEX
def get_child_at_index(self, index):
if index < 0 or index >= self.size:
return None
node_value: lldb.SBValue = self.nodes_cache[index]
if node_value is None:
return None
if self.custom_names:
name = self.custom_names[index]
else:
name = "[{}]".format(index)
value = eval_expression(node_value, self.value_expression, name)
opts: TypeVizFormatOptions = self.value_opts
array_size_expr = opts.array_size
if array_size_expr is not None and self.wildcards:
array_size_expr = _resolve_wildcards(array_size_expr, self.wildcards)
size = _eval_expression_result_array_size(node_value, array_size_expr) if array_size_expr is not None else None
value = _apply_value_formatting(value, opts.format_spec, opts.format_flags, size, opts.view_spec_id)
return value
class LinkedListIterator(object):
def __init__(self, node_value, next_expression):
self.node_value = node_value
self.next_expression = next_expression
def __bool__(self):
return _get_ptr_value(self.node_value) != 0
def __eq__(self, other):
return _get_ptr_value(self.node_value) == _get_ptr_value(other.node_value)
def cur_value(self):
return self.node_value.GetNonSyntheticValue().Dereference()
def cur_ptr(self):
return self.node_value.GetNonSyntheticValue().GetValueAsUnsigned()
def move_to_next(self):
self.node_value = self._next()
def _next(self):
return eval_expression(self.cur_value(), self.next_expression, None)
class LinkedListIndexedNodesProvider(NodesProvider):
def __init__(self, size, head_pointer, next_expression):
super(LinkedListIndexedNodesProvider, self).__init__()
it = LinkedListIterator(head_pointer, next_expression)
cache = []
has_more = False
# iterate all list nodes and cache them
start = _get_ptr_value(it.node_value)
max_size = size if size is not None else g_max_num_children
idx = 0
while it and idx < max_size:
cache.append(it.cur_value())
idx += 1
it.move_to_next()
if it and _get_ptr_value(it.node_value) == start:
# check for cycled
break
if size is None:
if it and idx >= max_size:
has_more = True
else:
if idx < size:
cache.extend([None] * (size - idx))
self.cache = cache
self.has_more = has_more
self.names = None
self.name2index = None
class LinkedListCustomNameNodesProvider(NodesProvider):
def __init__(self, size, head_pointer, next_expression, custom_value_name, wildcards):
super(LinkedListCustomNameNodesProvider, self).__init__()
it = LinkedListIterator(head_pointer, next_expression)
cache = []
has_more = False
names = []
name2index = {}
# iterate all list nodes and cache them
max_size = size if size is not None else g_max_num_children
idx = 0
start = _get_ptr_value(it.node_value)
while it and idx < max_size:
cur_val = it.cur_value()
name = _evaluate_interpolated_string(custom_value_name, cur_val, wildcards)
names.append(name)
name2index[name] = idx
cache.append(cur_val)
idx += 1
it.move_to_next()
if it and _get_ptr_value(it.node_value) == start:
# check for cycled
break
if size is None:
if it and idx >= max_size:
has_more = True
else:
if idx < size:
cache.extend([None] * (size - idx))
self.cache = cache
self.has_more = has_more
self.names = names
self.name2index = name2index
def _node_processor_linked_list_items_head_pointer(head_pointer_node, ctx_val, wildcards):
assert isinstance(head_pointer_node, TypeVizItemListItemsHeadPointerTypeNode)
expression = _resolve_wildcards(head_pointer_node.text, wildcards)
return eval_expression(ctx_val, expression, None)
@optional_node_processor
def _node_processor_linked_list_items(linked_list_node, ctx_val, wildcards):
assert isinstance(linked_list_node, TypeVizItemProviderLinkedListItems)
if linked_list_node.condition:
if not _process_node_condition(linked_list_node.condition, ctx_val, wildcards):
return None
size = _find_first_good_node(_node_processor_size, linked_list_node.size_nodes, ctx_val, wildcards)
# size can be None
head_pointer_value = _node_processor_linked_list_items_head_pointer(linked_list_node.head_pointer_node, ctx_val,
wildcards)
next_pointer_node = linked_list_node.next_pointer_node
assert isinstance(next_pointer_node, TypeVizItemListItemsNextPointerTypeNode)
next_pointer_expression = _resolve_wildcards(next_pointer_node.text, wildcards)
value_node = linked_list_node.value_node_node
assert isinstance(value_node, TypeVizItemListItemsIndexNodeTypeNode)
value_expression = _resolve_wildcards(value_node.expr.text, wildcards)
value_opts = value_node.expr.view_options
if value_node.name is None:
nodes_provider = LinkedListIndexedNodesProvider(size, head_pointer_value, next_pointer_expression)
else:
nodes_provider = LinkedListCustomNameNodesProvider(size, head_pointer_value, next_pointer_expression,
value_node.name, wildcards)
return CustomItemsProvider(nodes_provider, value_expression, value_opts, wildcards)
def _process_item_provider_linked_list_items(item_provider, val, wildcards):
return _node_processor_linked_list_items(item_provider, val, wildcards)
class BinaryTreeIndexedNodesProvider(NodesProvider):
def __init__(self, size, head_pointer, left_expression, right_expression, node_condition):
super(BinaryTreeIndexedNodesProvider, self).__init__()
cache = []
has_more = False
# iterate all list nodes and cache them
max_size = size if size is not None else g_max_num_children
idx = 0
cur = head_pointer
stack = [] # parents
def check_condition(node):
if node_condition is None:
return True
return _check_condition(node.GetNonSyntheticValue().Dereference(), node_condition)
while (_get_ptr_value(cur) != 0 and check_condition(cur) or stack) and idx < max_size:
while _get_ptr_value(cur) != 0 and check_condition(cur):
if len(stack) > 100: # ~2^100 nodes can't be true - something went wrong
raise Exception("Invalid tree")
stack.append(cur)
cur = eval_expression(cur.GetNonSyntheticValue().Dereference(), left_expression, None)
cur = stack.pop()
cache.append(cur.GetNonSyntheticValue().Dereference())
idx += 1
cur = eval_expression(cur.GetNonSyntheticValue().Dereference(), right_expression, None)
if size is None:
if _get_ptr_value(cur) != 0 and check_condition(cur) or stack and idx >= max_size:
has_more = True
else:
if idx < size:
cache.extend([None] * (size - idx))
self.cache = cache
self.has_more = has_more
self.names = None
self.name2index = None
class BinaryTreeCustomNamesNodesProvider(NodesProvider):
def __init__(self, size, head_pointer, left_expression, right_expression, node_condition, custom_value_name,
wildcards):
super(BinaryTreeCustomNamesNodesProvider, self).__init__()
cache = []
has_more = False
names = []
name2index = {}
# iterate all list nodes and cache them
max_size = size if size is not None else g_max_num_children
idx = 0
cur = head_pointer
stack = [] # parents
def check_condition(node):
if node_condition is None:
return True
return _check_condition(node.GetNonSyntheticValue().Dereference(), node_condition)
while (_get_ptr_value(cur) != 0 and check_condition(cur) or stack) and idx < max_size:
while _get_ptr_value(cur) != 0 and check_condition(cur):
if len(stack) > 100: # ~2^100 nodes can't be true - something went wrong
raise Exception("Invalid tree")
stack.append(cur)
cur = eval_expression(cur.GetNonSyntheticValue().Dereference(), left_expression, None)
cur = stack.pop()
cur_val = cur.GetNonSyntheticValue().Dereference()
name = _evaluate_interpolated_string(custom_value_name, cur_val, wildcards)
names.append(name)
name2index[name] = idx
cache.append(cur_val)
idx += 1
cur = eval_expression(cur_val, right_expression, None)
if size is None:
if _get_ptr_value(cur) != 0 and check_condition(cur) or stack and idx >= max_size:
has_more = True
else:
if idx < size:
cache.extend([None] * (size - idx))
self.cache = cache
self.has_more = has_more
self.names = names
self.name2index = name2index
def _node_processor_tree_items_head_pointer(head_pointer_node, ctx_val, wildcards):
assert isinstance(head_pointer_node, TypeVizItemTreeHeadPointerTypeNode)
expression = _resolve_wildcards(head_pointer_node.text, wildcards)
return eval_expression(ctx_val, expression, None)
@optional_node_processor
def _node_processor_tree_items(tree_node, ctx_val, wildcards):
assert isinstance(tree_node, TypeVizItemProviderTreeItems)
if tree_node.condition:
if not _process_node_condition(tree_node.condition, ctx_val, wildcards):
return None
size = _find_first_good_node(_node_processor_size, tree_node.size_nodes, ctx_val, wildcards)
# size can be None
head_pointer_value = _node_processor_tree_items_head_pointer(tree_node.head_pointer_node, ctx_val,
wildcards)
left_pointer_node = tree_node.left_pointer_node
assert isinstance(left_pointer_node, TypeVizItemTreeChildPointerTypeNode)
right_pointer_node = tree_node.right_pointer_node
assert isinstance(right_pointer_node, TypeVizItemTreeChildPointerTypeNode)
left_pointer_expression = _resolve_wildcards(left_pointer_node.text, wildcards)
right_pointer_expression = _resolve_wildcards(right_pointer_node.text, wildcards)
value_node = tree_node.value_node_node
assert isinstance(value_node, TypeVizItemTreeNodeTypeNode)
value_expression = _resolve_wildcards(value_node.expr.text, wildcards)
value_opts = value_node.expr.view_options
condition = value_node.condition
value_condition = _resolve_wildcards(condition.condition, wildcards) if condition and condition.condition else None
if value_node.name is None:
nodes_provider = BinaryTreeIndexedNodesProvider(size, head_pointer_value,
left_pointer_expression, right_pointer_expression,
value_condition)
else:
nodes_provider = BinaryTreeCustomNamesNodesProvider(size, head_pointer_value,
left_pointer_expression, right_pointer_expression,
value_condition, value_node.name, wildcards)
return CustomItemsProvider(nodes_provider, value_expression, value_opts, wildcards)
def _process_item_provider_tree_items(tree_node, val, wildcards):
return _node_processor_tree_items(tree_node, val, wildcards)
class CustomListItemsInstruction(object):
def __init__(self, next_instruction, condition):
self.next_instruction = next_instruction
self.condition = condition
def evaluate_condition(self, ctx_val: lldb.SBValue, context) -> bool:
if not self.condition:
return True
return _check_condition(ctx_val, self.condition, context)
def execute(self, ctx_val: lldb.SBValue, context, items_collector: List[lldb.SBValue]):
return None
class CustomListItemsExecInstruction(CustomListItemsInstruction):
def __init__(self, code, condition, next_instruction):
super(CustomListItemsExecInstruction, self).__init__(next_instruction, condition)
self.code = code
def execute(self, ctx_val: lldb.SBValue, context, items_collector: List[lldb.SBValue]):
if self.evaluate_condition(ctx_val, context):
eval_expression(ctx_val, self.code, None, context)
return self.next_instruction
class CustomListItemsItemInstruction(CustomListItemsInstruction):
def __init__(self, name: TypeVizInterpolatedString, expr, opts, condition, next_instruction):
super(CustomListItemsItemInstruction, self).__init__(next_instruction, condition)
self.name: TypeVizInterpolatedString = name
self.expr = expr
self.opts = opts
def execute(self, ctx_val: lldb.SBValue, context, items_collector: List[lldb.SBValue]):
if self.evaluate_condition(ctx_val, context):
if self.name:
name = _evaluate_interpolated_string(self.name, ctx_val, context=context)
else:
name = "[{}]".format(len(items_collector))
item = eval_expression(ctx_val, self.expr, name, context)
if self.opts.array_size:
size_value = eval_expression(ctx_val, self.opts.array_size, None, context)
size = size_value.GetValueAsSigned()
else:
size = None
item = _apply_value_formatting(item, self.opts.format_spec, self.opts.format_flags, size,
self.opts.view_spec_id)
items_collector.append(item)
return self.next_instruction
return self.next_instruction
class CustomListItemsIfInstruction(CustomListItemsInstruction):
def __init__(self, condition, then_instruction, next_instruction):
super(CustomListItemsIfInstruction, self).__init__(next_instruction, condition)
self.then_instruction = then_instruction
def execute(self, ctx_val: lldb.SBValue, context, items_collector: List[lldb.SBValue]):
if self.evaluate_condition(ctx_val, context):
return self.then_instruction
return self.next_instruction
def _process_code_block_nodes(block_nodes, wildcards, next_instr, loop_breaks: List[CustomListItemsInstruction]):
end_if_instr = None
for node in reversed(block_nodes):
if isinstance(node, TypeVizItemExecCodeBlockTypeNode):
value = _resolve_wildcards(node.value, wildcards)
condition = _resolve_wildcards(node.condition, wildcards) if node.condition else None
next_instr = CustomListItemsExecInstruction(value, condition, next_instr)
elif isinstance(node, TypeVizItemItemCodeBlockTypeNode):
name = _resolve_wildcards_in_interpolated_string(node.name, wildcards) if node.name else None
expression = _resolve_wildcards(node.expr.text, wildcards)
opts = node.expr.view_options
condition = _resolve_wildcards(node.condition, wildcards) if node.condition else None
next_instr = CustomListItemsItemInstruction(name, expression, opts, condition, next_instr)
elif isinstance(node, TypeVizItemIfCodeBlockTypeNode):
condition = _resolve_wildcards(node.condition, wildcards)
if not end_if_instr:
end_if_instr = next_instr
then_instr = _process_code_block_nodes(node.code_blocks, wildcards, end_if_instr, loop_breaks)
next_instr = CustomListItemsIfInstruction(condition, then_instr, next_instr)
end_if_instr = None
elif isinstance(node, TypeVizItemElseCodeBlockTypeNode):
end_if_instr = next_instr
next_instr = _process_code_block_nodes(node.code_blocks, wildcards, next_instr, loop_breaks)
elif isinstance(node, TypeVizItemElseIfCodeBlockTypeNode):
condition = _resolve_wildcards(node.condition, wildcards) if node.condition else None
if not end_if_instr:
end_if_instr = next_instr
then_instr = _process_code_block_nodes(node.code_blocks, wildcards, end_if_instr, loop_breaks)
next_instr = CustomListItemsIfInstruction(condition, then_instr, next_instr)
elif isinstance(node, TypeVizItemLoopCodeBlockTypeNode):
condition = _resolve_wildcards(node.condition, wildcards) if node.condition else None
loop_instr = CustomListItemsIfInstruction(condition, None, next_instr)
loop_breaks.append(next_instr)
then_instr = _process_code_block_nodes(node.code_blocks, wildcards, loop_instr, loop_breaks)
loop_breaks.pop()
loop_instr.then_instruction = then_instr
next_instr = loop_instr
elif isinstance(node, TypeVizItemBreakCodeBlockTypeNode):
if node.condition and node.condition != "":
condition = _resolve_wildcards(node.condition, wildcards)
next_instr = CustomListItemsIfInstruction(condition, loop_breaks[-1], next_instr)
else:
next_instr = loop_breaks[-1]
return next_instr
g_static_counter = 0
def _process_variables_nodes(variable_nodes: List[TypeVizItemVariableTypeNode], wildcards):
prolog_collection = []
epilog_collection = []
first_time_code_collection = []
code_collection = []
for node in variable_nodes:
initial_value = _resolve_wildcards(node.initial_value, wildcards)
global g_static_counter
g_static_counter += 1
persistent_name = "$" + node.name + str(g_static_counter)
first_time_code_collection.append("auto {} = {};".format(persistent_name, initial_value))
code_collection.append("{} = {};".format(persistent_name, initial_value))
prolog_collection.append("auto {} = {};".format(node.name, persistent_name))
epilog_collection.append("{} = {};".format(persistent_name, node.name))
prolog = "".join(prolog_collection)
epilog = "".join(epilog_collection)
code = "".join(code_collection) + "1"
first_time_code = "".join(first_time_code_collection) + "1"
def create_context(ctx_var: lldb.SBValue, first_time: bool):
options = lldb.SBExpressionOptions()
eval_expression(ctx_var, first_time_code if first_time else code, options=options)
return EvaluationContext(prolog, epilog)
return create_context
class CustomListItemsProvider(AbstractChildrenProvider):
def __init__(self, instr: CustomListItemsInstruction,
size, ctx_val: lldb.SBValue, context: EvaluationContext):
self.cached_items = list()
max_size = size if size is not None else g_max_num_children
while instr and len(self.cached_items) < max_size:
instr = instr.execute(ctx_val, context, self.cached_items)
self.size = len(self.cached_items)
self.name_to_item = dict()
for i in range(self.size):
self.name_to_item[self.cached_items[i].GetName()] = i
def num_children(self):
return len(self.cached_items)
def get_child_index(self, name):
try:
return self.name_to_item[name]
except KeyError:
return INVALID_CHILD_INDEX
def get_child_at_index(self, index):
return self.cached_items[index]
g_node_to_evaluation_context_factory = {}
@optional_node_processor
def _node_processor_custom_list_items(tree_node: TypeVizItemProviderCustomListItems, ctx_val: lldb.SBValue, wildcards):
root_instr = _process_code_block_nodes(tree_node.code_block_nodes, wildcards, None, [])
if tree_node.condition:
if not _process_node_condition(tree_node.condition, ctx_val, wildcards):
return None
size = _find_first_good_node(_node_processor_size, tree_node.size_nodes, ctx_val, wildcards)
# size can be None
instantiated_node = (tree_node, wildcards)
if instantiated_node not in g_node_to_evaluation_context_factory:
context_factory = _process_variables_nodes(tree_node.variables_nodes, wildcards)
g_node_to_evaluation_context_factory[instantiated_node] = context_factory
context = context_factory(ctx_val, True)
else:
context_factory = g_node_to_evaluation_context_factory[instantiated_node]
context = context_factory(ctx_val, False)
return CustomListItemsProvider(root_instr, size, ctx_val, context)
def _process_item_provider_custom_list_items(tree_node, val, wildcards):
return _node_processor_custom_list_items(tree_node, val, wildcards)
def _build_child_providers(item_providers: list,
value_non_synth: lldb.SBValue,
wildcards: tuple,
hide_raw_view: bool) -> list[AbstractChildrenProvider]:
provider_handlers = {
TypeVizItemProviderTypeKind.Single: _process_item_provider_single,
TypeVizItemProviderTypeKind.Expanded: _process_item_provider_expanded,
TypeVizItemProviderTypeKind.ArrayItems: _process_item_provider_array_items,
TypeVizItemProviderTypeKind.IndexListItems: _process_item_provider_index_list_items,
TypeVizItemProviderTypeKind.LinkedListItems: _process_item_provider_linked_list_items,
TypeVizItemProviderTypeKind.TreeItems: _process_item_provider_tree_items,
TypeVizItemProviderTypeKind.CustomListItems: _process_item_provider_custom_list_items,
}
child_providers = []
for item_provider in item_providers:
handler = provider_handlers.get(item_provider.kind)
if not handler:
continue
child_provider = handler(item_provider, value_non_synth, wildcards)
if not child_provider:
continue
child_providers.append(child_provider)
if not hide_raw_view and (value_non_synth.GetFormat() & eFormatNoRawView) == 0:
child_providers.append(RawViewItemProvider(value_non_synth))
return child_providers