Fix and simplify memory management, init methods, other fixes.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/branches/nsxml_using_libxml2@34670 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Doug Simons 2012-01-30 19:28:26 +00:00
parent 4a7581308d
commit f754ea74cb
5 changed files with 265 additions and 88 deletions

View file

@ -1,3 +1,15 @@
2012-01-05 22:20-EST Doug Simons <doug.simons@testplant.com>
* Source/NSXMLPrivate.h
* Source/NSXMLDocument.m
* Source/NSXMLElement.m
* Source/NSXMLNode.m: Simplify the memory management housekeeping by
just calling a new _updateExternalRetains method after any operations
that might have changed the retains. This still needs some cleaning up,
but is working much better now.
Clean up and simplify some of the init methods.
Fix a few other things.
2012-01-29 12:28-EST Gregory John Casamento <greg.casamento@gmail.com>
* Source/NSXMLNode.m: Correct infinite loop in releaseExtraRetains.

View file

@ -30,7 +30,8 @@
#import "GSInternal.h"
GS_PRIVATE_INTERNAL(NSXMLDocument)
#import <Foundation/NSXMLParser.h>
//#import <Foundation/NSXMLParser.h>
#import <Foundation/NSError.h>
extern void clearPrivatePointers(xmlNodePtr aNode);
@ -116,15 +117,14 @@ extern void clearPrivatePointers(xmlNodePtr aNode);
{
if (NSXMLDocumentKind == kind)
{
/* Create holder for internal instance variables so that we'll have
* all our ivars available rather than just those of the superclass.
*/
xmlChar *version = (xmlChar *)"1.0";
GS_CREATE_INTERNAL(NSXMLDocument);
internal->node = xmlNewDoc(version);
MY_DOC->_private = (void *)self;
return [super initWithKind: kind options: theOptions];
}
else
{
[self release];
return [[NSXMLNode alloc] initWithKind: kind
options: theOptions];
}
return [super initWithKind: kind options: theOptions];
}
- (id) initWithRootElement: (NSXMLElement*)element
@ -161,26 +161,27 @@ extern void clearPrivatePointers(xmlNodePtr aNode);
format: @"[NSXMLDocument-%@] invalid argument",
NSStringFromSelector(_cmd)];
}
GS_CREATE_INTERNAL(NSXMLDocument)
if ((self = [super initWithKind: NSXMLDocumentKind options: 0]) != nil)
if ((self = [self initWithKind: NSXMLDocumentKind options: 0]) != nil)
{
const char *str = [string UTF8String];
char *url = NULL;
char *encoding = NULL; // "UTF8";
int options = XML_PARSE_NOERROR;
xmlDocPtr doc = NULL;
if (!(mask & NSXMLNodePreserveWhitespace))
options |= XML_PARSE_NOBLANKS;
//xmlKeepBlanksDefault(0);
GS_CREATE_INTERNAL(NSXMLDocument); // create internal ivars...
internal->node = xmlReadDoc((xmlChar *)str, url, encoding, options);
if(internal->node == NULL)
doc = xmlReadDoc((xmlChar *)str, url, encoding, options);
if(doc == NULL)
{
[self release];
self = nil;
[NSException raise:NSInvalidArgumentException
format:@"Cannot instantiate NSXMLDocument with invalid data"];
if (error != NULL)
*error = [NSError errorWithDomain:@"NSXMLErrorDomain" code:0 userInfo:nil];
//[NSException raise:NSInvalidArgumentException
// format:@"Cannot instantiate NSXMLDocument with invalid data"];
}
MY_DOC->_private = (void *)self;
[self _setNode:doc];
}
return self;
}

View file

@ -67,38 +67,34 @@ extern void clearPrivatePointers(xmlNodePtr aNode);
return [self initWithKind: NSXMLElementKind options: 0];
}
- (id) initWithKind: (NSXMLNodeKind)kind options: (NSUInteger)theOptions
{
if (NSXMLElementKind == kind)
{
if ((self = [super initWithKind:kind options:theOptions]))
{
internal->objectValue = @"";
}
return self;
}
else
{
[self release];
return [[NSXMLNode alloc] initWithKind: kind
options: theOptions];
}
}
- (id) initWithName: (NSString*)name
{
return [self initWithName: name URI: nil];
}
- (id) initWithKind: (NSXMLNodeKind)kind options: (NSUInteger)theOptions
{
if (NSXMLElementKind == kind)
{
/* Create holder for internal instance variables so that we'll have
* all our ivars available rather than just those of the superclass.
*/
NSString *name = @"";
GS_CREATE_INTERNAL(NSXMLElement)
internal->node = (void *)xmlNewNode(NULL,(xmlChar *)[name UTF8String]);
((xmlNodePtr)internal->node)->_private = self;
internal->objectValue = @"";
// return self;
}
return [super initWithKind: kind options: theOptions];
}
- (id) initWithName: (NSString*)name URI: (NSString*)URI
{
/* Create holder for internal instance variables so that we'll have
* all our ivars available rather than just those of the superclass.
*/
GS_CREATE_INTERNAL(NSXMLElement)
if ((self = [super initWithKind: NSXMLElementKind]) != nil)
{
internal->node = (void *)xmlNewNode(NULL,(xmlChar *)[name UTF8String]);
((xmlNodePtr)internal->node)->_private = self;
[self setName:name];
ASSIGNCOPY(internal->URI, URI);
internal->objectValue = @"";
}
@ -367,7 +363,7 @@ extern void clearPrivatePointers(xmlNodePtr aNode);
}
else if(index < childCount)
{
xmlAddNextSibling(curNode, childNode);
xmlAddPrevSibling(curNode, childNode);
}
[self _addSubNode:child];
@ -398,6 +394,7 @@ extern void clearPrivatePointers(xmlNodePtr aNode);
child = [[self children] objectAtIndex: index];
n = [child _node];
xmlUnlinkNode(n);
[self _removeSubNode:child];
}
- (void) setChildren: (NSArray*)children

View file

@ -101,16 +101,111 @@ GS_PRIVATE_INTERNAL(NSXMLNode)
return result;
}
- (int) _externalRetains
{
return internal->externalRetains;
}
- (int) verifyExternalRetains
{
int extraRetains = ([self retainCount] > 1 ? 1 : 0); // start with 1 or 0 for ourself
int index;
for (index = 0; index < [internal->subNodes count]; index++)
extraRetains += [[internal->subNodes objectAtIndex:index] _externalRetains];
return extraRetains;
}
- (void) _updateExternalRetains
{
xmlNodePtr pnode = MY_NODE->parent;
NSXMLNode *parent = (NSXMLNode *)(pnode ? pnode->_private : nil);
int oldCount = internal->externalRetains;
int extraRetains = ([self retainCount] > 1 ? 1 : 0); // start with 1 or 0 for ourself
int index;
for (index = 0; index < [internal->subNodes count]; index++)
extraRetains += [[internal->subNodes objectAtIndex:index] _externalRetains];
internal->externalRetains = extraRetains;
if (extraRetains != oldCount)
{
if (parent)
[parent _updateExternalRetains]; // tell our parent (if any) since our count has changed
else
{ // we're the root node of this tree, so retain or release ourself as needed
if (oldCount == 0 && extraRetains > 0)
{
[super retain];
internal->retainedSelf++;
//NSLog(@"RETAINED SELF %@ (%d)", self, internal->retainedSelf);
}
else if (oldCount > 0 && extraRetains == 0 && internal->retainedSelf)
{
internal->retainedSelf--;
//NSLog(@"RELEASED SELF %@ (%d)", self, internal->retainedSelf);
[super release];
}
}
}
else
{
if (!parent)
{
if (extraRetains > 0 && internal->retainedSelf == 0)
{
[super retain];
internal->retainedSelf++;
//NSLog(@"RETAINED SELF AFTER STATUS CHANGED %@ (%d)", self, internal->retainedSelf);
}
else if (extraRetains == 0 && internal->retainedSelf > 0)
{
internal->retainedSelf--;
//NSLog(@"RELEASED SELF AFTER STATUS CHANGED %@ (%d)", self, internal->retainedSelf);
[super release];
}
}
}
}
- (void) _passExternalRetainsTo:(NSXMLNode *)parent
{
// this object just became a subNode, so pass responsibility for external retains up the line
BOOL releaseSelf = (internal->externalRetains > 0); // if we've retained ourself
while (internal->externalRetains-- > 0)
// this object just became a subNode, so pass knowledge of external retains up the line
if (internal->externalRetains > 0)
{
[parent recordExternalRetain];
//NSLog(@"_passExternalRetainsTo:%@ (%d,%d) from %@ Start:(%d,%d)", parent, [parent _externalRetains], [parent verifyExternalRetains], self, internal->externalRetains, [self verifyExternalRetains]);
// if ([self retainCount] == 2)
// {
// internal->externalRetains--;
//NSLog(@"RELEASING TRICKY EXTRA RETAIN WHILE ADDING TO PARENT in %@ now: %d", self, internal->externalRetains);
// }
//[self _updateExternalRetains];
if (internal->retainedSelf)
{
[super release]; // we're no longer the root of our branch, so stop retaining ourself
internal->retainedSelf--;
//NSLog(@"RELEASED SELF %@ (%d) in _passExternal...", self, internal->retainedSelf);
}
[parent _updateExternalRetains];
//NSLog(@"DID _passExternalRetainsTo:%@ (%d,%d) from %@ End:(%d,%d)", parent, [parent _externalRetains], [parent verifyExternalRetains], self, internal->externalRetains, [self verifyExternalRetains]);
}
}
- (void) _removeExternalRetainsFrom:(NSXMLNode *)parent
{
// this object is no longer a subNode, so pass removal of external retains up the line
if (internal->externalRetains > 0)
{
//NSLog(@"_removeExternalRetainsTo:%@ from %@ Start: %d", parent, self, internal->externalRetains);
/// [parent releaseExternalRetain:internal->externalRetains];
if ([self retainCount] == 1)
{
internal->externalRetains++;
//NSLog(@"ADDED TRICKY EXTRA COUNT WHILE REMOVING FROM PARENT in %@ now: %d subNodes(low):%d", self, internal->externalRetains, [self verifyExternalRetains]);
}
[super retain]; // becoming detached, so retain ourself as the new root of our branch
internal->retainedSelf++;
//NSLog(@"RETAINED SELF %@ (%d) in _removeExternal...", self, internal->retainedSelf);
[parent _updateExternalRetains];
}
if (releaseSelf)
[self release];
}
- (void) _addSubNode:(NSXMLNode *)subNode
@ -126,7 +221,10 @@ GS_PRIVATE_INTERNAL(NSXMLNode)
- (void) _removeSubNode:(NSXMLNode *)subNode
{
[subNode retain]; // retain temporarily so we can safely remove from our subNodes list first
[internal->subNodes removeObjectIdenticalTo:subNode];
[subNode _removeExternalRetainsFrom:self];
[subNode release]; // release temporary hold
}
- (void) _createInternal
@ -537,53 +635,71 @@ NSArray *execute_xpath(NSXMLNode *node,
return c;
}
- (void) recordExternalRetain
/** these methods should go away now
- (void) recordExternalRetain:(int)count
{
id parent = [self parent];
if (parent)
[parent recordExternalRetain];
[parent recordExternalRetain:count];
else
{
if (internal->externalRetains == 0)
[super retain]; // the top of the tree retains itself whenever there are external retains anywhere
internal->externalRetains++;
if (count > 0 && internal->externalRetains == 0)
{
[super retain]; // the top of the tree retains itself whenever there are external retains anywhere
if ([self retainCount] == 2)
{
internal->externalRetains++;
NSLog(@"ADDED TRICKY EXTRA COUNT in %@ now: %d subNodes(OFF):%d", self, internal->externalRetains, [self verifyExternalRetains]);
}
}
}
internal->externalRetains += count;
NSLog(@"recordExternalRetain in %@ now: %d subNodes:%d", self, internal->externalRetains, [self verifyExternalRetains]);
}
- (void) releaseExternalRetain
- (void) releaseExternalRetain:(int)count
{
id parent = [self parent];
internal->externalRetains -= count;
if (internal->externalRetains <0)
NSLog(@"ExternalRetains going NEGATIVE: %d in %@", internal->externalRetains - count, self);
NSLog(@"releaseExternalRetain in %@ now: %d", self, internal->externalRetains);
if (parent)
[parent releaseExternalRetain];
[parent releaseExternalRetain:count];
else
{
if (internal->externalRetains <=0)
NSLog(@"ExternalRetains going NEGATIVE: %d in %@", internal->externalRetains - 1, self);
internal->externalRetains--;
if (internal->externalRetains == 0)
// check for tricky condition where our only "external" retain is from ourself and is about to go away
if (count > 0 && internal->externalRetains == 1 && [self retainCount] == 2)
{
internal->externalRetains--;
NSLog(@"RELEASING TRICKY EXTRA RETAIN in %@ now: %d", self, internal->externalRetains);
}
if (count > 0 && internal->externalRetains == 0)
[super release]; // the top of the tree retains itself whenever there are external retains anywhere
}
}
**/
- (id) retain
{
if ([self retainCount] == 1)
[super retain]; // do this first
if ([self retainCount] == 2)
{
[self recordExternalRetain];
[self _updateExternalRetains]; //[self recordExternalRetain:1];
}
return [super retain];
return self;
}
- (void) release
{
if(GS_EXISTS_INTERNAL)
if ([self retainCount] == 2)
{
if ([self retainCount] == [internal->subNodes count]) // 2)
{
[self releaseExternalRetain];
}
[super release];
[self _updateExternalRetains]; //[self releaseExternalRetain:1];
}
[super release];
else
[super release];
}
- (void) dealloc
@ -594,6 +710,8 @@ if (internal->externalRetains <=0)
[internal->URI release];
[internal->objectValue release];
[internal->subNodes release];
if (node)
node->_private = NULL;
if (node && node->parent == NULL)
{
xmlFree(node); // the top level node frees the entire tree
@ -608,10 +726,29 @@ if (internal->externalRetains <=0)
xmlNodePtr node = (xmlNodePtr)(internal->node);
if(node)
{
int extraRetains = 0;
xmlNodePtr parentNode = node->parent;
NSXMLNode *parent = (parentNode ? parentNode->_private : nil); // get our parent object if it exists
[parent _removeSubNode:self];
xmlUnlinkNode(node);
xmlUnlinkNode(node); // separate our node from its parent and siblings
if (parent)
{
// transfer extra retains of this branch from our parent to ourself
extraRetains = internal->externalRetains; //[self verifyExternalRetains];
if (extraRetains)
{
/// [parent releaseExternalRetain:extraRetains];
if ([self retainCount] == 1)
{
internal->externalRetains++;
//NSLog(@"ADDED TRICKY EXTRA COUNT WHILE DETACHING in %@ now: %d subNodes(low):%d", self, internal->externalRetains, [self verifyExternalRetains]);
}
[super retain]; //[self recordExternalRetain:extraRetains];
internal->retainedSelf++;
//NSLog(@"RETAINED SELF %@ (%d) in detach", self, internal->retainedSelf);
}
[parent _removeSubNode:self];
}
//NSLog(@"DETACHED %@ from %@ and transferred extra retains: %d", self, parent, extraRetains);
}
}
@ -659,33 +796,75 @@ if (internal->externalRetains <=0)
switch (kind)
{
case NSXMLDocumentKind:
node = xmlNewDoc((xmlChar *)"1.0");
theSubclass = [NSXMLDocument class];
break;
case NSXMLInvalidKind:
case NSXMLElementKind:
node = xmlNewNode(NULL,(xmlChar *)"");
theSubclass = [NSXMLElement class];
break;
case NSXMLDTDKind:
node = xmlNewDtd(NULL, (xmlChar *)"", (xmlChar *)"",(xmlChar *)"");
theSubclass = [NSXMLDTD class];
break;
case NSXMLEntityDeclarationKind:
case NSXMLElementDeclarationKind:
case NSXMLNotationDeclarationKind:
node = xmlNewNode(NULL, (xmlChar *)"");
// ((xmlNodePtr)node)->type = XML_ATTRIBUTE_DECL;
theSubclass = [NSXMLDTDNode class];
break;
case NSXMLAttributeDeclarationKind:
[self release];
return nil;
break;
case NSXMLProcessingInstructionKind:
case NSXMLCommentKind:
case NSXMLTextKind:
case NSXMLNamespaceKind:
case NSXMLAttributeKind:
break;
default:
kind = NSXMLInvalidKind;
theSubclass = [NSXMLElement class];
break;
}
/*
* Check whether we are already initializing an instance of the given
* subclass. If we are not, release ourselves and allocate a subclass instance
* instead.
*/
if (NO == [self isKindOfClass: theSubclass])
{
[self release];
return [[theSubclass alloc] initWithKind: kind
options: theOptions];
}
switch (kind)
{
case NSXMLDocumentKind:
node = xmlNewDoc((xmlChar *)"1.0");
break;
case NSXMLInvalidKind:
case NSXMLElementKind:
node = xmlNewNode(NULL,(xmlChar *)"");
break;
case NSXMLDTDKind:
node = xmlNewDtd(NULL, (xmlChar *)"", (xmlChar *)"",(xmlChar *)"");
break;
case NSXMLEntityDeclarationKind:
case NSXMLElementDeclarationKind:
case NSXMLNotationDeclarationKind:
node = xmlNewNode(NULL, (xmlChar *)"");
break;
case NSXMLProcessingInstructionKind:
node = xmlNewPI((xmlChar *)"", (xmlChar *)"");
break;
@ -694,7 +873,6 @@ if (internal->externalRetains <=0)
node = xmlNewComment((xmlChar *)"");
break;
case NSXMLTextKind:
node = xmlNewText((xmlChar *)"");
break;
@ -711,21 +889,9 @@ if (internal->externalRetains <=0)
break;
}
/*
* Check whether we are already initializing an instance of the given
* subclass. If we are not, release ourselves and allocate a subclass instance
* instead.
*/
if (NO == [self isKindOfClass: theSubclass])
{
[self release];
return [[theSubclass alloc] initWithKind: kind
options: theOptions];
}
/* Create holder for internal instance variables if needed.
*/
GS_CREATE_INTERNAL(NSXMLNode);
[self _createInternal];
/* Create libxml object to go with it...
*/

View file

@ -114,6 +114,7 @@ StringFromXMLString(const unsigned char *bytes, unsigned length)
NSMutableArray *subNodes; \
NSString *xpath; \
int externalRetains; \
int retainedSelf; \
/* When using the non-fragile ABI, the instance variables are exposed to the