/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* Object.m Copyright 1988-1996 NeXT Software, Inc. */ #import #import "objc-private.h" #import #import #import #import #import OBJC_EXPORT id (*_cvtToId)(const char *); OBJC_EXPORT id (*_poseAs)(); // Error Messages static const char _errNoMem[] = "failed -- out of memory(%s, %u)", _errReAllocNil[] = "reallocating nil object", _errReAllocFreed[] = "reallocating freed object", _errReAllocTooSmall[] = "(%s, %u) requested size too small", _errShouldHaveImp[] = "should have implemented the '%s' method.", _errShouldNotImp[] = "should NOT have implemented the '%s' method.", _errLeftUndone[] = "method '%s' not implemented", _errBadSel[] = "method %s given invalid selector %s", _errDoesntRecognize[] = "does not recognize selector %c%s"; @implementation Object + initialize { return self; } - awake { return self; } + poseAs: aFactory { return (*_poseAs)(self, aFactory); } + new { id newObject = (*_alloc)((Class)self, 0); struct objc_class * metaClass = ((struct objc_class *) self)->isa; if (metaClass->version > 1) return [newObject init]; else return newObject; } + alloc { return (*_zoneAlloc)((Class)self, 0, malloc_default_zone()); } + allocFromZone:(void *) z { return (*_zoneAlloc)((Class)self, 0, z); } - init { return self; } - (const char *)name { return ((struct objc_class *)isa)->name; } + (const char *)name { return ((struct objc_class *)self)->name; } - (unsigned)hash { return ((uarith_t)self) >> 2; } - (BOOL)isEqual:anObject { return anObject == self; } - free { return (*_dealloc)(self); } + free { return nil; } - self { return self; } - class { return (id)isa; } + class { return self; } - (void *)zone { void *z = malloc_zone_from_ptr(self); return z ? z : malloc_default_zone(); } + superclass { return ((struct objc_class *)self)->super_class; } - superclass { return ((struct objc_class *)isa)->super_class; } + (int) version { struct objc_class * class = (struct objc_class *) self; return class->version; } + setVersion: (int) aVersion { struct objc_class * class = (struct objc_class *) self; class->version = aVersion; return self; } - (BOOL)isKindOf:aClass { register Class cls; for (cls = isa; cls; cls = ((struct objc_class *)cls)->super_class) if (cls == (Class)aClass) return YES; return NO; } - (BOOL)isMemberOf:aClass { return isa == (Class)aClass; } - (BOOL)isKindOfClassNamed:(const char *)aClassName { register Class cls; for (cls = isa; cls; cls = ((struct objc_class *)cls)->super_class) if (strcmp(aClassName, ((struct objc_class *)cls)->name) == 0) return YES; return NO; } - (BOOL)isMemberOfClassNamed:(const char *)aClassName { return strcmp(aClassName, ((struct objc_class *)isa)->name) == 0; } + (BOOL)instancesRespondTo:(SEL)aSelector { return class_respondsToMethod((Class)self, aSelector); } - (BOOL)respondsTo:(SEL)aSelector { return class_respondsToMethod(isa, aSelector); } - copy { return [self copyFromZone: [self zone]]; } - copyFromZone:(void *)z { return (*_zoneCopy)(self, 0, z); } - (IMP)methodFor:(SEL)aSelector { return class_lookupMethod(isa, aSelector); } + (IMP)instanceMethodFor:(SEL)aSelector { return class_lookupMethod(self, aSelector); } #if defined(__alpha__) #define MAX_RETSTRUCT_SIZE 256 typedef struct _foolGCC { char c[MAX_RETSTRUCT_SIZE]; } _variableStruct; typedef _variableStruct (*callReturnsStruct)(); OBJC_EXPORT long sizeOfReturnedStruct(char **); long sizeOfType(char **pp) { char *p = *pp; long stack_size = 0, n = 0; switch(*p) { case 'c': case 'C': stack_size += sizeof(char); // Alignment ? break; case 's': case 'S': stack_size += sizeof(short);// Alignment ? break; case 'i': case 'I': case '!': stack_size += sizeof(int); break; case 'l': case 'L': stack_size += sizeof(long int); break; case 'f': stack_size += sizeof(float); break; case 'd': stack_size += sizeof(double); break; case '*': case ':': case '@': case '%': stack_size += sizeof(char*); break; case '{': stack_size += sizeOfReturnedStruct(&p); while(*p!='}') p++; break; case '[': p++; while(isdigit(*p)) n = 10 * n + (*p++ - '0'); stack_size += (n * sizeOfType(&p)); break; default: break; } *pp = p; return stack_size; } long sizeOfReturnedStruct(char **pp) { char *p = *pp; long stack_size = 0, n = 0; while(p!=NULL && *++p!='=') ; // skip the struct name while(p!=NULL && *++p!='}') stack_size += sizeOfType(&p); return stack_size + 8; // Add 8 as a 'forfait value' // to take alignment into account } - perform:(SEL)aSelector { char *p; long stack_size; _variableStruct *dummyRetVal; Method method; if (aSelector) { method = class_getInstanceMethod((Class)self->isa, aSelector); if(method==NULL) method = class_getClassMethod((Class)self->isa, aSelector); if(method!=NULL) { p = &method->method_types[0]; if(*p=='{') { // Method returns a structure stack_size = sizeOfReturnedStruct(&p); if(stack_sizeisa, aSelector); if(method==NULL) method = class_getClassMethod((Class)self->isa, aSelector); if(method!=NULL) { p = &method->method_types[0]; if(*p=='{') { // Method returns a structure stack_size = sizeOfReturnedStruct(&p); if(stack_sizeisa, aSelector); if(method==NULL) method = class_getClassMethod((Class)self->isa, aSelector); if(method!=NULL) { p = &method->method_types[0]; if(*p=='{') { // Method returns a structure stack_size = sizeOfReturnedStruct(&p); if(stack_sizei25){ // Calculate size of the marg_list from the method's // signature. This looks for the method in self // and its superclasses. size = [self methodArgSize: sel]; // If neither self nor its superclasses implement // the method, forward the message because self // might know someone who does. This is a // "chained" forward... if (! size) return [self forward: sel: args]; // Message self with the specified selector and arguments return objc_msgSendv (self, sel, size, args); } // Look for instance method in self's class and superclasses method = class_getInstanceMethod((Class)self->isa,sel); // Look for class method in self's class and superclass if(method==NULL) method = class_getClassMethod((Class)self->isa,sel); // If neither self nor its superclasses implement // the method, forward the message because self // might know someone who does. This is a // "chained" forward... if(method==NULL) return [self forward: sel: args]; // Calculate size of the marg_list from the method's // signature. size = method_getSizeOfArguments(method); // Ready to send message now if the return type // is not a structure p = &method->method_types[0]; if(*p!='{') return objc_msgSendv(self, sel, size, args); // Method returns a structure stack_size = sizeOfReturnedStruct(&p); if(stack_size>=MAX_RETSTRUCT_SIZE) scratchMemP = (char*)malloc(stack_size); else scratchMemP = &scratchMem[0]; // Set i25 so objc_msgSendv will know that method returns a structure ((_m_args_p)args)->i25 = 1; // Set first param of method to be called to safe return address ((_m_args_p)args)->i16 = (unsigned long int) scratchMemP; objc_msgSendv(self, sel, size, args); if(stack_size>=MAX_RETSTRUCT_SIZE) free(scratchMemP); return (id)NULL; } #else - performv: (SEL) sel : (marg_list) args { unsigned size; #if hppa && 0 void *ret; // Save ret0 so methods that return a struct might work. asm("copy %%r28, %0": "=r"(ret): ); #endif hppa // Messages to nil object always return nil if (! self) return nil; // Calculate size of the marg_list from the method's // signature. This looks for the method in self // and its superclasses. size = [self methodArgSize: sel]; // If neither self nor its superclasses implement // it, forward the message because self might know // someone who does. This is a "chained" forward... if (! size) return [self forward: sel: args]; #if hppa && 0 // Unfortunately, it looks like the compiler puts something else in // r28 right after this instruction, so this is all for naught. asm("copy %0, %%r28": : "r"(ret)); #endif hppa // Message self with the specified selector and arguments return objc_msgSendv (self, sel, size, args); } #endif /* Testing protocol conformance */ - (BOOL) conformsTo: (Protocol *)aProtocolObj { return [(id)isa conformsTo:aProtocolObj]; } + (BOOL) conformsTo: (Protocol *)aProtocolObj { struct objc_class * class; for (class = self; class; class = class->super_class) { if (class->isa->version >= 3) { struct objc_protocol_list *protocols = class->protocols; while (protocols) { int i; for (i = 0; i < protocols->count; i++) { Protocol *p = protocols->list[i]; if ([p conformsTo:aProtocolObj]) return YES; } if (class->isa->version <= 4) break; protocols = protocols->next; } } } return NO; } /* Looking up information for a method */ - (struct objc_method_description *) descriptionForMethod:(SEL)aSelector { struct objc_class * cls; struct objc_method_description *m; /* Look in the protocols first. */ for (cls = isa; cls; cls = cls->super_class) { if (cls->isa->version >= 3) { struct objc_protocol_list *protocols = cls->protocols; while (protocols) { int i; for (i = 0; i < protocols->count; i++) { Protocol *p = protocols->list[i]; if (ISMETA (cls)) m = [p descriptionForClassMethod:aSelector]; else m = [p descriptionForInstanceMethod:aSelector]; if (m) { return m; } } if (cls->isa->version <= 4) break; protocols = protocols->next; } } } /* Then try the class implementations. */ for (cls = isa; cls; cls = cls->super_class) { void *iterator = 0; int i; struct objc_method_list *mlist; while ( (mlist = class_nextMethodList( cls, &iterator )) ) { for (i = 0; i < mlist->method_count; i++) if (mlist->method_list[i].method_name == aSelector) { struct objc_method_description *m; m = (struct objc_method_description *)&mlist->method_list[i]; return m; } } } return 0; } + (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSelector { struct objc_class * cls; /* Look in the protocols first. */ for (cls = self; cls; cls = cls->super_class) { if (cls->isa->version >= 3) { struct objc_protocol_list *protocols = cls->protocols; while (protocols) { int i; for (i = 0; i < protocols->count; i++) { Protocol *p = protocols->list[i]; struct objc_method_description *m; if ((m = [p descriptionForInstanceMethod:aSelector])) return m; } if (cls->isa->version <= 4) break; protocols = protocols->next; } } } /* Then try the class implementations. */ for (cls = self; cls; cls = cls->super_class) { void *iterator = 0; int i; struct objc_method_list *mlist; while ( (mlist = class_nextMethodList( cls, &iterator )) ) { for (i = 0; i < mlist->method_count; i++) if (mlist->method_list[i].method_name == aSelector) { struct objc_method_description *m; m = (struct objc_method_description *)&mlist->method_list[i]; return m; } } } return 0; } /* Obsolete methods (for binary compatibility only). */ + superClass { return [self superclass]; } - superClass { return [self superclass]; } - (BOOL)isKindOfGivenName:(const char *)aClassName { return [self isKindOfClassNamed: aClassName]; } - (BOOL)isMemberOfGivenName:(const char *)aClassName { return [self isMemberOfClassNamed: aClassName]; } - (struct objc_method_description *) methodDescFor:(SEL)aSelector { return [self descriptionForMethod: aSelector]; } + (struct objc_method_description *) instanceMethodDescFor:(SEL)aSelector { return [self descriptionForInstanceMethod: aSelector]; } - findClass:(const char *)aClassName { return (*_cvtToId)(aClassName); } - shouldNotImplement:(SEL)aSelector { return [self error:_errShouldNotImp, sel_getName(aSelector)]; } @end static id _internal_object_copyFromZone(Object *anObject, unsigned nBytes, void *z) { id obj; register unsigned siz; if (anObject == nil) return nil; obj = (*_zoneAlloc)(anObject->isa, nBytes, z); siz = ((struct objc_class *)anObject->isa)->instance_size + nBytes; bcopy((const char*)anObject, (char*)obj, siz); return obj; } static id _internal_object_copy(Object *anObject, unsigned nBytes) { void *z= malloc_zone_from_ptr(anObject); return _internal_object_copyFromZone(anObject, nBytes, z ? z : malloc_default_zone()); } static id _internal_object_dispose(Object *anObject) { if (anObject==nil) return nil; object_cxxDestruct((id)anObject); anObject->isa = _objc_getFreedObjectClass (); free(anObject); return nil; } static id _internal_object_reallocFromZone(Object *anObject, unsigned nBytes, void *z) { Object *newObject; struct objc_class * tmp; if (anObject == nil) __objc_error(nil, _errReAllocNil, 0); if (anObject->isa == _objc_getFreedObjectClass ()) __objc_error(anObject, _errReAllocFreed, 0); if (nBytes < ((struct objc_class *)anObject->isa)->instance_size) __objc_error(anObject, _errReAllocTooSmall, object_getClassName(anObject), nBytes); // Make sure not to modify space that has been declared free tmp = anObject->isa; anObject->isa = _objc_getFreedObjectClass (); newObject = (Object*)malloc_zone_realloc(z, (void*)anObject, (size_t)nBytes); if (newObject) { newObject->isa = tmp; return newObject; } else { __objc_error(anObject, _errNoMem, object_getClassName(anObject), nBytes); return nil; } } static id _internal_object_realloc(Object *anObject, unsigned nBytes) { void *z= malloc_zone_from_ptr(anObject); return _internal_object_reallocFromZone(anObject, nBytes, z ? z : malloc_default_zone()); } /* Functional Interface to system primitives */ id object_copy(Object *anObject, unsigned nBytes) { return (*_copy)(anObject, nBytes); } id object_copyFromZone(Object *anObject, unsigned nBytes, void *z) { return (*_zoneCopy)(anObject, nBytes, z); } id object_dispose(Object *anObject) { return (*_dealloc)(anObject); } id object_realloc(Object *anObject, unsigned nBytes) { return (*_realloc)(anObject, nBytes); } id object_reallocFromZone(Object *anObject, unsigned nBytes, void *z) { return (*_zoneRealloc)(anObject, nBytes, z); } Ivar object_setInstanceVariable(id obj, const char *name, void *value) { Ivar ivar = 0; if (obj && name) { if ((ivar = class_getInstanceVariable(((Object*)obj)->isa, name))) { objc_assign_ivar((id)value, obj, ivar->ivar_offset); } } return ivar; } Ivar object_getInstanceVariable(id obj, const char *name, void **value) { Ivar ivar = 0; if (obj && name) { void **ivaridx; if ((ivar = class_getInstanceVariable(((Object*)obj)->isa, name))) { ivaridx = (void **)((char *)obj + ivar->ivar_offset); *value = *ivaridx; } else *value = 0; } return ivar; } id (*_copy)(id, unsigned) = _internal_object_copy; id (*_realloc)(id, unsigned) = _internal_object_realloc; id (*_dealloc)(id) = _internal_object_dispose; id (*_cvtToId)(const char *) = objc_lookUpClass; SEL (*_cvtToSel)(const char *) = sel_getUid; void (*_error)() = (void(*)())_objc_error; id (*_zoneCopy)(id, unsigned, void *) = _internal_object_copyFromZone; id (*_zoneRealloc)(id, unsigned, void *) = _internal_object_reallocFromZone;