Add 'strict pointer mode' to BlendFile
Add a 'strict pointer mode' to the `BlendFile` class, which is enabled by default. This allows users of the `BlendFile` class to decide whether a bad pointer (i.e. one that points to a non-existing datablock) returns `None` or raises a `SegmentationFault` exception.
This commit is contained in:
parent
803c38dac1
commit
087ff25c76
@ -106,6 +106,12 @@ class BlendFile:
|
|||||||
|
|
||||||
log = log.getChild("BlendFile")
|
log = log.getChild("BlendFile")
|
||||||
|
|
||||||
|
strict_pointer_mode = True
|
||||||
|
"""Raise exceptions.SegmentationFault when dereferencing an unknown pointer.
|
||||||
|
|
||||||
|
Set to False to disable this exception, and to return None instead.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, path: pathlib.Path, mode="rb") -> None:
|
def __init__(self, path: pathlib.Path, mode="rb") -> None:
|
||||||
"""Create a BlendFile instance for the blend file at the path.
|
"""Create a BlendFile instance for the blend file at the path.
|
||||||
|
|
||||||
@ -401,15 +407,21 @@ class BlendFile:
|
|||||||
|
|
||||||
return abspath
|
return abspath
|
||||||
|
|
||||||
def dereference_pointer(self, address: int) -> "BlendFileBlock":
|
def dereference_pointer(self, address: int) -> typing.Optional["BlendFileBlock"]:
|
||||||
"""Return the pointed-to block, or raise SegmentationFault."""
|
"""Return the pointed-to block, or raise SegmentationFault.
|
||||||
|
|
||||||
|
When BlendFile.strict_pointer_mode is False, the exception will not be
|
||||||
|
thrown, but None will be returned.
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.block_from_addr[address]
|
return self.block_from_addr[address]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise exceptions.SegmentationFault(
|
if self.strict_pointer_mode:
|
||||||
"address does not exist", address
|
raise exceptions.SegmentationFault(
|
||||||
) from None
|
"address does not exist", address
|
||||||
|
) from None
|
||||||
|
return None
|
||||||
|
|
||||||
def struct(self, name: bytes) -> dna.Struct:
|
def struct(self, name: bytes) -> dna.Struct:
|
||||||
index = self.sdna_index_from_id[name]
|
index = self.sdna_index_from_id[name]
|
||||||
@ -755,7 +767,11 @@ class BlendFileBlock:
|
|||||||
address = endian.read_pointer(fileobj, ps)
|
address = endian.read_pointer(fileobj, ps)
|
||||||
if address == 0:
|
if address == 0:
|
||||||
continue
|
continue
|
||||||
yield self.bfile.dereference_pointer(address)
|
dereferenced = self.bfile.dereference_pointer(address)
|
||||||
|
if dereferenced is None:
|
||||||
|
# This can happen when strict pointer mode is disabled.
|
||||||
|
continue
|
||||||
|
yield dereferenced
|
||||||
|
|
||||||
def iter_fixed_array_of_pointers(
|
def iter_fixed_array_of_pointers(
|
||||||
self, path: dna.FieldPath
|
self, path: dna.FieldPath
|
||||||
@ -786,7 +802,12 @@ class BlendFileBlock:
|
|||||||
if not address:
|
if not address:
|
||||||
# Fixed-size arrays contain 0-pointers.
|
# Fixed-size arrays contain 0-pointers.
|
||||||
continue
|
continue
|
||||||
yield self.bfile.dereference_pointer(address)
|
|
||||||
|
dereferenced = self.bfile.dereference_pointer(address)
|
||||||
|
if dereferenced is None:
|
||||||
|
# This can happen when strict pointer mode is disabled.
|
||||||
|
continue
|
||||||
|
yield dereferenced
|
||||||
|
|
||||||
def __getitem__(self, path: dna.FieldPath):
|
def __getitem__(self, path: dna.FieldPath):
|
||||||
return self.get(path)
|
return self.get(path)
|
||||||
|
|||||||
@ -512,6 +512,14 @@ class Packer:
|
|||||||
|
|
||||||
# Find the same block in the newly copied file.
|
# Find the same block in the newly copied file.
|
||||||
block = bfile.dereference_pointer(usage.block.addr_old)
|
block = bfile.dereference_pointer(usage.block.addr_old)
|
||||||
|
|
||||||
|
# Pointers can point to a non-existing data block, in which case
|
||||||
|
# either a SegmentationFault exception is thrown, or None is
|
||||||
|
# returned, based on the strict pointer mode set on the
|
||||||
|
# BlendFile class. Since this block was already meant to be
|
||||||
|
# rewritten, it was found before.
|
||||||
|
assert block is not None
|
||||||
|
|
||||||
if usage.path_full_field is None:
|
if usage.path_full_field is None:
|
||||||
dir_field = usage.path_dir_field
|
dir_field = usage.path_dir_field
|
||||||
assert dir_field is not None
|
assert dir_field is not None
|
||||||
|
|||||||
@ -217,6 +217,15 @@ class PointerTest(AbstractBlendFileTest):
|
|||||||
with self.assertRaises(exceptions.SegmentationFault):
|
with self.assertRaises(exceptions.SegmentationFault):
|
||||||
scene.get_pointer(b"ed")
|
scene.get_pointer(b"ed")
|
||||||
|
|
||||||
|
def test_disabled_strict_pointer_mode(self):
|
||||||
|
scene = self.bf.code_index[b"SC"][0]
|
||||||
|
ed_ptr = scene.get(b"ed")
|
||||||
|
del self.bf.block_from_addr[ed_ptr]
|
||||||
|
|
||||||
|
self.bf.strict_pointer_mode = False
|
||||||
|
dereferenced = scene.get_pointer(b"ed")
|
||||||
|
self.assertIsNone(dereferenced)
|
||||||
|
|
||||||
def test_abs_offset(self):
|
def test_abs_offset(self):
|
||||||
scene = self.bf.code_index[b"SC"][0]
|
scene = self.bf.code_index[b"SC"][0]
|
||||||
ed = scene.get_pointer(b"ed")
|
ed = scene.get_pointer(b"ed")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user