Commit c7a4967d10fb9688f235baa8e57a1fb578f5387d

Authored by Jay Berkenbilt
1 parent dba61da1

Change reset to disconnect and clarify comments

I decided that it's actually fine to copy a direct object to another
QPDF. Even if we eventually prevent a QPDFObject from having multiple
parents, this could happen if an object is moved.
@@ -785,7 +785,7 @@ Rejected Ideas @@ -785,7 +785,7 @@ Rejected Ideas
785 and too much toil for library users to be worth the small benefit of 785 and too much toil for library users to be worth the small benefit of
786 not having to call resetObjGen in QPDF's destructor. 786 not having to call resetObjGen in QPDF's destructor.
787 787
788 -* Fix Multiple Direct Object Owner Issue 788 +* Fix Multiple Direct Object Parent Issue
789 789
790 These are some ideas I had before m-holger's changes to split 790 These are some ideas I had before m-holger's changes to split
791 QPDFValue from QPDFObject. These notes were written prior to the 791 QPDFValue from QPDFObject. These notes were written prior to the
@@ -811,12 +811,3 @@ Rejected Ideas @@ -811,12 +811,3 @@ Rejected Ideas
811 Note that arrays and dictionaries still need to contain 811 Note that arrays and dictionaries still need to contain
812 QPDFObjectHandle because of indirect objects. This only pertains to 812 QPDFObjectHandle because of indirect objects. This only pertains to
813 direct objects, which are always "resolved" in QPDFObjectHandle. 813 direct objects, which are always "resolved" in QPDFObjectHandle.
814 -  
815 - If this is addressed, read comments in the following places:  
816 - * QPDFWriter.cc::enqueueObject near the call to getOwningQPDF  
817 - * QPDFValueProxy::reset and QPDFValueProxy::destroy  
818 - * QPDF::~QPDF()  
819 - * test 92 in test_driver.cc  
820 - * QPDFObjectHandle.hh near isDestroyed  
821 - All these references were from the release of qpdf 11 (in case they  
822 - have moved by such time as this might be resurrected).  
include/qpdf/QPDFObjectHandle.hh
@@ -392,7 +392,8 @@ class QPDFObjectHandle @@ -392,7 +392,8 @@ class QPDFObjectHandle
392 inline bool isIndirect() const; 392 inline bool isIndirect() const;
393 393
394 // This returns true for indirect objects from a QPDF that has 394 // This returns true for indirect objects from a QPDF that has
395 - // been destroyed. 395 + // been destroyed. Trying unparse such an object will throw a
  396 + // logic_error.
396 QPDF_DLL 397 QPDF_DLL
397 bool isDestroyed(); 398 bool isDestroyed();
398 399
@@ -1540,8 +1541,8 @@ class QPDFObjectHandle @@ -1540,8 +1541,8 @@ class QPDFObjectHandle
1540 friend class ObjAccessor; 1541 friend class ObjAccessor;
1541 1542
1542 // Provide access to specific classes for recursive 1543 // Provide access to specific classes for recursive
1543 - // reset().  
1544 - class Resetter 1544 + // disconnected().
  1545 + class DisconnectAccess
1545 { 1546 {
1546 friend class QPDF_Dictionary; 1547 friend class QPDF_Dictionary;
1547 friend class QPDF_Stream; 1548 friend class QPDF_Stream;
@@ -1549,9 +1550,9 @@ class QPDFObjectHandle @@ -1549,9 +1550,9 @@ class QPDFObjectHandle
1549 1550
1550 private: 1551 private:
1551 static void 1552 static void
1552 - reset(QPDFObjectHandle& o) 1553 + disconnect(QPDFObjectHandle& o)
1553 { 1554 {
1554 - o.reset(); 1555 + o.disconnect();
1555 } 1556 }
1556 }; 1557 };
1557 friend class Resetter; 1558 friend class Resetter;
@@ -1653,7 +1654,7 @@ class QPDFObjectHandle @@ -1653,7 +1654,7 @@ class QPDFObjectHandle
1653 bool first_level_only, 1654 bool first_level_only,
1654 bool stop_at_streams); 1655 bool stop_at_streams);
1655 void shallowCopyInternal(QPDFObjectHandle& oh, bool first_level_only); 1656 void shallowCopyInternal(QPDFObjectHandle& oh, bool first_level_only);
1656 - void reset(); 1657 + void disconnect();
1657 void setParsedOffset(qpdf_offset_t offset); 1658 void setParsedOffset(qpdf_offset_t offset);
1658 void parseContentStream_internal( 1659 void parseContentStream_internal(
1659 std::string const& description, ParserCallbacks* callbacks); 1660 std::string const& description, ParserCallbacks* callbacks);
libqpdf/QPDF.cc
@@ -252,9 +252,11 @@ QPDF::~QPDF() @@ -252,9 +252,11 @@ QPDF::~QPDF()
252 // resolved indirect references by replacing them with an internal 252 // resolved indirect references by replacing them with an internal
253 // object type representing that they have been destroyed. Note 253 // object type representing that they have been destroyed. Note
254 // that we can't break references like this at any time when the 254 // that we can't break references like this at any time when the
255 - // QPDF object is active. The call to reset also causes all 255 + // QPDF object is active. The call to reset also causes all direct
256 // QPDFObjectHandle objects that are reachable from this object to 256 // QPDFObjectHandle objects that are reachable from this object to
257 - // release their association with this QPDF. 257 + // release their association with this QPDF. Direct objects are
  258 + // not destroyed since they can be moved to other QPDF objects
  259 + // safely.
258 260
259 // At this point, obviously no one is still using the QPDF object, 261 // At this point, obviously no one is still using the QPDF object,
260 // but we'll explicitly clear the xref table anyway just to 262 // but we'll explicitly clear the xref table anyway just to
@@ -262,9 +264,7 @@ QPDF::~QPDF() @@ -262,9 +264,7 @@ QPDF::~QPDF()
262 this->m->xref_table.clear(); 264 this->m->xref_table.clear();
263 auto null_obj = QPDF_Null::create(); 265 auto null_obj = QPDF_Null::create();
264 for (auto const& iter: this->m->obj_cache) { 266 for (auto const& iter: this->m->obj_cache) {
265 - iter.second.object->reset();  
266 - // It would be better if reset() could call destroy(), but it  
267 - // can't -- see comments in QPDFValueProxy::reset(). 267 + iter.second.object->disconnect();
268 iter.second.object->destroy(); 268 iter.second.object->destroy();
269 } 269 }
270 } 270 }
libqpdf/QPDFObjectHandle.cc
@@ -249,7 +249,7 @@ QPDFObjectHandle::operator!=(QPDFObjectHandle const& rhs) const @@ -249,7 +249,7 @@ QPDFObjectHandle::operator!=(QPDFObjectHandle const& rhs) const
249 } 249 }
250 250
251 void 251 void
252 -QPDFObjectHandle::reset() 252 +QPDFObjectHandle::disconnect()
253 { 253 {
254 // Recursively remove association with any QPDF object. This 254 // Recursively remove association with any QPDF object. This
255 // method may only be called during final destruction. 255 // method may only be called during final destruction.
@@ -257,7 +257,7 @@ QPDFObjectHandle::reset() @@ -257,7 +257,7 @@ QPDFObjectHandle::reset()
257 // pointer itself, so we don't do that here. Other objects call it 257 // pointer itself, so we don't do that here. Other objects call it
258 // through this method. 258 // through this method.
259 if (!isIndirect()) { 259 if (!isIndirect()) {
260 - this->obj->reset(); 260 + this->obj->disconnect();
261 } 261 }
262 } 262 }
263 263
libqpdf/QPDFValueProxy.cc
@@ -13,6 +13,5 @@ QPDFValueProxy::doResolve() @@ -13,6 +13,5 @@ QPDFValueProxy::doResolve()
13 void 13 void
14 QPDFValueProxy::destroy() 14 QPDFValueProxy::destroy()
15 { 15 {
16 - // See comments in reset() for why this isn't part of reset.  
17 value = QPDF_Destroyed::getInstance(); 16 value = QPDF_Destroyed::getInstance();
18 } 17 }
libqpdf/QPDFWriter.cc
@@ -1198,14 +1198,12 @@ void @@ -1198,14 +1198,12 @@ void
1198 QPDFWriter::enqueueObject(QPDFObjectHandle object) 1198 QPDFWriter::enqueueObject(QPDFObjectHandle object)
1199 { 1199 {
1200 if (object.isIndirect()) { 1200 if (object.isIndirect()) {
1201 - // This owner check should really be done for all objects, not  
1202 - // just indirect objects. As of the time of the release of  
1203 - // qpdf 11, it is known that there are cases of direct objects  
1204 - // from other files getting copied into multiple QPDF objects.  
1205 - // This definitely happens in the page splitting code. If we  
1206 - // were to implement strong checks to prevent objects from  
1207 - // having multiple owners, once that was complete phased in,  
1208 - // this check could be moved outside the if statement. 1201 + // This owner check can only be done for indirect objects. It
  1202 + // is possible for a direct object to have an owning QPDF that
  1203 + // is from another file if a direct QPDFObjectHandle from one
  1204 + // file was insert into another file without copying. Doing
  1205 + // that is safe even if the original QPDF gets destroyed,
  1206 + // which just disconnects the QPDFObjectHandle from its owner.
1209 if (object.getOwningQPDF() != &(this->m->pdf)) { 1207 if (object.getOwningQPDF() != &(this->m->pdf)) {
1210 QTC::TC("qpdf", "QPDFWriter foreign object"); 1208 QTC::TC("qpdf", "QPDFWriter foreign object");
1211 throw std::logic_error( 1209 throw std::logic_error(
libqpdf/QPDF_Array.cc
@@ -35,9 +35,9 @@ QPDF_Array::shallowCopy() @@ -35,9 +35,9 @@ QPDF_Array::shallowCopy()
35 } 35 }
36 36
37 void 37 void
38 -QPDF_Array::reset() 38 +QPDF_Array::disconnect()
39 { 39 {
40 - elements.reset(); 40 + elements.disconnect();
41 } 41 }
42 42
43 std::string 43 std::string
libqpdf/QPDF_Dictionary.cc
@@ -22,10 +22,10 @@ QPDF_Dictionary::shallowCopy() @@ -22,10 +22,10 @@ QPDF_Dictionary::shallowCopy()
22 } 22 }
23 23
24 void 24 void
25 -QPDF_Dictionary::reset() 25 +QPDF_Dictionary::disconnect()
26 { 26 {
27 for (auto& iter: this->items) { 27 for (auto& iter: this->items) {
28 - QPDFObjectHandle::Resetter::reset(iter.second); 28 + QPDFObjectHandle::DisconnectAccess::disconnect(iter.second);
29 } 29 }
30 } 30 }
31 31
libqpdf/QPDF_Stream.cc
@@ -168,10 +168,10 @@ QPDF_Stream::getFilterOnWrite() const @@ -168,10 +168,10 @@ QPDF_Stream::getFilterOnWrite() const
168 } 168 }
169 169
170 void 170 void
171 -QPDF_Stream::reset() 171 +QPDF_Stream::disconnect()
172 { 172 {
173 this->stream_provider = nullptr; 173 this->stream_provider = nullptr;
174 - QPDFObjectHandle::Resetter::reset(this->stream_dict); 174 + QPDFObjectHandle::DisconnectAccess::disconnect(this->stream_dict);
175 } 175 }
176 176
177 void 177 void
libqpdf/SparseOHArray.cc
@@ -49,10 +49,10 @@ SparseOHArray::remove_last() @@ -49,10 +49,10 @@ SparseOHArray::remove_last()
49 } 49 }
50 50
51 void 51 void
52 -SparseOHArray::reset() 52 +SparseOHArray::disconnect()
53 { 53 {
54 for (auto& iter: this->elements) { 54 for (auto& iter: this->elements) {
55 - QPDFObjectHandle::Resetter::reset(iter.second); 55 + QPDFObjectHandle::DisconnectAccess::disconnect(iter.second);
56 } 56 }
57 } 57 }
58 58
libqpdf/qpdf/QPDFValue.hh
@@ -64,7 +64,7 @@ class QPDFValue @@ -64,7 +64,7 @@ class QPDFValue
64 return og; 64 return og;
65 } 65 }
66 virtual void 66 virtual void
67 - reset() 67 + disconnect()
68 { 68 {
69 } 69 }
70 70
libqpdf/qpdf/QPDFValueProxy.hh
@@ -102,33 +102,24 @@ class QPDFValueProxy @@ -102,33 +102,24 @@ class QPDFValueProxy
102 o->value->og = og; 102 o->value->og = og;
103 } 103 }
104 104
105 - // The following two methods are for use by class QPDF only  
106 void 105 void
107 setObjGen(QPDF* qpdf, QPDFObjGen const& og) 106 setObjGen(QPDF* qpdf, QPDFObjGen const& og)
108 { 107 {
  108 + // Intended for use by the QPDF class
109 value->qpdf = qpdf; 109 value->qpdf = qpdf;
110 value->og = og; 110 value->og = og;
111 } 111 }
112 void 112 void
113 - reset()  
114 - {  
115 - value->reset();  
116 - // It would be better if, rather than clearing value->qpdf and  
117 - // value->og, we completely replaced value with  
118 - // QPDF_Destroyed. However, at the time of the release of qpdf  
119 - // 11, this causes test failures and would likely break a lot  
120 - // of code since it possible for a direct object that  
121 - // recursively contains no indirect objects to be copied into  
122 - // multiple QPDF objects. For that reason, we have to break  
123 - // the association with the owning QPDF but not otherwise  
124 - // mutate the object. For indirect objects, QPDF::~QPDF  
125 - // replaces indirect objects with QPDF_Destroyed, which clears  
126 - // circular references. If this code were able to do that,  
127 - // that code would not have to. 113 + disconnect()
  114 + {
  115 + // Disconnect an object from its owning QPDF. This is called
  116 + // by QPDF's destructor.
  117 + value->disconnect();
128 value->qpdf = nullptr; 118 value->qpdf = nullptr;
129 value->og = QPDFObjGen(); 119 value->og = QPDFObjGen();
130 } 120 }
131 - 121 + // Mark an object as destroyed. Used by QPDF's destructor for its
  122 + // indirect objects.
132 void destroy(); 123 void destroy();
133 124
134 bool 125 bool
libqpdf/qpdf/QPDF_Array.hh
@@ -17,7 +17,7 @@ class QPDF_Array: public QPDFValue @@ -17,7 +17,7 @@ class QPDF_Array: public QPDFValue
17 virtual std::shared_ptr<QPDFValueProxy> shallowCopy(); 17 virtual std::shared_ptr<QPDFValueProxy> shallowCopy();
18 virtual std::string unparse(); 18 virtual std::string unparse();
19 virtual JSON getJSON(int json_version); 19 virtual JSON getJSON(int json_version);
20 - virtual void reset(); 20 + virtual void disconnect();
21 21
22 int getNItems() const; 22 int getNItems() const;
23 QPDFObjectHandle getItem(int n) const; 23 QPDFObjectHandle getItem(int n) const;
libqpdf/qpdf/QPDF_Dictionary.hh
@@ -17,7 +17,7 @@ class QPDF_Dictionary: public QPDFValue @@ -17,7 +17,7 @@ class QPDF_Dictionary: public QPDFValue
17 virtual std::shared_ptr<QPDFValueProxy> shallowCopy(); 17 virtual std::shared_ptr<QPDFValueProxy> shallowCopy();
18 virtual std::string unparse(); 18 virtual std::string unparse();
19 virtual JSON getJSON(int json_version); 19 virtual JSON getJSON(int json_version);
20 - virtual void reset(); 20 + virtual void disconnect();
21 21
22 // hasKey() and getKeys() treat keys with null values as if they 22 // hasKey() and getKeys() treat keys with null values as if they
23 // aren't there. getKey() returns null for the value of a 23 // aren't there. getKey() returns null for the value of a
libqpdf/qpdf/QPDF_Stream.hh
@@ -27,7 +27,7 @@ class QPDF_Stream: public QPDFValue @@ -27,7 +27,7 @@ class QPDF_Stream: public QPDFValue
27 virtual std::string unparse(); 27 virtual std::string unparse();
28 virtual JSON getJSON(int json_version); 28 virtual JSON getJSON(int json_version);
29 virtual void setDescription(QPDF*, std::string const&); 29 virtual void setDescription(QPDF*, std::string const&);
30 - virtual void reset(); 30 + virtual void disconnect();
31 QPDFObjectHandle getDict() const; 31 QPDFObjectHandle getDict() const;
32 bool isDataModified() const; 32 bool isDataModified() const;
33 void setFilterOnWrite(bool); 33 void setFilterOnWrite(bool);
libqpdf/qpdf/SparseOHArray.hh
@@ -15,7 +15,7 @@ class SparseOHArray @@ -15,7 +15,7 @@ class SparseOHArray
15 void setAt(size_t idx, QPDFObjectHandle oh); 15 void setAt(size_t idx, QPDFObjectHandle oh);
16 void erase(size_t idx); 16 void erase(size_t idx);
17 void insert(size_t idx, QPDFObjectHandle oh); 17 void insert(size_t idx, QPDFObjectHandle oh);
18 - void reset(); 18 + void disconnect();
19 19
20 typedef std::unordered_map<size_t, QPDFObjectHandle>::const_iterator 20 typedef std::unordered_map<size_t, QPDFObjectHandle>::const_iterator
21 const_iterator; 21 const_iterator;
qpdf/test_driver.cc
@@ -3316,8 +3316,8 @@ test_92(QPDF&amp; pdf, char const* arg2) @@ -3316,8 +3316,8 @@ test_92(QPDF&amp; pdf, char const* arg2)
3316 check(contents); 3316 check(contents);
3317 check(contents_dict); 3317 check(contents_dict);
3318 // Objects that were originally indirect should be destroyed. 3318 // Objects that were originally indirect should be destroyed.
3319 - // Otherwise, they should have retained their old values. See  
3320 - // comments in QPDFValueProxy::reset for why this is the case. 3319 + // Otherwise, they should have retained their old values but just
  3320 + // lost their connection to the owning QPDF.
3321 assert(root.isDestroyed()); 3321 assert(root.isDestroyed());
3322 assert(page1.isDestroyed()); 3322 assert(page1.isDestroyed());
3323 assert(contents.isDestroyed()); 3323 assert(contents.isDestroyed());