1## 2# Copyright (c) 2023 Apple Inc. All rights reserved. 3# 4# @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5# 6# This file contains Original Code and/or Modifications of Original Code 7# as defined in and that are subject to the Apple Public Source License 8# Version 2.0 (the 'License'). You may not use this file except in 9# compliance with the License. The rights granted to you under the License 10# may not be used to create, or enable the creation or redistribution of, 11# unlawful or unlicensed copies of an Apple operating system, or to 12# circumvent, violate, or enable the circumvention or violation of, any 13# terms of an Apple operating system software license agreement. 14# 15# Please obtain a copy of the License at 16# http://www.opensource.apple.com/apsl/ and read it before using this file. 17# 18# The Original Code and all software distributed under the License are 19# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23# Please see the License for the specific language governing rights and 24# limitations under the License. 25# 26# @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27## 28 29""" Mocking framework for LLDB scripted process target. 30 31 The goal of this module is to provide a mock object that behaves like 32 original SBType in target but serializes its properties into memory. 33 34 That allows injection of artificial type instances into testing target. 35""" 36 37from abc import ABC, abstractmethod 38import typing 39import lldb 40import io 41from lldbmock.utils import lookup_type, Singleton 42 43 44# 45# Data serializers 46# 47# A goal of a serializer is to convert between Python's native type and byte 48# array used internally by mocking layer. This allows users to more easily operate 49# on mock properties. 50# 51# For example: 52# 53# mock.numeric_property = 0x12345 54# 55# The value above ends up encoded as little endian at correct offset in the 56# mocked type memory. 57# 58 59 60class Serializer(ABC): 61 """ Value serializer. """ 62 63 @abstractmethod 64 def accepts(self, value: typing.Any) -> bool: 65 """ Checks that value instance is supported. """ 66 67 @abstractmethod 68 def serialize(self, value: typing.Any, size: int) -> bytes: 69 """ Serializes value to raw bytes""" 70 71 @abstractmethod 72 def deserialize(self, data: bytes, offs: int, size: int) -> typing.Any: 73 """ Deserialize value from raw bytes. """ 74 75 @abstractmethod 76 def default(self): 77 """ Return default value for a property. """ 78 79 @staticmethod 80 def createSerializerForType(sbtype: lldb.SBType) -> 'Serializer': 81 """ Construct serializer for given SBType. """ 82 83 flags = sbtype.GetTypeFlags() 84 85 if (flags & (lldb.eTypeIsInteger | lldb.eTypeIsPointer)) != 0: 86 return NumericSerializer() 87 88 # Default: No serializer enforces bytes instances as input. 89 return NoSerializer() 90 91 92class NoSerializer(Serializer, metaclass=Singleton): 93 """ No transformation, only enforces bytes as input. """ 94 95 def default(self): 96 return b'' 97 98 def accepts(self, value: typing.Any) -> bool: 99 if isinstance(value, bytes) or isinstance(value, bytearray): 100 return True 101 102 return False 103 104 def serialize(self, value: typing.Any, size: int) -> bytes: 105 return value[:size] 106 107 def deserialize(self, data: bytes, offs: int, size: int) -> typing.Any: 108 return data[offs : offs + size] 109 110 111class NumericSerializer(Serializer, metaclass=Singleton): 112 """ Serializes python's numeric (integral) types to bytes. """ 113 114 def default(self): 115 return 0 116 117 def accepts(self, value: typing.Any) -> bool: 118 if isinstance(value, int): 119 return True 120 121 return False 122 123 def serialize(self, value: typing.Any, size: int) -> bytes: 124 return value.to_bytes(length=size, byteorder='little') 125 126 def deserialize(self, data: bytes, offs: int, size: int) -> typing.Any: 127 return int.from_bytes(data[offs: offs + size], byteorder='little') 128 129 130# 131# Mock class properties 132# 133# Mock does not create attributes on an instance class. Instead a MockProperty 134# is created on a base class that contains enough metadata to find a value of 135# a property in class instance's buffer. 136# 137# To achieve this a MockProperty implements Python's descriptor protocol and 138# overrides __get__/__set__ methods. Every access results in data being 139# serialized or deserialized from the instance's buffer. 140# 141# +-----------------+ +-----------------+ 142# | Mock base class | | Mock instance | 143# +-----------------+ +-----------------+ 144# | | | Buffer | 145# | | | | 146# | |<--------------------| | 147# | | | | 148# +-----------------+ | +-------+ | 149# | MockProperty | | +-------+ | 150# +-----------------+ | | | 151# | | | | | | 152# +--------|--------+ +--------|--------+ 153# +---------------------------------------+ 154# 155# 156# It is allowed to create overlaping properties. This helps in solving support 157# for union types. 158 159class MockProperty: 160 """ Serializable property on the mock object. 161 162 A property maintains size/offset based on DWARF so it knows where to 163 serialize its own data inside an owner's buffer. 164 """ 165 166 def __init__(self, offs, sz, serializer=NoSerializer()): 167 """ Create property with given ofset, size and serializer. """ 168 169 self._offs = offs 170 self._sz = sz 171 self._attrname = None 172 self.serializer = serializer 173 174 def __set__(self, instance, value): 175 """ Updates shadow attribute on target's instance. """ 176 177 # Enforce that value is serializable. 178 if not self.serializer.accepts(value): 179 raise AttributeError("Unsupported value for this property") 180 181 # Serialize value to instance's buffer 182 data = self.serializer.serialize(value, self._sz) 183 instance._buf[self._offs: self._offs + min(self._sz, len(data))] = data 184 185 def __get__(self, instance, owner = None): 186 """ Retruns value from the shadow attribute on an instance. """ 187 188 return self.serializer.deserialize(instance._buf, self._offs, self._sz) 189 190 def __delete__(self, instance): 191 """ Deletes property. """ 192 193 # It is not possible to delete a type's member dynamically. 194 # The property mirros that behavior. 195 raise AttributeError("MockProperty instances can't be deleted.") 196 197 def __set_name__(self, owner, name): 198 """ Registers owning class property name. """ 199 200 self._attrname = f'_{name}' 201 202 def deserialize(self, data: bytes) -> typing.Any: 203 """ De-serializes value of a property from data. """ 204 205 return self.serializer.deserialize(data, self._offs, self._sz) 206 207 208class BitArray: 209 """ Simple wrapper around bytearray that provites bit access. 210 211 Note: The implementation is limited to mock requirements. 212 Not suitable for general purpose use. 213 """ 214 215 def __init__(self, bytes): 216 self._buf = bytes 217 218 def __getbit(self, idx): 219 byte = self._buf[idx // 8] 220 return byte & (1 << (idx % 8)) != 0 221 222 def __setbit(self, idx, value): 223 if value: 224 self._buf[idx // 8] |= (1 << idx % 8) 225 else: 226 self._buf[idx // 8] &= ~(1 << idx % 8) 227 228 def __getitem__(self, key): 229 if isinstance(key, slice): 230 return [self[ii] for ii in range(*key.indices(len(self)))] 231 232 return self.__getbit(key) 233 234 def __setitem__(self, key, value): 235 if isinstance(key, slice): 236 raise NotImplementedError("Not donbe yet") 237 238 self.__setbit(key, value) 239 240 def __len__(self): 241 return len(self._buf) * 8 242 243 def __str__(self): 244 s = "b" 245 for byte in self._buf: 246 for bit in range(0, 7): 247 s += "1" if byte & (1 << bit) else "0" 248 249 return s 250 251 252class BitfieldProperty(MockProperty): 253 """ Similar to MockProperty but all operations are in bits. 254 255 This type of property is used exclusively to implement bitfields. 256 """ 257 258 def __init__(self, offs, sz, serializer=NoSerializer()): 259 # Enforce NumericSerializer for bitfields. 260 super().__init__(offs, sz, NumericSerializer()) 261 262 self._bsz = (sz + 8) >> 3 263 264 def __set__(self, instance, value): 265 """ Updates shadow attribute on target's instance. """ 266 267 # Enforce that value is serializable. 268 if not self.serializer.accepts(value): 269 raise AttributeError("Unsupported value for this property") 270 271 # Serialize value to instance's buffer 272 data = self.serializer.serialize(value, self._bsz) 273 barr = BitArray(instance._buf) 274 275 offs = 0 276 for b in data: 277 for i in range(0, 8): 278 barr[self._offs + offs] = b & (1 << i) != 0 279 280 if offs == self._sz - 1: 281 break 282 283 offs += 1 284 285 def __get__(self, instance, owner = None): 286 """ Retruns value from the shadow attribute on an instance. """ 287 288 barrb = BitArray(instance._buf) 289 ba = bytearray(self._bsz) 290 barr = BitArray(ba) 291 292 newoffs = 0 293 for offs in range(self._offs, self._offs + self._sz): 294 barr[newoffs] = barrb[offs] 295 newoffs += 1 296 297 return self.serializer.deserialize(ba, 0, self._bsz) 298 299# 300# Proxied properties 301# 302# Proxy properties are used to expose members of anonymous structures or unions 303# at the top-level instance. ProxyProperty implements descriptor protocol like 304# MockProperty. Instead of keeping metadata about buffer location it delegates 305# all opertions to the proxy target. A proxy target's instance is stored in 306# mock instance attribute. 307# 308# +-----------------+ +-----------------+ 309# | Mock base class | | Mock instance | 310# +-----------------+ +-----------------+ 311# | | | Buffer | 312# | | | | 313# | |<--------------------| | 314# | | | | 315# +-----------------+ | | 316# | ProxyProperty |----------+ | | 317# +-----------------+ | +-----------------+ 318# | | +--------->| ProxyDesination | 319# +-----------------+ +-----------------+ 320# | 321# +------------------------------------------------------+ 322# | 323# | +-----------------+ +-------------------+ 324# | | Sub-mock base | | Sub-mock instance | 325# | | class | +-------------------+ 326# | +-----------------+ | | 327# | | | | | 328# | | |<--------------------| | 329# | | | | | 330# | +-----------------+ | +-------+ | 331# +---->| MockProperty | | +-------+ | 332# +-----------------+ | | | 333# | | | | | | 334# +--------|--------+ +---------|---------+ 335# +----------------------------------------+ 336# 337 338 339class ProxyProperty: 340 """ Proxies requests to a property on an unrelated class instance. 341 342 This property alows to expose anon struct/union members in a top 343 level mock class (Similar to what compilers are doing). 344 """ 345 346 def __init__(self): 347 """ Initializes unound proxy property. """ 348 349 self._attrproxy = None 350 self._attrname = None 351 352 def __set__(self, instance, value): 353 """ Forwards set operation to proxy. """ 354 355 proxy = getattr(instance, self._attrproxy) 356 setattr(proxy, self._attrname, value) 357 358 def __get__(self, instance, owner = None): 359 """ Forwards get operation to proxy. """ 360 361 proxy = getattr(instance, self._attrproxy) 362 return getattr(proxy, self._attrname) 363 364 def __delete__(self, instance): 365 """ Deletes property. """ 366 367 raise AttributeError("ProxyProperty instances can't be deleted.") 368 369 def __set_name__(self, owner, name): 370 """ Registers owning class property name. """ 371 372 self._attrname = name 373 self._attrproxy = f'_$proxy_{name}' 374 375 def setProxy(self, instance, proxy): 376 """ Bind this descriptor to proxy target. """ 377 378 setattr(instance, self._attrproxy, proxy) 379 380 381# 382# Base mock classes 383# 384# Every mock class is derived from BaseMock class which provides common 385# behavior / logic. 386# 387# One of the goals is to detect broken references between test/macro and kernel 388# structures. It is required to disallow creation of new attributes on an instance 389# by mock client. For that reason a mock instance can be finalized (frozen). 390# This prevents user from accidentally creating members that does not exist in 391# original structure. 392# 393 394 395class BaseMock(ABC): 396 """ Abstract base class serving as a base of every scripted process mock. """ 397 398 __frozen = False 399 400 def __init__(self, size: int): 401 """ Init mock with given offset and size. """ 402 403 self._size = size 404 self.log = lldb.test_logger.getChild(self.__class__.__name__) 405 406 407 def __setattr__(self, key: str, value: typing.Any) -> None: 408 """ Sets new attribute value or creates one (if not frozen). """ 409 410 # Raise exception if new attribute is being created on frozen mock. 411 if self.__frozen and not hasattr(self, key): 412 self.log.debug("Frozen mock missing attribute %s", key) 413 raise TypeError(f"Can't add {key} as {self} is a frozen") 414 415 return super().__setattr__(key, value) 416 417 def freeze(self): 418 """ Freeze the mock so no additional attributes can be created. """ 419 420 self.__frozen = True 421 422 # Please be conservative when adding methods / attributes here. 423 # Each such method may conflict with members a mock sub-class may create. 424 425 @property 426 def size(self): 427 """ Size in bytes of this mock. """ 428 429 return self._size 430 431 def fromDict(self, members): 432 """ Initialize mock members from dictionary. """ 433 434 for k, v in members.items(): 435 if isinstance(v, dict): 436 getattr(self, k).fromDict(v) 437 438 setattr(self, k, v) 439 440 return self 441 442 @abstractmethod 443 def getData(self): 444 """ Returns byte representation of the mock. """ 445 446 @abstractmethod 447 def setData(self, data): 448 """ Restores mock attributes from bytes. """ 449 450 # Take care when implementing this method as sub-mocks may reference 451 # existing data instance. Replacing underlying buffer with new one 452 # may result in data no longer being shared with sub-mocks. 453 454 455class RawMock(BaseMock): 456 """ Simple mock that wraps raw data that are going to be placed in memory. 457 458 This mock does not have any attributes. It is possible to provide a 459 serializer to allow converstion from types like string. 460 """ 461 462 def __init__(self, size: int, serializer=NoSerializer()): 463 """ A mock that holds raw bytes for given offset/size range. """ 464 465 super().__init__(size) 466 self._data = serializer.default() 467 self.serializer = serializer 468 self.freeze() 469 470 def getData(self): 471 """ Returns memory view based on the data in this mock. """ 472 473 return memoryview(self._data) 474 475 def setData(self, data): 476 """ Sets value of the mock. """ 477 478 if self.serializer: 479 self._data = self.serializer.serialize(data, self.size) 480 else: 481 self._data = data 482 483 def fromDict(self, members): 484 """ Not supported by raw memory mock """ 485 raise NotImplementedError("RawMock can't be populated from dict.") 486 487 @staticmethod 488 def fromBufferedIO(fromIO: io.BufferedIOBase) -> 'RawMock': 489 """ Populate mock data from I/O intance. """ 490 491 data = fromIO.read() 492 mock = RawMock(len(data)) 493 mock.setData(data) 494 495 return mock 496 497class ArrayMock(BaseMock): 498 """ Inserts array of mocks into target's memory. 499 500 High-level wrapper that constructs array of mocks of given type. 501 All mocks share same underlying buffer. 502 """ 503 504 def __init__(self, sbtype: lldb.SBType, parentBuf=None): 505 """ """ 506 super().__init__(sbtype.GetByteSize()) 507 508 # Top level array mock will allocate buffer. Otherwise it will distribute 509 # sub-mocks across parent's buffer. 510 if parentBuf: 511 self._data = memoryview(parentBuf)[:self._size] 512 else: 513 self._data = bytearray(self._size) 514 515 elem_sbtype = sbtype.GetArrayElementType() 516 self._count = self._size // elem_sbtype.GetByteSize() 517 518 self._arrmocks = [] 519 offs = 0 520 for _ in range(self._count): 521 submock = MockFactory.createFromType(elem_sbtype, 0, 522 memoryview(self._data)[offs: offs + sbtype.GetByteSize()]) 523 self._arrmocks.append(submock) 524 offs += elem_sbtype.GetByteSize() 525 526 def getData(self): 527 return memoryview(self._data) 528 529 def setData(self, data): 530 self._data[0: len(data)] = data 531 532 def fromDict(self, members): 533 for k, v in members.items(): 534 idx = int(k) 535 self._arrmocks[idx].fromDict(v) 536 537 return self 538 539 def __getitem__(self, key): 540 """ Returns sub-mock at given index. """ 541 if isinstance(key, slice): 542 return [self[ii] for ii in range(*key.indices(len(self)))] 543 544 return self._arrmocks[key] 545 546 547class MemoryMock(BaseMock): 548 """ Inserts serialized MemoryMock directly into target's memory. """ 549 550 def __init__(self, sbtype: lldb.SBType, buf): 551 super().__init__(sbtype.GetByteSize()) 552 self._sbtype = sbtype 553 self._anon_mocks = [] 554 self._buf = buf 555 556 def setData(self, data: bytes): 557 """ Set underlying buffer and reconstruct mock values. """ 558 self.log.debug("setData refereshing mocks") 559 560 # Setting data on a sub-mock is not allowed. 561 # Sub-mocks are always using memoryviews. 562 if isinstance(self._buf, memoryview): 563 raise AttributeError("Can't set data on a sub-mock.") 564 565 self._buf[0: len(data)] = data 566 567 def getData(self): 568 """ Return memory view of mock's data buffer. """ 569 570 return memoryview(self._buf) 571 572 573# 574# Mock factories 575# 576# Abstracts away mock creation from the actual mock instances. Most of the 577# factories are singletons. 578# 579 580class MockFactory(ABC): 581 """ Abstract base class factory. 582 """ 583 584 def __init__(self): 585 """ Initialize factory. """ 586 self.log = lldb.test_logger.getChild(self.__class__.__name__) 587 588 @abstractmethod 589 def create_mock(self, sbtype: lldb.SBType, offs: int = 0, parent_buf = None): 590 """ Constructs concrete mock class instance for given SBType. """ 591 592 @staticmethod 593 def createFromType(mocktype: typing.Union[str, lldb.SBType], 594 offs: int = 0, parent_buf = None) -> 'MemoryMock': 595 """ Top-level factory method available to users. """ 596 597 # Lookup type to be created 598 sbtype = lookup_type(mocktype) 599 if not sbtype or not sbtype.IsValid(): 600 raise AttributeError("Unknown type") 601 602 # Resolve typedefs to canconical type (to avoid typedefs) 603 sbtype = sbtype.GetCanonicalType() 604 605 # Select factory based on type. 606 factory = SimpleTypeFactory() 607 type_class = sbtype.GetTypeClass() 608 609 # Structures/Unions 610 if type_class == lldb.eTypeClassStruct or type_class == lldb.eTypeClassUnion: 611 factory = CompoundTypeFactory() 612 613 # Arrays 614 if type_class == lldb.eTypeClassArray: 615 factory = ArrayTypeFactory() 616 617 # Use factory's method to create mock class instance. 618 return factory.create_mock(sbtype, offs, parent_buf) 619 620 621class SimpleTypeFactory(MockFactory, metaclass=Singleton): 622 """ Constructs mocking class for a simple types. """ 623 624 def create_mock(self, sbtype: lldb.SBType, offs: int = 0, parent_buf = None): 625 """ Create RawMock instance for simple types.""" 626 627 self.log.debug("Creating mock for %s", sbtype.GetName()) 628 629 # Simple type does not have any members. 630 if sbtype.GetNumberOfFields() > 0: 631 raise AttributeError("Not a simple type") 632 633 # Return new mock instance and freeze it. 634 mock = RawMock(size=sbtype.GetByteSize(), 635 serializer=Serializer.createSerializerForType(sbtype)) 636 mock.freeze() 637 return mock 638 639 640class ArrayTypeFactory(MockFactory, metaclass=Singleton): 641 """ Constructs mocking class for array types. """ 642 643 def create_mock(self, sbtype: lldb.SBType, offs: int = 0, parent_buf = None): 644 """ Delegate array type creation and construct an array. """ 645 646 self.log.debug("Creating mock for %s", sbtype.GetName()) 647 mock = ArrayMock(sbtype) 648 mock.freeze() 649 return mock 650 651 652# 653# Compound type mocks 654# 655# A compound mock tries to convert SBType to a native Python class. Such conversion 656# happens in three phases: 657# 658# 1. SBType gets converted to new Python class defintion with MockProperties. 659# 2. Mock is created as an instance of the new class. 660# 3. Postprocessing resolves binding / nesting. 661# 4. Mocks are frozen and returned to a client. 662# 663# It may be required to repeat steps above multiple times to recrete whole 664# type hierarchy. The top level mock is byte array provider for the whole 665# hierarchy of sub-mocks. This avoids the need for complex sync between class 666# attributes and final byte array holding serialized copy of the type value. 667# At the same time it makes handling of unions easier because it is possible 668# to simply create overlaping sub-mocks that share same parent's buffer area. 669# 670# Example - Simple type: 671# 672# struct node { 673# uint64_t memA; 674# uint64_t memB; 675# } 676# 677# First a Mock_node class is created by factory. This class gets two properties 678# - MockProperty<int> memA which spans indexes (0 .. 7) 679# - MockProperty<int> memB which spans indexes (8 .. 15) 680# 681# An instance of the Mock_node class is created which will hold the buffer 682# Any operation on a MockProperty will result in serializer/deserializer being 683# invoked directly on the associated buffer area. 684# 685# Example - Nested structures: 686# 687# struct node_t { 688# struct { 689# uint64_t member; 690# } sub_node; 691# } 692# 693# Nested structures are handled by creation of sub-mock for given type 694# of a compound member. The only difference is that sub-mock instance will not 695# have its own buffer. Instead it is given view of parent's buffer arrea covered 696# by the sub-mock. This way all changes are propagated to the parent and all 697# offsets within sub-mock can be applied directly as the view itself is offset 698# from the start of buffer. 699# 700# Example - Anonymous members 701# 702# struct node_t { 703# struct { 704# uint64_t member; 705# } 706# } 707# 708# This is more complex example because there is no way how to reference anon 709# type in the structure. However C compiler allows accessing such members by 710# thier names. 711# 712# CompoundMock supports anonymous members in a following way: 713# 714# 1. A nested sub-mock is created like in example above. 715# 3. A ProxyProperty is added to top level mock (unbound) for every anon 716# member's child. 717# 2. Reference is kept in parent's _anon_mocks (becasue there is no member) 718# 4. Sub-mocks are instantiated 719# 5. Proxy lookup will find sub-mock instances providing a member and 720# bind top-level proxies to it. 721# 722# See ProxyProperty comment above for illustration. 723# 724# This way a top-level mock delegates property handling to a submock. Submock 725# then propagates all changes back to top-level mock's buffer. 726# 727 728 729class CompoundTypeFactory(MockFactory, metaclass=Singleton): 730 """ Constructs mock class and instance for non-trivial types. """ 731 732 cache = {} 733 734 def _make_property(self, member: lldb.SBTypeMember): 735 """ Creates correct property type based on type member. """ 736 737 if member.IsBitfield(): 738 self.log.debug("Creating BitfieldProperty for %s", member.GetName()) 739 return BitfieldProperty( 740 member.GetOffsetInBits(), 741 member.GetBitfieldSizeInBits(), 742 Serializer.createSerializerForType(member.GetType())) 743 744 self.log.debug("Creating MockProperty for %s", member.GetName()) 745 return MockProperty( 746 member.GetOffsetInBytes(), 747 member.GetType().GetByteSize(), 748 Serializer.createSerializerForType(member.GetType())) 749 750 def _createMockClass(self, sbtype: lldb.SBType, offs: int = 0): 751 """ Converts SBType into a new mock base class. """ 752 753 self.log.debug("Creating mock class for %s", sbtype.GetName()) 754 755 # Try cache first 756 clsname = f'MockBase_{sbtype.GetName()}_{offs}' 757 cls = self.cache.get(clsname, None) 758 if cls is not None: 759 self.log.debug("Found in cache %s", clsname) 760 return cls 761 762 attrs = {} 763 764 # Add all direct members as MockProperty. 765 for t in sbtype.get_members_array(): 766 767 # Create MockProperty for members with simple data types. 768 if t.GetType().GetNumberOfFields() == 0: 769 attrs[t.GetName()] = self._make_property(t) 770 771 772 # Skip creation of proxied properties for sub-mocks. There is no need 773 # to build a chain. Top-level mock will bind directly to desired 774 # sub-mock. 775 if offs != 0: 776 continue 777 778 # Create ProxyProperty for anonymous sub-members so we can later 779 # establish the forwarding to the mock that overlays this area. 780 if t.GetType().IsAnonymousType(): 781 782 def visit_anon(sbtype): 783 for at in sbtype.get_members_array(): 784 if at.GetType().IsAnonymousType(): 785 yield from visit_anon(at.GetType()) 786 else: 787 yield at 788 789 for at in visit_anon(t.GetType()): 790 self.log.debug("Adding anon proxy for %s", at.GetName()) 791 attrs[at.GetName()] = ProxyProperty() 792 793 # Compound types are not handled at mock class level. They are 794 # going to be added to the class instance. 795 796 # Insert class into cache. anon types are ignored as there may be 797 # name conflicts. 798 self.log.debug("Created new mock class %s", clsname) 799 800 clsname = f'MockBase_{sbtype.GetName()}_{offs}' 801 cls = type(clsname, (MemoryMock,), attrs) 802 803 # No caching of anon types 804 if sbtype.IsAnonymousType(): 805 self.log.debug("Anon type not caching") 806 return cls 807 808 if clsname in self.cache: 809 raise AttributeError("Already in cache !!") 810 811 self.cache[clsname] = cls 812 return cls 813 814 def create_mock(self, sbtype: lldb.SBType, offs: int = 0, parent_buf = None): 815 """ Creates mock instance of mock class created from SBType. """ 816 817 if sbtype.GetNumberOfFields() == 0: 818 raise AttributeError("Not a compound type") 819 820 # Top level mocks are buffer providers 821 if not parent_buf: 822 buf = bytearray(sbtype.GetByteSize()) 823 self.log.debug("%s size is %d", sbtype.GetName(), sbtype.GetByteSize()) 824 else: 825 buf = memoryview(parent_buf)[offs: offs + sbtype.GetByteSize()] 826 827 # Construct new mock base class. 828 mock_class = self._createMockClass(sbtype, offs) 829 mock_inst = mock_class(sbtype, buf) 830 831 # Pre-populate all properties with default values. 832 for prop, value in mock_class.__dict__.items(): 833 if isinstance(value, MockProperty): 834 self.log.debug(f'Setting default for {prop}') 835 setattr(mock_inst, prop, value.serializer.default()) 836 837 # Instantiate regular sub-mocks 838 for t in sbtype.get_members_array(): 839 if t.GetType().GetNumberOfFields() == 0: 840 continue 841 842 # Create sub-mocks for regular structures/unions 843 submock = MockFactory.createFromType(t.GetType(), t.GetOffsetInBytes(), buf) 844 if not t.GetType().IsAnonymousType(): 845 self.log.debug("Creating anon sub-mock %s at %d", 846 t.GetName(), t.GetOffsetInBytes()) 847 setattr(mock_inst, t.GetName(), submock) 848 continue 849 850 # Create and connect instances 851 mock_inst._anon_mocks.append(submock) 852 853 # Resolve proxies 854 resolve = { 855 k:p for (k, p) 856 in mock_class.__dict__.items() 857 if isinstance(p, ProxyProperty) 858 } 859 860 # Assumption is there are no conflicts (compiler would refuse to build the code) 861 862 def visit_anon_mocks(mock): 863 for m in mock._anon_mocks: 864 yield from m._anon_mocks 865 yield m 866 867 for am in visit_anon_mocks(mock_inst): 868 for t in am._sbtype.get_members_array(): 869 if t.GetName() in resolve: 870 resolve[t.GetName()].setProxy(mock_inst, am) 871 self.log.debug("Resolving %s", t.GetName()) 872 del resolve[t.GetName()] 873 874 # Fail if there are unresolved members. 875 if resolve: 876 raise TypeError('Unresolved proxies in a mock class') 877 878 # Construct mock instance 879 mock_inst.freeze() 880 return mock_inst 881