1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef _MDB_
7 #  include "mdb.h"
8 #endif
9 
10 #ifndef _MORK_
11 #  include "mork.h"
12 #endif
13 
14 #ifndef _MORKNODE_
15 #  include "morkNode.h"
16 #endif
17 
18 #ifndef _MORKPOOL_
19 #  include "morkPool.h"
20 #endif
21 
22 #ifndef _MORKENV_
23 #  include "morkEnv.h"
24 #endif
25 
26 #ifndef _MORKHANDLE_
27 #  include "morkHandle.h"
28 #endif
29 
30 /*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/
31 
32 /* ===== ===== ===== ===== morkUsage ===== ===== ===== ===== */
33 
34 static morkUsage morkUsage_gHeap;  // ensure EnsureReadyStaticUsage()
35 const morkUsage& morkUsage::kHeap = morkUsage_gHeap;
36 
37 static morkUsage morkUsage_gStack;  // ensure EnsureReadyStaticUsage()
38 const morkUsage& morkUsage::kStack = morkUsage_gStack;
39 
40 static morkUsage morkUsage_gMember;  // ensure EnsureReadyStaticUsage()
41 const morkUsage& morkUsage::kMember = morkUsage_gMember;
42 
43 static morkUsage morkUsage_gGlobal;  // ensure EnsureReadyStaticUsage()
44 const morkUsage& morkUsage::kGlobal = morkUsage_gGlobal;
45 
46 static morkUsage morkUsage_gPool;  // ensure EnsureReadyStaticUsage()
47 const morkUsage& morkUsage::kPool = morkUsage_gPool;
48 
49 static morkUsage morkUsage_gNone;  // ensure EnsureReadyStaticUsage()
50 const morkUsage& morkUsage::kNone = morkUsage_gNone;
51 
52 // This must be structured to allow for non-zero values in global variables
53 // just before static init time.  We can only safely check for whether a
54 // global has the address of some other global.  Please, do not initialize
55 // either of the variables below to zero, because this could break when a zero
56 // is assigned at static init time, but after EnsureReadyStaticUsage() runs.
57 
58 static mork_u4 morkUsage_g_static_init_target;  // only address of this matters
59 static mork_u4* morkUsage_g_static_init_done;   // is address of target above?
60 
61 #define morkUsage_do_static_init() \
62   (morkUsage_g_static_init_done = &morkUsage_g_static_init_target)
63 
64 #define morkUsage_need_static_init() \
65   (morkUsage_g_static_init_done != &morkUsage_g_static_init_target)
66 
67 /*static*/
EnsureReadyStaticUsage()68 void morkUsage::EnsureReadyStaticUsage() {
69   if (morkUsage_need_static_init()) {
70     morkUsage_do_static_init();
71 
72     morkUsage_gHeap.InitUsage(morkUsage_kHeap);
73     morkUsage_gStack.InitUsage(morkUsage_kStack);
74     morkUsage_gMember.InitUsage(morkUsage_kMember);
75     morkUsage_gGlobal.InitUsage(morkUsage_kGlobal);
76     morkUsage_gPool.InitUsage(morkUsage_kPool);
77     morkUsage_gNone.InitUsage(morkUsage_kNone);
78   }
79 }
80 
81 /*static*/
GetHeap()82 const morkUsage& morkUsage::GetHeap()  // kHeap safe at static init time
83 {
84   EnsureReadyStaticUsage();
85   return morkUsage_gHeap;
86 }
87 
88 /*static*/
GetStack()89 const morkUsage& morkUsage::GetStack()  // kStack safe at static init time
90 {
91   EnsureReadyStaticUsage();
92   return morkUsage_gStack;
93 }
94 
95 /*static*/
GetMember()96 const morkUsage& morkUsage::GetMember()  // kMember safe at static init time
97 {
98   EnsureReadyStaticUsage();
99   return morkUsage_gMember;
100 }
101 
102 /*static*/
GetGlobal()103 const morkUsage& morkUsage::GetGlobal()  // kGlobal safe at static init time
104 {
105   EnsureReadyStaticUsage();
106   return morkUsage_gGlobal;
107 }
108 
109 /*static*/
GetPool()110 const morkUsage& morkUsage::GetPool()  // kPool safe at static init time
111 {
112   EnsureReadyStaticUsage();
113   return morkUsage_gPool;
114 }
115 
116 /*static*/
GetNone()117 const morkUsage& morkUsage::GetNone()  // kNone safe at static init time
118 {
119   EnsureReadyStaticUsage();
120   return morkUsage_gNone;
121 }
122 
morkUsage()123 morkUsage::morkUsage() {
124   if (morkUsage_need_static_init()) {
125     morkUsage::EnsureReadyStaticUsage();
126   }
127 }
128 
morkUsage(mork_usage code)129 morkUsage::morkUsage(mork_usage code) : mUsage_Code(code) {
130   if (morkUsage_need_static_init()) {
131     morkUsage::EnsureReadyStaticUsage();
132   }
133 }
134 
135 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
136 
MakeNew(size_t inSize,nsIMdbHeap & ioHeap,morkEnv * ev)137 /*static*/ void* morkNode::MakeNew(size_t inSize, nsIMdbHeap& ioHeap,
138                                    morkEnv* ev) {
139   void* node = 0;
140   ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**)&node);
141   if (!node) ev->OutOfMemoryError();
142 
143   return node;
144 }
145 
ZapOld(morkEnv * ev,nsIMdbHeap * ioHeap)146 /*public non-poly*/ void morkNode::ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap) {
147   if (this->IsNode()) {
148     mork_usage usage = mNode_Usage;  // mNode_Usage before ~morkNode
149     this->morkNode::~morkNode();     // first call polymorphic destructor
150     if (ioHeap)                      // was this node heap allocated?
151       ioHeap->Free(ev->AsMdbEnv(), this);
152     else if (usage == morkUsage_kPool)  // mNode_Usage before ~morkNode
153     {
154       morkHandle* h = (morkHandle*)this;
155       if (h->IsHandle() && h->GoodHandleTag()) {
156         if (h->mHandle_Face) {
157           if (ev->mEnv_HandlePool)
158             ev->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face);
159           else if (h->mHandle_Env && h->mHandle_Env->mEnv_HandlePool)
160             h->mHandle_Env->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face);
161         } else
162           ev->NilPointerError();
163       }
164     }
165   } else
166     this->NonNodeError(ev);
167 }
168 
CloseMorkNode(morkEnv * ev)169 /*public virtual*/ void morkNode::CloseMorkNode(
170     morkEnv* ev)  // CloseNode() only if open
171 {
172   if (this->IsOpenNode()) {
173     this->MarkClosing();
174     this->CloseNode(ev);
175     this->MarkShut();
176   }
177 }
178 NS_IMETHODIMP
CloseMdbObject(nsIMdbEnv * mev)179 morkNode::CloseMdbObject(nsIMdbEnv* mev) {
180   return morkNode::CloseMdbObject((morkEnv*)mev);
181 }
182 
CloseMdbObject(morkEnv * ev)183 nsresult morkNode::CloseMdbObject(morkEnv* ev) {
184   // if only one ref, Handle_CutStrongRef will clean up better.
185   if (mNode_Uses == 1)
186     // XXX Casting mork_uses to nsresult
187     return static_cast<nsresult>(CutStrongRef(ev));
188 
189   nsresult outErr = NS_OK;
190 
191   if (IsNode() && IsOpenNode()) {
192     if (ev) {
193       CloseMorkNode(ev);
194       outErr = ev->AsErr();
195     }
196   }
197   return outErr;
198 }
199 
200 /*public virtual*/
~morkNode()201 morkNode::~morkNode()  // assert that CloseNode() executed earlier
202 {
203   MORK_ASSERT(this->IsShutNode() ||
204               IsDeadNode());  // sometimes we call destructor explicitly w/o
205                               // freeing object.
206   mNode_Access = morkAccess_kDead;
207   mNode_Usage = morkUsage_kNone;
208 }
209 
210 /*public virtual*/
211 // void CloseMorkNode(morkEnv* ev) = 0; // CloseNode() only if open
212 // CloseMorkNode() is the polymorphic close method called when uses==0,
213 // which must do NOTHING at all when IsOpenNode() is not true.  Otherwise,
214 // CloseMorkNode() should call a static close method specific to an object.
215 // Each such static close method should either call inherited static close
216 // methods, or else perform the consolidated effect of calling them, where
217 // subclasses should closely track any changes in base classes with care.
218 
219 /*public non-poly*/
morkNode(mork_usage inCode)220 morkNode::morkNode(mork_usage inCode)
221     : mNode_Heap(0),
222       mNode_Base(morkBase_kNode),
223       mNode_Derived(0)  // until subclass sets appropriately
224       ,
225       mNode_Access(morkAccess_kOpen),
226       mNode_Usage(inCode),
227       mNode_Mutable(morkAble_kEnabled),
228       mNode_Load(morkLoad_kClean),
229       mNode_Uses(1),
230       mNode_Refs(1) {}
231 
232 /*public non-poly*/
morkNode(const morkUsage & inUsage,nsIMdbHeap * ioHeap)233 morkNode::morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap)
234     : mNode_Heap(ioHeap),
235       mNode_Base(morkBase_kNode),
236       mNode_Derived(0)  // until subclass sets appropriately
237       ,
238       mNode_Access(morkAccess_kOpen),
239       mNode_Usage(inUsage.Code()),
240       mNode_Mutable(morkAble_kEnabled),
241       mNode_Load(morkLoad_kClean),
242       mNode_Uses(1),
243       mNode_Refs(1) {
244   if (!ioHeap && mNode_Usage == morkUsage_kHeap) MORK_ASSERT(ioHeap);
245 }
246 
247 /*public non-poly*/
morkNode(morkEnv * ev,const morkUsage & inUsage,nsIMdbHeap * ioHeap)248 morkNode::morkNode(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap)
249     : mNode_Heap(ioHeap),
250       mNode_Base(morkBase_kNode),
251       mNode_Derived(0)  // until subclass sets appropriately
252       ,
253       mNode_Access(morkAccess_kOpen),
254       mNode_Usage(inUsage.Code()),
255       mNode_Mutable(morkAble_kEnabled),
256       mNode_Load(morkLoad_kClean),
257       mNode_Uses(1),
258       mNode_Refs(1) {
259   if (!ioHeap && mNode_Usage == morkUsage_kHeap) {
260     this->NilHeapError(ev);
261   }
262 }
263 
RefsUnderUsesWarning(morkEnv * ev) const264 /*protected non-poly*/ void morkNode::RefsUnderUsesWarning(morkEnv* ev) const {
265   ev->NewError("mNode_Refs < mNode_Uses");
266 }
267 
NonNodeError(morkEnv * ev) const268 /*protected non-poly*/ void morkNode::NonNodeError(
269     morkEnv* ev) const  // called when IsNode() is false
270 {
271   ev->NewError("non-morkNode");
272 }
273 
NonOpenNodeError(morkEnv * ev) const274 /*protected non-poly*/ void morkNode::NonOpenNodeError(
275     morkEnv* ev) const  // when IsOpenNode() is false
276 {
277   ev->NewError("non-open-morkNode");
278 }
279 
NonMutableNodeError(morkEnv * ev) const280 /*protected non-poly*/ void morkNode::NonMutableNodeError(
281     morkEnv* ev) const  // when IsMutable() is false
282 {
283   ev->NewError("non-mutable-morkNode");
284 }
285 
NilHeapError(morkEnv * ev) const286 /*protected non-poly*/ void morkNode::NilHeapError(
287     morkEnv* ev) const  // zero mNode_Heap w/ kHeap usage
288 {
289   ev->NewError("nil mNode_Heap");
290 }
291 
RefsOverflowWarning(morkEnv * ev) const292 /*protected non-poly*/ void morkNode::RefsOverflowWarning(
293     morkEnv* ev) const  // mNode_Refs overflow
294 {
295   ev->NewWarning("mNode_Refs overflow");
296 }
297 
UsesOverflowWarning(morkEnv * ev) const298 /*protected non-poly*/ void morkNode::UsesOverflowWarning(
299     morkEnv* ev) const  // mNode_Uses overflow
300 {
301   ev->NewWarning("mNode_Uses overflow");
302 }
303 
RefsUnderflowWarning(morkEnv * ev) const304 /*protected non-poly*/ void morkNode::RefsUnderflowWarning(
305     morkEnv* ev) const  // mNode_Refs underflow
306 {
307   ev->NewWarning("mNode_Refs underflow");
308 }
309 
UsesUnderflowWarning(morkEnv * ev) const310 /*protected non-poly*/ void morkNode::UsesUnderflowWarning(
311     morkEnv* ev) const  // mNode_Uses underflow
312 {
313   ev->NewWarning("mNode_Uses underflow");
314 }
315 
CloseNode(morkEnv * ev)316 /*public non-poly*/ void morkNode::CloseNode(
317     morkEnv* ev)  // called by CloseMorkNode();
318 {
319   if (this->IsNode())
320     this->MarkShut();
321   else
322     this->NonNodeError(ev);
323 }
324 
325 extern void  // utility method very similar to morkNode::SlotStrongNode():
nsIMdbFile_SlotStrongFile(nsIMdbFile * self,morkEnv * ev,nsIMdbFile ** ioSlot)326 nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot)
327 // If *ioSlot is non-nil, that file is released by CutStrongRef() and
328 // then zeroed out.  Then if self is non-nil, this is acquired by
329 // calling AddStrongRef(), and if the return value shows success,
330 // then self is put into slot *ioSlot.  Note self can be nil, so we take
331 // expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'.
332 {
333   nsIMdbFile* file = *ioSlot;
334   if (self != file) {
335     if (file) {
336       *ioSlot = 0;
337       NS_RELEASE(file);
338     }
339     if (self && ev->Good()) NS_ADDREF(*ioSlot = self);
340   }
341 }
342 
343 void  // utility method very similar to morkNode::SlotStrongNode():
nsIMdbHeap_SlotStrongHeap(nsIMdbHeap * self,morkEnv * ev,nsIMdbHeap ** ioSlot)344 nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot)
345 // If *ioSlot is non-nil, that heap is released by CutStrongRef() and
346 // then zeroed out.  Then if self is non-nil, self is acquired by
347 // calling AddStrongRef(), and if the return value shows success,
348 // then self is put into slot *ioSlot.  Note self can be nil, so we
349 // permit expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'.
350 {
351   nsIMdbHeap* heap = *ioSlot;
352   if (self != heap) {
353     if (heap) *ioSlot = 0;
354 
355     if (self && ev->Good()) *ioSlot = self;
356   }
357 }
358 
SlotStrongNode(morkNode * me,morkEnv * ev,morkNode ** ioSlot)359 /*public static*/ void morkNode::SlotStrongNode(morkNode* me, morkEnv* ev,
360                                                 morkNode** ioSlot)
361 // If *ioSlot is non-nil, that node is released by CutStrongRef() and
362 // then zeroed out.  Then if me is non-nil, this is acquired by
363 // calling AddStrongRef(), and if positive is returned to show success,
364 // then me is put into slot *ioSlot.  Note me can be nil, so we take
365 // expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'.
366 {
367   morkNode* node = *ioSlot;
368   if (me != node) {
369     if (node) {
370       // what if this nulls out the ev and causes asserts?
371       // can we move this after the CutStrongRef()?
372       *ioSlot = 0;
373       node->CutStrongRef(ev);
374     }
375     if (me && me->AddStrongRef(ev)) *ioSlot = me;
376   }
377 }
378 
SlotWeakNode(morkNode * me,morkEnv * ev,morkNode ** ioSlot)379 /*public static*/ void morkNode::SlotWeakNode(morkNode* me, morkEnv* ev,
380                                               morkNode** ioSlot)
381 // If *ioSlot is non-nil, that node is released by CutWeakRef() and
382 // then zeroed out.  Then if me is non-nil, this is acquired by
383 // calling AddWeakRef(), and if positive is returned to show success,
384 // then me is put into slot *ioSlot.  Note me can be nil, so we
385 // expression 'morkNode::SlotWeakNode((morkNode*) 0, ev, &slot)'.
386 {
387   morkNode* node = *ioSlot;
388   if (me != node) {
389     if (node) {
390       *ioSlot = 0;
391       node->CutWeakRef(ev);
392     }
393     if (me && me->AddWeakRef(ev)) *ioSlot = me;
394   }
395 }
396 
AddStrongRef(morkEnv * ev)397 /*public non-poly*/ mork_uses morkNode::AddStrongRef(morkEnv* ev) {
398   mork_uses outUses = 0;
399   if (this->IsNode()) {
400     mork_uses uses = mNode_Uses;
401     mork_refs refs = mNode_Refs;
402     if (refs < uses)  // need to fix broken refs/uses relation?
403     {
404       this->RefsUnderUsesWarning(ev);
405       mNode_Refs = mNode_Uses = refs = uses;
406     }
407     if (refs < morkNode_kMaxRefCount)  // not too great?
408     {
409       mNode_Refs = ++refs;
410       mNode_Uses = ++uses;
411     } else
412       this->RefsOverflowWarning(ev);
413 
414     outUses = uses;
415   } else
416     this->NonNodeError(ev);
417   return outUses;
418 }
419 
cut_use_count(morkEnv * ev)420 /*private non-poly*/ mork_bool morkNode::cut_use_count(
421     morkEnv* ev)  // just one part of CutStrongRef()
422 {
423   mork_bool didCut = morkBool_kFalse;
424   if (this->IsNode()) {
425     mork_uses uses = mNode_Uses;
426     if (uses)  // not yet zero?
427       mNode_Uses = --uses;
428     else
429       this->UsesUnderflowWarning(ev);
430 
431     didCut = morkBool_kTrue;
432     if (!mNode_Uses)  // last use gone? time to close node?
433     {
434       if (this->IsOpenNode()) {
435         if (!mNode_Refs)  // no outstanding reference?
436         {
437           this->RefsUnderflowWarning(ev);
438           ++mNode_Refs;  // prevent potential crash during close
439         }
440         this->CloseMorkNode(ev);  // polymorphic self close
441         // (Note CutNode() is not polymorphic -- so don't call that.)
442       }
443     }
444   } else
445     this->NonNodeError(ev);
446   return didCut;
447 }
448 
CutStrongRef(morkEnv * ev)449 /*public non-poly*/ mork_uses morkNode::CutStrongRef(morkEnv* ev) {
450   mork_refs outRefs = 0;
451   if (this->IsNode()) {
452     if (this->cut_use_count(ev)) outRefs = this->CutWeakRef(ev);
453   } else
454     this->NonNodeError(ev);
455 
456   return outRefs;
457 }
458 
AddWeakRef(morkEnv * ev)459 /*public non-poly*/ mork_refs morkNode::AddWeakRef(morkEnv* ev) {
460   mork_refs outRefs = 0;
461   if (this->IsNode()) {
462     mork_refs refs = mNode_Refs;
463     if (refs < morkNode_kMaxRefCount)  // not too great?
464       mNode_Refs = ++refs;
465     else
466       this->RefsOverflowWarning(ev);
467 
468     outRefs = refs;
469   } else
470     this->NonNodeError(ev);
471 
472   return outRefs;
473 }
474 
CutWeakRef(morkEnv * ev)475 /*public non-poly*/ mork_refs morkNode::CutWeakRef(morkEnv* ev) {
476   mork_refs outRefs = 0;
477   if (this->IsNode()) {
478     mork_uses uses = mNode_Uses;
479     mork_refs refs = mNode_Refs;
480     if (refs)  // not yet zero?
481       mNode_Refs = --refs;
482     else
483       this->RefsUnderflowWarning(ev);
484 
485     if (refs < uses)  // need to fix broken refs/uses relation?
486     {
487       this->RefsUnderUsesWarning(ev);
488       mNode_Refs = mNode_Uses = refs = uses;
489     }
490 
491     outRefs = refs;
492     if (!refs)  // last reference gone? time to destroy node?
493       this->ZapOld(ev, mNode_Heap);  // self destroy, use this no longer
494   } else
495     this->NonNodeError(ev);
496 
497   return outRefs;
498 }
499 
500 static const char morkNode_kBroken[] = "broken";
501 
GetNodeAccessAsString() const502 /*public non-poly*/ const char* morkNode::GetNodeAccessAsString()
503     const  // e.g. "open", "shut", etc.
504 {
505   const char* outString = morkNode_kBroken;
506   switch (mNode_Access) {
507     case morkAccess_kOpen:
508       outString = "open";
509       break;
510     case morkAccess_kClosing:
511       outString = "closing";
512       break;
513     case morkAccess_kShut:
514       outString = "shut";
515       break;
516     case morkAccess_kDead:
517       outString = "dead";
518       break;
519   }
520   return outString;
521 }
522 
GetNodeUsageAsString() const523 /*public non-poly*/ const char* morkNode::GetNodeUsageAsString()
524     const  // e.g. "heap", "stack", etc.
525 {
526   const char* outString = morkNode_kBroken;
527   switch (mNode_Usage) {
528     case morkUsage_kHeap:
529       outString = "heap";
530       break;
531     case morkUsage_kStack:
532       outString = "stack";
533       break;
534     case morkUsage_kMember:
535       outString = "member";
536       break;
537     case morkUsage_kGlobal:
538       outString = "global";
539       break;
540     case morkUsage_kPool:
541       outString = "pool";
542       break;
543     case morkUsage_kNone:
544       outString = "none";
545       break;
546   }
547   return outString;
548 }
549 
550 // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
551