From 9641626cae44ed17ef4af4e72e89276065dd85ed Mon Sep 17 00:00:00 2001 From: m-holger Date: Sun, 10 Mar 2024 15:34:58 +0000 Subject: [PATCH] Refactor resolving of objects --- include/qpdf/QPDF.hh | 9 ++- include/qpdf/QPDFObjectHandle.hh | 25 +++--- libqpdf/QPDF.cc | 7 +- libqpdf/QPDFObject.cc | 7 -- libqpdf/QPDFObjectHandle.cc | 122 +++++++++++++---------------- libqpdf/QPDF_Array.cc | 6 +- libqpdf/QPDF_Unresolved.cc | 17 ++-- libqpdf/qpdf/QPDFObject_private.hh | 35 ++++++--- libqpdf/qpdf/QPDF_Unresolved.hh | 1 + 9 files changed, 111 insertions(+), 118 deletions(-) diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index f936d401..e637434a 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -792,12 +792,13 @@ class QPDF class Resolver { friend class QPDFObject; + friend class QPDF_Unresolved; private: - static void - resolve(QPDF* qpdf, QPDFObjGen const& og) + static QPDFObject* + resolved(QPDF* qpdf, QPDFObjGen og) { - qpdf->resolve(og); + return qpdf->resolve(og); } }; @@ -1056,7 +1057,7 @@ class QPDF QPDFObjGen exp_og, QPDFObjGen& og, bool skip_cache_if_in_xref); - void resolve(QPDFObjGen og); + QPDFObject* resolve(QPDFObjGen og); void resolveObjectsInStream(int obj_stream_number); void stopOnError(std::string const& message); QPDFObjectHandle reserveObjectIfNotExists(QPDFObjGen const& og); diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 9ea329ff..0a03c035 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -1363,24 +1363,23 @@ class QPDFObjectHandle void writeJSON(int json_version, JSON::Writer& p, bool dereference_indirect = false); private: - QPDF_Array* asArray(); - QPDF_Bool* asBool(); - QPDF_Dictionary* asDictionary(); - QPDF_InlineImage* asInlineImage(); - QPDF_Integer* asInteger(); - QPDF_Name* asName(); - QPDF_Null* asNull(); - QPDF_Operator* asOperator(); - QPDF_Real* asReal(); - QPDF_Reserved* asReserved(); - QPDF_Stream* asStream(); + QPDF_Array* asArray() const; + QPDF_Bool* asBool() const; + QPDF_Dictionary* asDictionary() const; + QPDF_InlineImage* asInlineImage() const; + QPDF_Integer* asInteger() const; + QPDF_Name* asName() const; + QPDF_Null* asNull() const; + QPDF_Operator* asOperator() const; + QPDF_Real* asReal() const; + QPDF_Reserved* asReserved() const; + QPDF_Stream* asStream() const; QPDF_Stream* asStreamWithAssert(); - QPDF_String* asString(); + QPDF_String* asString() const; void typeWarning(char const* expected_type, std::string const& warning); void objectWarning(std::string const& warning); void assertType(char const* type_name, bool istype); - inline bool dereference(); void makeDirect(QPDFObjGen::set& visited, bool stop_at_streams); void disconnect(); void setParsedOffset(qpdf_offset_t offset); diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index d43fec16..8276b68f 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1728,11 +1728,11 @@ QPDF::readObjectAtOffset( return oh; } -void +QPDFObject* QPDF::resolve(QPDFObjGen og) { if (!isUnresolved(og)) { - return; + return m->obj_cache[og].object.get(); } if (m->resolving.count(og)) { @@ -1741,7 +1741,7 @@ QPDF::resolve(QPDFObjGen og) QTC::TC("qpdf", "QPDF recursion loop in resolve"); warn(damagedPDF("", "loop detected resolving object " + og.unparse(' '))); updateCache(og, QPDF_Null::create(), -1, -1); - return; + return m->obj_cache[og].object.get(); } ResolveRecorder rr(this, og); @@ -1782,6 +1782,7 @@ QPDF::resolve(QPDFObjGen og) auto result(m->obj_cache[og].object); result->setDefaultDescription(this, og); + return result.get(); } void diff --git a/libqpdf/QPDFObject.cc b/libqpdf/QPDFObject.cc index f6869925..7e3cc218 100644 --- a/libqpdf/QPDFObject.cc +++ b/libqpdf/QPDFObject.cc @@ -3,13 +3,6 @@ #include #include -void -QPDFObject::doResolve() -{ - auto og = value->og; - QPDF::Resolver::resolve(value->qpdf, og); -} - void QPDFObject::destroy() { diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index 163ebe88..b8ce416f 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -240,79 +240,79 @@ QPDFObjectHandle::disconnect() qpdf_object_type_e QPDFObjectHandle::getTypeCode() { - return dereference() ? this->obj->getTypeCode() : ::ot_uninitialized; + return obj ? obj->getResolvedTypeCode() : ::ot_uninitialized; } char const* QPDFObjectHandle::getTypeName() { - return dereference() ? this->obj->getTypeName() : "uninitialized"; + return obj ? obj->getTypeName() : "uninitialized"; } QPDF_Array* -QPDFObjectHandle::asArray() +QPDFObjectHandle::asArray() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Bool* -QPDFObjectHandle::asBool() +QPDFObjectHandle::asBool() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Dictionary* -QPDFObjectHandle::asDictionary() +QPDFObjectHandle::asDictionary() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_InlineImage* -QPDFObjectHandle::asInlineImage() +QPDFObjectHandle::asInlineImage() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Integer* -QPDFObjectHandle::asInteger() +QPDFObjectHandle::asInteger() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Name* -QPDFObjectHandle::asName() +QPDFObjectHandle::asName() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Null* -QPDFObjectHandle::asNull() +QPDFObjectHandle::asNull() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Operator* -QPDFObjectHandle::asOperator() +QPDFObjectHandle::asOperator() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Real* -QPDFObjectHandle::asReal() +QPDFObjectHandle::asReal() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Reserved* -QPDFObjectHandle::asReserved() +QPDFObjectHandle::asReserved() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Stream* -QPDFObjectHandle::asStream() +QPDFObjectHandle::asStream() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } QPDF_Stream* @@ -324,21 +324,21 @@ QPDFObjectHandle::asStreamWithAssert() } QPDF_String* -QPDFObjectHandle::asString() +QPDFObjectHandle::asString() const { - return dereference() ? obj->as() : nullptr; + return obj ? obj->as() : nullptr; } bool QPDFObjectHandle::isDestroyed() { - return dereference() && (obj->getTypeCode() == ::ot_destroyed); + return obj && obj->getResolvedTypeCode() == ::ot_destroyed; } bool QPDFObjectHandle::isBool() { - return dereference() && (obj->getTypeCode() == ::ot_boolean); + return obj && obj->getResolvedTypeCode() == ::ot_boolean; } bool @@ -346,25 +346,25 @@ QPDFObjectHandle::isDirectNull() const { // Don't call dereference() -- this is a const method, and we know // objid == 0, so there's nothing to resolve. - return (isInitialized() && (getObjectID() == 0) && (obj->getTypeCode() == ::ot_null)); + return (obj && getObjectID() == 0 && obj->getTypeCode() == ::ot_null); } bool QPDFObjectHandle::isNull() { - return dereference() && (obj->getTypeCode() == ::ot_null); + return obj && obj->getResolvedTypeCode() == ::ot_null; } bool QPDFObjectHandle::isInteger() { - return dereference() && (obj->getTypeCode() == ::ot_integer); + return obj && obj->getResolvedTypeCode() == ::ot_integer; } bool QPDFObjectHandle::isReal() { - return dereference() && (obj->getTypeCode() == ::ot_real); + return obj && obj->getResolvedTypeCode() == ::ot_real; } bool @@ -401,49 +401,49 @@ QPDFObjectHandle::getValueAsNumber(double& value) bool QPDFObjectHandle::isName() { - return dereference() && (obj->getTypeCode() == ::ot_name); + return obj && obj->getResolvedTypeCode() == ::ot_name; } bool QPDFObjectHandle::isString() { - return dereference() && (obj->getTypeCode() == ::ot_string); + return obj && obj->getResolvedTypeCode() == ::ot_string; } bool QPDFObjectHandle::isOperator() { - return dereference() && (obj->getTypeCode() == ::ot_operator); + return obj && obj->getResolvedTypeCode() == ::ot_operator; } bool QPDFObjectHandle::isInlineImage() { - return dereference() && (obj->getTypeCode() == ::ot_inlineimage); + return obj && obj->getResolvedTypeCode() == ::ot_inlineimage; } bool QPDFObjectHandle::isArray() { - return dereference() && (obj->getTypeCode() == ::ot_array); + return obj && obj->getResolvedTypeCode() == ::ot_array; } bool QPDFObjectHandle::isDictionary() { - return dereference() && (obj->getTypeCode() == ::ot_dictionary); + return obj && obj->getResolvedTypeCode() == ::ot_dictionary; } bool QPDFObjectHandle::isStream() { - return dereference() && (obj->getTypeCode() == ::ot_stream); + return obj && obj->getResolvedTypeCode() == ::ot_stream; } bool QPDFObjectHandle::isReserved() { - return dereference() && (obj->getTypeCode() == ::ot_reserved); + return obj && obj->getResolvedTypeCode() == ::ot_reserved; } bool @@ -1586,7 +1586,7 @@ QPDFObjectHandle::unparse() std::string QPDFObjectHandle::unparseResolved() { - if (!dereference()) { + if (!obj) { throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); } return obj->unparse(); @@ -1615,7 +1615,7 @@ QPDFObjectHandle::getJSON(int json_version, bool dereference_indirect) { if ((!dereference_indirect) && isIndirect()) { return JSON::makeString(unparse()); - } else if (!dereference()) { + } else if (!obj) { throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); } else { Pl_Buffer p{"json"}; @@ -1631,7 +1631,7 @@ QPDFObjectHandle::writeJSON(int json_version, JSON::Writer& p, bool dereference_ { if (!dereference_indirect && isIndirect()) { p << "\"" << getObjGen().unparse(' ') << " R\""; - } else if (!dereference()) { + } else if (!obj) { throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); } else { obj->writeJSON(json_version, p); @@ -1874,11 +1874,7 @@ QPDFObjectHandle::parse( qpdf_offset_t QPDFObjectHandle::getParsedOffset() { - if (dereference()) { - return this->obj->getParsedOffset(); - } else { - return -1; - } + return obj ? obj->getParsedOffset() : -1; } QPDFObjectHandle @@ -2055,9 +2051,7 @@ QPDFObjectHandle::newReserved(QPDF* qpdf) void QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& object_description) { - // This is called during parsing on newly created direct objects, so we can't call dereference() - // here. - if (isInitialized() && obj.get()) { + if (obj) { auto descr = std::make_shared(object_description); obj->setDescription(owning_qpdf, descr); } @@ -2066,13 +2060,13 @@ QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, std::string const& obj bool QPDFObjectHandle::hasObjectDescription() { - return dereference() && obj.get() && obj->hasDescription(); + return obj && obj->hasDescription(); } QPDFObjectHandle QPDFObjectHandle::shallowCopy() { - if (!dereference()) { + if (!obj) { throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle"); } return {obj->copy()}; @@ -2081,7 +2075,7 @@ QPDFObjectHandle::shallowCopy() QPDFObjectHandle QPDFObjectHandle::unsafeShallowCopy() { - if (!dereference()) { + if (!obj) { throw std::logic_error("operation attempted on uninitialized QPDFObjectHandle"); } return {obj->copy(true)}; @@ -2172,10 +2166,10 @@ QPDFObjectHandle::typeWarning(char const* expected_type, std::string const& warn std::string description; // Type checks above guarantee that the object has been dereferenced. Nevertheless, dereference // throws exceptions in the test suite - if (!dereference()) { + if (!obj) { throw std::logic_error("attempted to dereference an uninitialized QPDFObjectHandle"); } - this->obj->getDescription(context, description); + obj->getDescription(context, description); // Null context handled by warn warn( context, @@ -2193,7 +2187,7 @@ QPDFObjectHandle::warnIfPossible(std::string const& warning) { QPDF* context = nullptr; std::string description; - if (dereference() && obj->getDescription(context, description)) { + if (obj && obj->getDescription(context, description)) { warn(context, QPDFExc(qpdf_e_damaged_pdf, "", description, 0, warning)); } else { *QPDFLogger::defaultLogger()->getError() << warning << "\n"; @@ -2205,8 +2199,8 @@ QPDFObjectHandle::objectWarning(std::string const& warning) { QPDF* context = nullptr; std::string description; - // Type checks above guarantee that the object has been dereferenced. - this->obj->getDescription(context, description); + // Type checks above guarantee that the object is initialized. + obj->getDescription(context, description); // Null context handled by warn warn(context, QPDFExc(qpdf_e_object, "", description, 0, warning)); } @@ -2372,16 +2366,6 @@ QPDFObjectHandle::assertPageObject() } } -inline bool -QPDFObjectHandle::dereference() -{ - if (!isInitialized()) { - return false; - } - this->obj->resolve(); - return true; -} - void QPDFObjectHandle::warn(QPDF* qpdf, QPDFExc const& e) { diff --git a/libqpdf/QPDF_Array.cc b/libqpdf/QPDF_Array.cc index ab2a4c8c..c1c373ae 100644 --- a/libqpdf/QPDF_Array.cc +++ b/libqpdf/QPDF_Array.cc @@ -130,8 +130,7 @@ QPDF_Array::unparse() for (int j = next; j < key; ++j) { result += "null "; } - item.second->resolve(); - auto og = item.second->getObjGen(); + auto og = item.second->resolved_object()->getObjGen(); result += og.isIndirect() ? og.unparse(' ') + " R " : item.second->unparse() + " "; next = ++key; } @@ -140,8 +139,7 @@ QPDF_Array::unparse() } } else { for (auto const& item: elements) { - item->resolve(); - auto og = item->getObjGen(); + auto og = item->resolved_object()->getObjGen(); result += og.isIndirect() ? og.unparse(' ') + " R " : item->unparse() + " "; } } diff --git a/libqpdf/QPDF_Unresolved.cc b/libqpdf/QPDF_Unresolved.cc index dc14c2f2..c0c24fda 100644 --- a/libqpdf/QPDF_Unresolved.cc +++ b/libqpdf/QPDF_Unresolved.cc @@ -1,6 +1,7 @@ #include -#include +#include +#include QPDF_Unresolved::QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og) : QPDFValue(::ot_unresolved, "unresolved", qpdf, og) @@ -16,19 +17,23 @@ QPDF_Unresolved::create(QPDF* qpdf, QPDFObjGen const& og) std::shared_ptr QPDF_Unresolved::copy(bool shallow) { - throw std::logic_error("attempted to shallow copy an unresolved QPDFObjectHandle"); - return nullptr; + return QPDF::Resolver::resolved(qpdf, og)->copy(shallow); } std::string QPDF_Unresolved::unparse() { - throw std::logic_error("attempted to unparse an unresolved QPDFObjectHandle"); - return ""; + return QPDF::Resolver::resolved(qpdf, og)->unparse(); } void QPDF_Unresolved::writeJSON(int json_version, JSON::Writer& p) { - throw std::logic_error("attempted to get JSON from an unresolved QPDFObjectHandle"); + QPDF::Resolver::resolved(qpdf, og)->writeJSON(json_version, p); +} + +std::string +QPDF_Unresolved::getStringValue() const +{ + return QPDF::Resolver::resolved(qpdf, og)->getStringValue(); } diff --git a/libqpdf/qpdf/QPDFObject_private.hh b/libqpdf/qpdf/QPDFObject_private.hh index 1c3dadc9..e5748006 100644 --- a/libqpdf/qpdf/QPDFObject_private.hh +++ b/libqpdf/qpdf/QPDFObject_private.hh @@ -5,8 +5,8 @@ // include/qpdf/QPDFObject.hh. See comments there for an explanation. #include -#include #include +#include #include #include @@ -43,18 +43,26 @@ class QPDFObject { return value->getStringValue(); } + // Return a unique type code for the resolved object + qpdf_object_type_e + getResolvedTypeCode() const + { + auto tc = value->type_code; + return tc == ::ot_unresolved + ? QPDF::Resolver::resolved(value->qpdf, value->og)->value->type_code + : tc; + } // Return a unique type code for the object qpdf_object_type_e - getTypeCode() const + getTypeCode() const noexcept { return value->type_code; } - // Return a string literal that describes the type, useful for debugging and testing char const* getTypeName() const { - return value->type_name; + return resolved_object()->value->type_name; } QPDF* @@ -157,20 +165,23 @@ class QPDFObject { return value->type_code == ::ot_unresolved; } - void - resolve() + const QPDFObject* + resolved_object() const { - if (isUnresolved()) { - doResolve(); - } + return isUnresolved() ? QPDF::Resolver::resolved(value->qpdf, value->og) : this; } - void doResolve(); template T* - as() + as() const { - return dynamic_cast(value.get()); + if (auto result = dynamic_cast(value.get())) { + return result; + } else { + return isUnresolved() + ? dynamic_cast(QPDF::Resolver::resolved(value->qpdf, value->og)->value.get()) + : nullptr; + } } private: diff --git a/libqpdf/qpdf/QPDF_Unresolved.hh b/libqpdf/qpdf/QPDF_Unresolved.hh index b4c1d9e4..df443d34 100644 --- a/libqpdf/qpdf/QPDF_Unresolved.hh +++ b/libqpdf/qpdf/QPDF_Unresolved.hh @@ -11,6 +11,7 @@ class QPDF_Unresolved: public QPDFValue std::shared_ptr copy(bool shallow = false) override; std::string unparse() override; void writeJSON(int json_version, JSON::Writer& p) override; + std::string getStringValue() const override; private: QPDF_Unresolved(QPDF* qpdf, QPDFObjGen const& og);