Improve docstrings for all plugins, and reformat all docstrings.

This commit is contained in:
Mike Auty
2019-09-07 22:59:54 +01:00
parent dc0a809729
commit e922cef316
134 changed files with 1933 additions and 1250 deletions
+89 -66
View File
@@ -19,7 +19,7 @@ DataFormatInfo = collections.namedtuple('DataFormatInfo', ['length', 'byteorder'
def convert_data_to_value(data: bytes, struct_type: Type[TUnion[int, float, bytes, str, bool]],
data_format: DataFormatInfo) -> TUnion[int, float, bytes, str, bool]:
"""Converts a series of bytes to a particular type of value"""
"""Converts a series of bytes to a particular type of value."""
if struct_type == int:
return int.from_bytes(data, byteorder = data_format.byteorder, signed = data_format.signed)
if struct_type == bool:
@@ -40,7 +40,7 @@ def convert_data_to_value(data: bytes, struct_type: Type[TUnion[int, float, byte
def convert_value_to_data(value: TUnion[int, float, bytes, str, bool],
struct_type: Type[TUnion[int, float, bytes, str, bool]],
data_format: DataFormatInfo) -> bytes:
"""Converts a particular value to a series of bytes"""
"""Converts a particular value to a series of bytes."""
if not isinstance(value, struct_type):
raise TypeError("Written value is not of the correct type for {}".format(struct_type.__class__.__name__))
@@ -64,17 +64,17 @@ def convert_value_to_data(value: TUnion[int, float, bytes, str, bool],
class Void(interfaces.objects.ObjectInterface):
"""Returns an object to represent void/unknown types"""
"""Returns an object to represent void/unknown types."""
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
@classmethod
def size(cls, template: interfaces.objects.Template) -> int:
"""Dummy size for Void objects"""
"""Dummy size for Void objects."""
raise TypeError("Void types are incomplete, cannot contain data and do not have a size")
def write(self, value: Any) -> None:
"""Dummy method that does nothing for Void objects"""
"""Dummy method that does nothing for Void objects."""
raise TypeError("Cannot write data to a void, recast as another object")
@@ -83,7 +83,8 @@ class Function(interfaces.objects.ObjectInterface):
class PrimitiveObject(interfaces.objects.ObjectInterface):
"""PrimitiveObject is an interface for any objects that should simulate a Python primitive"""
"""PrimitiveObject is an interface for any objects that should simulate a
Python primitive."""
_struct_type = int # type: ClassVar[Type]
def __init__(self, context: interfaces.context.ContextInterface, type_name: str,
@@ -98,13 +99,15 @@ class PrimitiveObject(interfaces.objects.ObjectInterface):
data_format: DataFormatInfo,
new_value: TUnion[int, float, bool, bytes, str] = None,
**kwargs) -> 'PrimitiveObject':
"""Creates the appropriate class and returns it so that the native type is inherited
"""Creates the appropriate class and returns it so that the native type
is inherited.
The only reason the **kwargs is added, is so that the inherriting types can override __init__
without needing to override __new__
We also sneak in new_value, so that we don't have to do expensive (read: impossible) context reads
when unpickling."""
when unpickling.
"""
if new_value is None:
value = cls._unmarshall(context, data_format, object_info)
else:
@@ -116,7 +119,8 @@ class PrimitiveObject(interfaces.objects.ObjectInterface):
return result
def __getnewargs_ex__(self):
"""Make sure that when pickling, all appropiate parameters for new are provided"""
"""Make sure that when pickling, all appropiate parameters for new are
provided."""
kwargs = {}
for k, v in self._vol.maps[-1].items():
if k not in ["context", "data_format", "object_info", "type_name"]:
@@ -134,36 +138,37 @@ class PrimitiveObject(interfaces.objects.ObjectInterface):
@classmethod
def size(cls, template: interfaces.objects.Template) -> int:
"""Returns the size of the templated object"""
"""Returns the size of the templated object."""
return template.vol.data_format.length
def write(self, value: TUnion[int, float, bool, bytes, str]) -> None:
"""Writes the object into the layer of the context at the current offset"""
"""Writes the object into the layer of the context at the current
offset."""
data = convert_value_to_data(value, self._struct_type, self._data_format)
return self._context.layers.write(self.vol.layer_name, self.vol.offset, data)
class Boolean(PrimitiveObject, int):
"""Primitive Object that handles boolean types"""
"""Primitive Object that handles boolean types."""
_struct_type = bool # type: ClassVar[Type]
class Integer(PrimitiveObject, int):
"""Primitive Object that handles standard numeric types"""
"""Primitive Object that handles standard numeric types."""
class Float(PrimitiveObject, float):
"""Primitive Object that handles double or floating point numbers"""
"""Primitive Object that handles double or floating point numbers."""
_struct_type = float # type: ClassVar[Type]
class Char(PrimitiveObject, int):
"""Primitive Object that handles characters"""
"""Primitive Object that handles characters."""
_struct_type = int # type: ClassVar[Type]
class Bytes(PrimitiveObject, bytes):
"""Primitive Object that handles specific series of bytes"""
"""Primitive Object that handles specific series of bytes."""
_struct_type = bytes # type: ClassVar[Type]
def __init__(self,
@@ -184,22 +189,24 @@ class Bytes(PrimitiveObject, bytes):
object_info: interfaces.objects.ObjectInformation,
length: int = 1,
**kwargs) -> 'Bytes':
"""Creates the appropriate class and returns it so that the native type is inherritted
"""Creates the appropriate class and returns it so that the native type
is inherritted.
The only reason the **kwargs is added, is so that the inherriting types can override __init__
without needing to override __new__"""
The only reason the **kwargs is added, is so that the
inherriting types can override __init__ without needing to
override __new__
"""
return cls._struct_type.__new__(
cls,
cls._unmarshall(context, data_format = DataFormatInfo(length, "big", False), object_info = object_info))
class String(PrimitiveObject, str):
"""Primitive Object that handles string values
"""Primitive Object that handles string values.
Args:
max_length: specifies the maximum possible length that the string could hold within memory
(for multibyte characters, this will not be the maximum length of the string)
"""
_struct_type = str # type: ClassVar[Type]
@@ -227,10 +234,13 @@ class String(PrimitiveObject, str):
encoding: str = "utf-8",
errors: str = "strict",
**kwargs) -> 'String':
"""Creates the appropriate class and returns it so that the native type is inherited
"""Creates the appropriate class and returns it so that the native type
is inherited.
The only reason the **kwargs is added, is so that the inherriting types can override __init__
without needing to override __new__"""
The only reason the **kwargs is added, is so that the
inherriting types can override __init__ without needing to
override __new__
"""
params = {}
if encoding:
params['encoding'] = encoding
@@ -247,7 +257,7 @@ class String(PrimitiveObject, str):
class Pointer(Integer):
"""Pointer which points to another object"""
"""Pointer which points to another object."""
def __init__(self,
context: interfaces.context.ContextInterface,
@@ -261,10 +271,12 @@ class Pointer(Integer):
@classmethod
def _unmarshall(cls, context: interfaces.context.ContextInterface, data_format: DataFormatInfo,
object_info: ObjectInformation) -> Any:
"""Ensure that pointer values always fall within the domain of the layer they're constructed on
"""Ensure that pointer values always fall within the domain of the
layer they're constructed on.
If there's a need for all the data within the address, the pointer should be recast. The "pointer"
must always live within the space (even if the data provided is invalid).
If there's a need for all the data within the address, the
pointer should be recast. The "pointer" must always live within
the space (even if the data provided is invalid).
"""
length, endian, signed = data_format
if signed:
@@ -275,10 +287,12 @@ class Pointer(Integer):
return value & mask
def dereference(self, layer_name: Optional[str] = None) -> interfaces.objects.ObjectInterface:
"""Dereferences the pointer
"""Dereferences the pointer.
Layer_name is identifies the appropriate layer within the context that the pointer points to.
If layer_name is None, it defaults to the same layer that the pointer is currently instantiated in.
Layer_name is identifies the appropriate layer within the
context that the pointer points to. If layer_name is None, it
defaults to the same layer that the pointer is currently
instantiated in.
"""
layer_name = layer_name or self.vol.native_layer_name
mask = self._context.layers[layer_name].address_mask
@@ -288,16 +302,18 @@ class Pointer(Integer):
object_info = interfaces.objects.ObjectInformation(layer_name = layer_name, offset = offset, parent = self))
def is_readable(self, layer_name: Optional[str] = None) -> bool:
"""Determines whether the address of this pointer can be read from memory"""
"""Determines whether the address of this pointer can be read from
memory."""
layer_name = layer_name or self.vol.layer_name
return self._context.layers[layer_name].is_valid(self)
def __getattr__(self, attr: str) -> Any:
"""Convenience function to access unknown attributes by getting them from the subtype object"""
"""Convenience function to access unknown attributes by getting them
from the subtype object."""
return getattr(self.dereference(), attr)
def has_member(self, member_name: str) -> bool:
"""Returns whether the dereferenced type has this member"""
"""Returns whether the dereferenced type has this member."""
return self._vol['subtype'].has_member(member_name)
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
@@ -308,7 +324,7 @@ class Pointer(Integer):
@classmethod
def children(cls, template: interfaces.objects.Template) -> List[interfaces.objects.Template]:
"""Returns the children of the template"""
"""Returns the children of the template."""
if 'subtype' in template.vol:
return [template.vol.subtype]
return []
@@ -316,7 +332,7 @@ class Pointer(Integer):
@classmethod
def replace_child(cls, template: interfaces.objects.Template, old_child: interfaces.objects.Template,
new_child: interfaces.objects.Template) -> None:
"""Substitutes the old_child for the new_child"""
"""Substitutes the old_child for the new_child."""
if 'subtype' in template.vol:
if template.vol.subtype == old_child:
template.update_vol(subtype = new_child)
@@ -327,7 +343,8 @@ class Pointer(Integer):
class BitField(interfaces.objects.ObjectInterface, int):
"""Object containing a field which is made up of bits rather than whole bytes"""
"""Object containing a field which is made up of bits rather than whole
bytes."""
def __init__(self,
context: interfaces.context.ContextInterface,
@@ -363,7 +380,7 @@ class BitField(interfaces.objects.ObjectInterface, int):
@classmethod
def children(cls, template: interfaces.objects.Template) -> List[interfaces.objects.Template]:
"""Returns the children of the template"""
"""Returns the children of the template."""
if 'base_type' in template.vol:
return [template.vol.base_type]
return []
@@ -371,14 +388,14 @@ class BitField(interfaces.objects.ObjectInterface, int):
@classmethod
def replace_child(cls, template: interfaces.objects.Template, old_child: interfaces.objects.Template,
new_child: interfaces.objects.Template) -> None:
"""Substitutes the old_child for the new_child"""
"""Substitutes the old_child for the new_child."""
if 'base_type' in template.vol:
if template.vol.base_type == old_child:
template.update_vol(base_type = new_child)
class Enumeration(interfaces.objects.ObjectInterface, int):
"""Returns an object made up of choices"""
"""Returns an object made up of choices."""
def __new__(cls, context: interfaces.context.ContextInterface, type_name: str,
object_info: interfaces.objects.ObjectInformation, base_type: interfaces.objects.Template,
@@ -397,7 +414,7 @@ class Enumeration(interfaces.objects.ObjectInterface, int):
@classmethod
def _generate_inverse_choices(cls, choices: Dict[str, int]) -> Dict[int, str]:
"""Generates the inverse choices for the object"""
"""Generates the inverse choices for the object."""
inverse_choices = {} # type: Dict[int, str]
for k, v in choices.items():
if v in inverse_choices:
@@ -409,7 +426,7 @@ class Enumeration(interfaces.objects.ObjectInterface, int):
return inverse_choices
def lookup(self, value: int = None) -> str:
"""Looks up an individual value and returns the associated name"""
"""Looks up an individual value and returns the associated name."""
if value is None:
return self.lookup(self)
if value in self._inverse_choices:
@@ -418,7 +435,7 @@ class Enumeration(interfaces.objects.ObjectInterface, int):
@property
def description(self) -> str:
"""Returns the chosen name for the value this object contains"""
"""Returns the chosen name for the value this object contains."""
return self.lookup(self)
@property
@@ -426,7 +443,7 @@ class Enumeration(interfaces.objects.ObjectInterface, int):
return self._vol['choices']
def __getattr__(self, attr: str) -> str:
"""Returns the value for a specific name"""
"""Returns the value for a specific name."""
if attr in self._vol['choices']:
return self._vol['choices'][attr]
raise AttributeError("Unknown attribute {} for Enumeration {}".format(attr, self._vol['type_name']))
@@ -439,7 +456,7 @@ class Enumeration(interfaces.objects.ObjectInterface, int):
@classmethod
def lookup(cls, template: interfaces.objects.Template, value: int) -> str:
"""Looks up an individual value and returns the associated name"""
"""Looks up an individual value and returns the associated name."""
_inverse_choices = Enumeration._generate_inverse_choices(template.vol['choices'])
if value in _inverse_choices:
return _inverse_choices[value]
@@ -451,7 +468,7 @@ class Enumeration(interfaces.objects.ObjectInterface, int):
@classmethod
def children(cls, template: interfaces.objects.Template) -> List[interfaces.objects.Template]:
"""Returns the children of the template"""
"""Returns the children of the template."""
if 'base_type' in template.vol:
return [template.vol.base_type]
return []
@@ -459,14 +476,14 @@ class Enumeration(interfaces.objects.ObjectInterface, int):
@classmethod
def replace_child(cls, template: interfaces.objects.Template, old_child: interfaces.objects.Template,
new_child: interfaces.objects.Template) -> None:
"""Substitutes the old_child for the new_child"""
"""Substitutes the old_child for the new_child."""
if 'base_type' in template.vol:
if template.vol.base_type == old_child:
template.update_vol(base_type = new_child)
class Array(interfaces.objects.ObjectInterface, abc.Sequence):
"""Object which can contain a fixed number of an object type"""
"""Object which can contain a fixed number of an object type."""
def __init__(self,
context: interfaces.context.ContextInterface,
@@ -482,26 +499,27 @@ class Array(interfaces.objects.ObjectInterface, abc.Sequence):
# Changing the name would be confusing (since we use count of an array everywhere else), so this is more important
@property
def count(self) -> int:
"""Returns the count dynamically"""
"""Returns the count dynamically."""
return self.vol.count
@count.setter
def count(self, value: int) -> None:
"""Sets the count to a specific value"""
"""Sets the count to a specific value."""
self._vol['count'] = value
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
@classmethod
def size(cls, template: interfaces.objects.Template) -> int:
"""Returns the size of the array, based on the count and the subtype"""
"""Returns the size of the array, based on the count and the
subtype."""
if 'subtype' not in template.vol and 'count' not in template.vol:
raise TypeError("Array ObjectTemplate must be provided a count and subtype")
return template.vol.get('subtype', None).size * template.vol.get('count', 0)
@classmethod
def children(cls, template: interfaces.objects.Template) -> List[interfaces.objects.Template]:
"""Returns the children of the template"""
"""Returns the children of the template."""
if 'subtype' in template.vol:
return [template.vol.subtype]
return []
@@ -509,14 +527,15 @@ class Array(interfaces.objects.ObjectInterface, abc.Sequence):
@classmethod
def replace_child(cls, template: interfaces.objects.Template, old_child: interfaces.objects.Template,
new_child: interfaces.objects.Template) -> None:
"""Substitutes the old_child for the new_child"""
"""Substitutes the old_child for the new_child."""
if 'subtype' in template.vol:
if template.vol['subtype'] == old_child:
template.update_vol(subtype = new_child)
@classmethod
def relative_child_offset(cls, template: interfaces.objects.Template, child: str) -> int:
"""Returns the relative offset from the head of the parent data to the child member"""
"""Returns the relative offset from the head of the parent data to
the child member."""
if 'subtype' in template.vol and child == 'subtype':
return 0
raise IndexError("Member not present in array template: {}".format(child))
@@ -530,7 +549,7 @@ class Array(interfaces.objects.ObjectInterface, abc.Sequence):
...
def __getitem__(self, i):
"""Returns the i-th item from the array"""
"""Returns the i-th item from the array."""
result = [] # type: List[interfaces.objects.Template]
mask = self._context.layers[self.vol.layer_name].address_mask
# We use the range function to deal with slices for us
@@ -551,7 +570,7 @@ class Array(interfaces.objects.ObjectInterface, abc.Sequence):
return result
def __len__(self) -> int:
"""Returns the length of the array"""
"""Returns the length of the array."""
return self.vol.count
def write(self, value) -> None:
@@ -559,9 +578,10 @@ class Array(interfaces.objects.ObjectInterface, abc.Sequence):
class AggregateType(interfaces.objects.ObjectInterface):
"""Object which can contain members that are other objects
"""Object which can contain members that are other objects.
Keep the number of methods in this class low or very specific, since each one could overload a valid member.
Keep the number of methods in this class low or very specific, since
each one could overload a valid member.
"""
def __init__(self, context: interfaces.context.ContextInterface, type_name: str,
@@ -573,27 +593,29 @@ class AggregateType(interfaces.objects.ObjectInterface):
self._concrete_members = {} # type: Dict[str, Dict]
def has_member(self, member_name: str) -> bool:
"""Returns whether the object would contain a member called member_name"""
"""Returns whether the object would contain a member called
member_name."""
return member_name in self.vol.members
class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy):
@classmethod
def size(cls, template: interfaces.objects.Template) -> int:
"""Method to return the size of this type"""
"""Method to return the size of this type."""
if template.vol.get('size', None) is None:
raise TypeError("ObjectTemplate not provided with a size")
return template.vol.size
@classmethod
def children(cls, template: interfaces.objects.Template) -> List[interfaces.objects.Template]:
"""Method to list children of a template"""
"""Method to list children of a template."""
return [member for _, member in template.vol.members.values()]
@classmethod
def replace_child(cls, template: interfaces.objects.Template, old_child: interfaces.objects.Template,
new_child: interfaces.objects.Template) -> None:
"""Replace a child elements within the arguments handed to the template"""
"""Replace a child elements within the arguments handed to the
template."""
for member in template.vol.members.get('members', {}):
relative_offset, member_template = template.vol.members[member]
if member_template == old_child:
@@ -608,7 +630,7 @@ class AggregateType(interfaces.objects.ObjectInterface):
@classmethod
def relative_child_offset(cls, template: interfaces.objects.Template, child: str) -> int:
"""Returns the relative offset of a child to its parent"""
"""Returns the relative offset of a child to its parent."""
retlist = template.vol.members.get(child, None)
if retlist is None:
raise IndexError("Member not present in template: {}".format(child))
@@ -616,7 +638,8 @@ class AggregateType(interfaces.objects.ObjectInterface):
@classmethod
def has_member(cls, template: interfaces.objects.Template, member_name: str) -> bool:
"""Returns whether the object would contain a member called member_name"""
"""Returns whether the object would contain a member called
member_name."""
return member_name in template.vol.members
@classmethod
@@ -640,7 +663,7 @@ class AggregateType(interfaces.objects.ObjectInterface):
return self.__getattr__(attr)
def __getattr__(self, attr: str) -> Any:
"""Method for accessing members of the type"""
"""Method for accessing members of the type."""
if attr in self._concrete_members:
return self._concrete_members[attr]
elif attr in self.vol.members:
@@ -664,7 +687,7 @@ class AggregateType(interfaces.objects.ObjectInterface):
raise AttributeError("{} has no attribute: {}.{}".format(agg_name, self.vol.type_name, attr))
def __dir__(self) -> Iterable[str]:
"""Returns a complete list of members when dir is called"""
"""Returns a complete list of members when dir is called."""
return list(super().__dir__()) + list(self.vol.members)
def write(self, value):
+25 -21
View File
@@ -11,14 +11,15 @@ vollog = logging.getLogger(__name__)
class ObjectTemplate(interfaces.objects.Template):
"""Factory class that produces objects that adhere to the Object interface on demand
"""Factory class that produces objects that adhere to the Object interface
on demand.
This is effectively a method of currying, but adds more structure to avoid abuse.
It also allows inspection of information that should already be known:
This is effectively a method of currying, but adds more structure to avoid abuse.
It also allows inspection of information that should already be known:
* Type size
* Members
* etc
* Type size
* Members
* etc
"""
def __init__(self, object_class: Type[interfaces.objects.ObjectInterface], type_name: str, **arguments) -> None:
@@ -31,35 +32,38 @@ class ObjectTemplate(interfaces.objects.Template):
@property
def size(self) -> int:
"""Returns the children of the templated object (see :class:`~volatility.framework.interfaces.objects.ObjectInterface.VolTemplateProxy`)"""
"""Returns the children of the templated object (see :class:`~volatilit
y.framework.interfaces.objects.ObjectInterface.VolTemplateProxy`)"""
return self.vol.object_class.VolTemplateProxy.size(self)
@property
def children(self) -> List[interfaces.objects.Template]:
"""Returns the children of the templated object (see :class:`~volatility.framework.interfaces.objects.ObjectInterface.VolTemplateProxy`)
"""
"""Returns the children of the templated object (see :class:`~volatilit
y.framework.interfaces.objects.ObjectInterface.VolTemplateProxy`)"""
return self.vol.object_class.VolTemplateProxy.children(self)
def relative_child_offset(self, child: str) -> int:
"""Returns the relative offset of a child of the templated object (see :class:`~volatility.framework.interfaces.objects.ObjectInterface.VolTemplateProxy`)
"""
"""Returns the relative offset of a child of the templated object (see
:class:`~volatility.framework.interfaces.objects.ObjectInterface.VolTem
plateProxy`)"""
return self.vol.object_class.VolTemplateProxy.relative_child_offset(self, child)
def replace_child(self, old_child: interfaces.objects.Template, new_child: interfaces.objects.Template) -> None:
"""Replaces `old_child` for `new_child` in the templated object's child list (see :class:`~volatility.framework.interfaces.objects.ObjectInterface.VolTemplateProxy`)
"""
"""Replaces `old_child` for `new_child` in the templated object's child
list (see :class:`~volatility.framework.interfaces.objects.ObjectInterf
ace.VolTemplateProxy`)"""
return self.vol.object_class.VolTemplateProxy.replace_child(self, old_child, new_child)
def has_member(self, member_name: str) -> bool:
"""Returns whether the object would contain a member called member_name
"""
"""Returns whether the object would contain a member called
member_name."""
return self.vol.object_class.VolTemplateProxy.has_member(self, member_name)
def __call__(self, context: interfaces.context.ContextInterface,
object_info: interfaces.objects.ObjectInformation) -> interfaces.objects.ObjectInterface:
"""Constructs the object
"""Constructs the object.
Returns: an object adhereing to the :class:`~volatility.framework.interfaces.objects.ObjectInterface`
Returns: an object adhereing to the :class:`~volatility.framework.interfaces.objects.ObjectInterface`
"""
arguments = {} # type: Dict[str, Any]
for arg in self.vol:
@@ -69,7 +73,7 @@ class ObjectTemplate(interfaces.objects.Template):
class ReferenceTemplate(interfaces.objects.Template):
"""Factory class that produces objects based on a delayed reference type
"""Factory class that produces objects based on a delayed reference type.
Attempts to access any standard attributes of a resolved template will result in a
:class:`~volatility.framework.exceptions.SymbolError`.
@@ -80,9 +84,9 @@ class ReferenceTemplate(interfaces.objects.Template):
return []
def _unresolved(self, *args, **kwargs) -> Any:
"""Referenced symbols must be appropriately resolved before they can provide information such as size
This is because the size request has no context within which to determine the actual symbol structure.
"""
"""Referenced symbols must be appropriately resolved before they can
provide information such as size This is because the size request has
no context within which to determine the actual symbol structure."""
raise exceptions.SymbolError("Template contains no information about its structure: {}".format(
self.vol.type_name))
+3 -3
View File
@@ -9,7 +9,7 @@ from volatility.framework import interfaces, objects, constants
def array_to_string(array: 'objects.Array', count: Optional[int] = None,
errors: str = 'replace') -> interfaces.objects.ObjectInterface:
"""Takes a volatility Array of characters and returns a string"""
"""Takes a volatility Array of characters and returns a string."""
# TODO: Consider checking the Array's target is a native char
if count is None:
count = array.vol.count
@@ -20,7 +20,7 @@ def array_to_string(array: 'objects.Array', count: Optional[int] = None,
def pointer_to_string(pointer: 'objects.Pointer', count: int, errors: str = 'replace'):
"""Takes a volatility Pointer to characters and returns a string"""
"""Takes a volatility Pointer to characters and returns a string."""
if not isinstance(pointer, objects.Pointer):
raise TypeError("pointer_to_string takes a Pointer")
if count < 1:
@@ -32,7 +32,7 @@ def pointer_to_string(pointer: 'objects.Pointer', count: int, errors: str = 'rep
def array_of_pointers(array: interfaces.objects.ObjectInterface, count: int,
subtype: Union[str, interfaces.objects.Template],
context: interfaces.context.ContextInterface) -> interfaces.objects.ObjectInterface:
"""Takes an object, and recasts it as an array of pointers to subtype"""
"""Takes an object, and recasts it as an array of pointers to subtype."""
symbol_table = array.vol.type_name.split(constants.BANG)[0]
if isinstance(subtype, str) and context is not None:
subtype = context.symbol_space.get_type(subtype)