|
3 | 3 | from lldb import (
|
4 | 4 | SBData,
|
5 | 5 | SBError,
|
| 6 | + SBType, |
| 7 | + SBTypeStaticField, |
6 | 8 | SBValue,
|
7 | 9 | eBasicTypeLong,
|
8 | 10 | eBasicTypeUnsignedLong,
|
@@ -325,6 +327,179 @@ def _getCurrentVariantIndex(self, all_variants: SBValue) -> int:
|
325 | 327 | default_index = i
|
326 | 328 | return default_index
|
327 | 329 |
|
| 330 | +class MSVCEnumSyntheticProvider: |
| 331 | + """ |
| 332 | + Synthetic provider for sum-type enums on MSVC. For a detailed explanation of the internals, |
| 333 | + see: |
| 334 | +
|
| 335 | + https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs |
| 336 | + """ |
| 337 | + |
| 338 | + __slots__ = ["valobj", "variant", "value"] |
| 339 | + def __init__(self, valobj: SBValue, _dict: LLDBOpaque): |
| 340 | + self.valobj = valobj |
| 341 | + self.variant: SBValue |
| 342 | + self.value: SBValue |
| 343 | + self.update() |
| 344 | + |
| 345 | + def update(self): |
| 346 | + tag: SBValue = self.valobj.GetChildMemberWithName("tag") |
| 347 | + |
| 348 | + if tag.IsValid(): |
| 349 | + tag: int = tag.GetValueAsUnsigned() |
| 350 | + for child in self.valobj.GetNonSyntheticValue().children: |
| 351 | + if not child.name.startswith("variant"): |
| 352 | + continue |
| 353 | + |
| 354 | + variant_type: SBType = child.GetType() |
| 355 | + exact: SBTypeStaticField = variant_type.GetStaticFieldWithName( |
| 356 | + "DISCR_EXACT" |
| 357 | + ) |
| 358 | + |
| 359 | + if exact.IsValid(): |
| 360 | + discr: int = exact.GetConstantValue( |
| 361 | + self.valobj.target |
| 362 | + ).GetValueAsUnsigned() |
| 363 | + if tag == discr: |
| 364 | + self.variant = child |
| 365 | + self.value = child.GetChildMemberWithName("value").GetSyntheticValue() |
| 366 | + return |
| 367 | + else: # if invalid, DISCR must be a range |
| 368 | + begin: int = variant_type.GetStaticFieldWithName( |
| 369 | + "DISCR_BEGIN" |
| 370 | + ).GetConstantValue(self.valobj.target).GetValueAsUnsigned() |
| 371 | + end: int = variant_type.GetStaticFieldWithName( |
| 372 | + "DISCR_END" |
| 373 | + ).GetConstantValue(self.valobj.target).GetValueAsUnsigned() |
| 374 | + |
| 375 | + # begin isn't necessarily smaller than end, so we must test for both cases |
| 376 | + if begin < end: |
| 377 | + if begin <= tag <= end: |
| 378 | + self.variant = child |
| 379 | + self.value = child.GetChildMemberWithName("value").GetSyntheticValue() |
| 380 | + return |
| 381 | + else: |
| 382 | + if tag >= begin or tag <= end: |
| 383 | + self.variant = child |
| 384 | + self.value = child.GetChildMemberWithName("value").GetSyntheticValue() |
| 385 | + return |
| 386 | + else: # if invalid, tag is a 128 bit value |
| 387 | + tag_lo: int = self.valobj.GetChildMemberWithName("tag128_lo").GetValueAsUnsigned() |
| 388 | + tag_hi: int = self.valobj.GetChildMemberWithName("tag128_hi").GetValueAsUnsigned() |
| 389 | + |
| 390 | + tag: int = (tag_hi << 64) | tag_lo |
| 391 | + |
| 392 | + for child in self.valobj.GetNonSyntheticValue().children: |
| 393 | + if not child.name.startswith("variant"): |
| 394 | + continue |
| 395 | + |
| 396 | + variant_type: SBType = child.GetType() |
| 397 | + exact_lo: SBTypeStaticField = variant_type.GetStaticFieldWithName( |
| 398 | + "DISCR128_EXACT_LO" |
| 399 | + ) |
| 400 | + |
| 401 | + if exact_lo.IsValid(): |
| 402 | + exact_lo: int = exact_lo.GetConstantValue(self.valobj.target).GetValueAsUnsigned() |
| 403 | + exact_hi: int = variant_type.GetStaticFieldWithName( |
| 404 | + "DISCR128_EXACT_HI" |
| 405 | + ).GetConstantValue(self.valobj.target).GetValueAsUnsigned() |
| 406 | + |
| 407 | + discr: int = (exact_hi << 64) | exact_lo |
| 408 | + if tag == discr: |
| 409 | + self.variant = child |
| 410 | + self.value = child.GetChildMemberWithName("value").GetSyntheticValue() |
| 411 | + return |
| 412 | + else: # if invalid, DISCR must be a range |
| 413 | + begin_lo: int = variant_type.GetStaticFieldWithName( |
| 414 | + "DISCR128_BEGIN_LO" |
| 415 | + ).GetConstantValue(self.valobj.target).GetValueAsUnsigned() |
| 416 | + begin_hi: int = variant_type.GetStaticFieldWithName( |
| 417 | + "DISCR128_BEGIN_HI" |
| 418 | + ).GetConstantValue(self.valobj.target).GetValueAsUnsigned() |
| 419 | + |
| 420 | + end_lo: int = variant_type.GetStaticFieldWithName( |
| 421 | + "DISCR128_END_LO" |
| 422 | + ).GetConstantValue(self.valobj.target).GetValueAsUnsigned() |
| 423 | + end_hi: int = variant_type.GetStaticFieldWithName( |
| 424 | + "DISCR128_END_HI" |
| 425 | + ).GetConstantValue(self.valobj.target).GetValueAsUnsigned() |
| 426 | + |
| 427 | + begin = (begin_hi << 64) | begin_lo |
| 428 | + end = (end_hi << 64) | end_lo |
| 429 | + |
| 430 | + # begin isn't necessarily smaller than end, so we must test for both cases |
| 431 | + if begin < end: |
| 432 | + if begin <= tag <= end: |
| 433 | + self.variant = child |
| 434 | + self.value = child.GetChildMemberWithName("value").GetSyntheticValue() |
| 435 | + return |
| 436 | + else: |
| 437 | + if tag >= begin or tag <= end: |
| 438 | + self.variant = child |
| 439 | + self.value = child.GetChildMemberWithName("value").GetSyntheticValue() |
| 440 | + return |
| 441 | + |
| 442 | + def num_children(self) -> int: |
| 443 | + return self.value.GetNumChildren() |
| 444 | + |
| 445 | + def get_child_index(self, name: str) -> int: |
| 446 | + return self.value.GetIndexOfChildWithName(name) |
| 447 | + |
| 448 | + def get_child_at_index(self, index: int) -> SBValue: |
| 449 | + return self.value.GetChildAtIndex(index) |
| 450 | + |
| 451 | + def has_children(self) -> bool: |
| 452 | + return self.value.MightHaveChildren() |
| 453 | + |
| 454 | + def get_type_name(self) -> str: |
| 455 | + name = self.valobj.GetTypeName() |
| 456 | + # remove "enum2$<", str.removeprefix() is python 3.9+ |
| 457 | + name = name[7:] |
| 458 | + |
| 459 | + # MSVC misinterprets ">>" as a shift operator, so spaces are inserted by rust to |
| 460 | + # avoid that |
| 461 | + if name.endswith(" >"): |
| 462 | + name = name[:-2] |
| 463 | + elif name.endswith(">"): |
| 464 | + name = name[:-1] |
| 465 | + |
| 466 | + return name |
| 467 | + |
| 468 | + |
| 469 | +def MSVCEnumSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: |
| 470 | + enum_synth = MSVCEnumSyntheticProvider(valobj.GetNonSyntheticValue(), _dict) |
| 471 | + variant_names: SBType = valobj.target.FindFirstType( |
| 472 | + f"{enum_synth.valobj.GetTypeName()}::VariantNames" |
| 473 | + ) |
| 474 | + name_idx = ( |
| 475 | + enum_synth.variant.GetType() |
| 476 | + .GetStaticFieldWithName("NAME") |
| 477 | + .GetConstantValue(valobj.target) |
| 478 | + .GetValueAsUnsigned() |
| 479 | + ) |
| 480 | + |
| 481 | + name: str = variant_names.enum_members[name_idx].name |
| 482 | + |
| 483 | + if enum_synth.num_children() == 0: |
| 484 | + return name |
| 485 | + |
| 486 | + child_name: str = enum_synth.value.GetChildAtIndex(0).name |
| 487 | + if child_name == "0" or child_name == "__0": |
| 488 | + # enum variant is a tuple struct |
| 489 | + return name + TupleSummaryProvider(enum_synth.value, _dict) |
| 490 | + else: |
| 491 | + # enum variant is a regular struct |
| 492 | + var_list = ( |
| 493 | + str(enum_synth.value.GetNonSyntheticValue()).split("= ", 1)[1].splitlines() |
| 494 | + ) |
| 495 | + vars = [x.strip() for x in var_list if x not in ("{", "}")] |
| 496 | + if vars[0][0] == "(": |
| 497 | + vars[0] = vars[0][1:] |
| 498 | + if vars[-1][-1] == ")": |
| 499 | + vars[-1] = vars[-1][:-1] |
| 500 | + |
| 501 | + return f'{name}{{{", ".join(vars)}}}' |
| 502 | + |
328 | 503 |
|
329 | 504 | class TupleSyntheticProvider:
|
330 | 505 | """Pretty-printer for tuples and tuple enum variants"""
|
|
0 commit comments