This is objc.m in view mode; [Download] [Up]
#import "gdb.h" #import "objc.h" #import "objfiles.h" #import "symfile.h" #import <mach-o/ldsyms.h> #import <stdio.h> #import <stdlib.h> #import <mach-o/loader.h> #import <objc/hashtable.h> #import <objc/maptable.h> #import <objc/Protocol.h> int forceRegionManager = 0; #define currentManager (inferior_task ? (RelocManager *)regionManager : (RelocManager *)manager) #define WARPG(p) \ ((typeof(p))[currentManager pointerFor: (p) withSize: sizeof(*(p))]) extern RegionManager *regionManager; typedef struct _ClassImp { char *className; IMP function; BOOL isFactory; struct _ClassImp *next; } ClassImp; static NXHashTable *selectorHash; static NXMapTable *classMap; typedef enum _OnlyOne {ANYTHING, INSTANCEONLY, CLASSONLY} OnlyOne; static inline BOOL streq(str1, str2) char *str1, *str2; { return ((*str1 == *str2) && (strcmp(str1, str2) == 0)); } #include <sys/time.h> #include <sys/resource.h> #include <time.h> /* Return time used so far, in microseconds. */ int gettime () { struct rusage rusage; getrusage (0, &rusage); return (rusage.ru_utime.tv_sec * 1000000 + rusage.ru_utime.tv_usec + rusage.ru_stime.tv_sec * 1000000 + rusage.ru_stime.tv_usec); } void print_time (str, total, tv) char *str; int total; struct timeval *tv; { extern char **NXArgv; fprintf (stderr, "time to `%s' for `%s':\n\t%7d.%06d (sys+usr)" "\t%7d.%01d (real).\n", str, NXArgv[0], total / 1000000, total % 1000000, tv->tv_sec, tv->tv_usec/100000); } static ClassImp *findImp(manager, methodName, className, isFactory, methodLists, imps, goToEnd) SegmentManager *manager; char *methodName, *className; BOOL isFactory; struct objc_method_list **methodLists; ClassImp *imps; BOOL goToEnd; { Method foundMethod = NULL, method; struct objc_method_list *methods; /* for use by goToEnd */ int nMethods; ClassImp *imp; char *mName; if ( !methodLists || !*methodLists ) return imps; methods = *methodLists; if (goToEnd) { while ( *methodLists != 0 && *methodLists != -1 ) { methods = *methodLists; methodLists++; } } methods = WARPG( methods ); for (nMethods = methods->method_count, method = methods->method_list; !foundMethod && nMethods; nMethods--, method++) { mName = [currentManager pointerForString: (char *)method->method_name]; if (mName && streq(mName, methodName)) foundMethod = method; } if (foundMethod) { imp = xmalloc(sizeof(*imp)); imp->className = className; imp->function = foundMethod->method_imp; imp->isFactory = isFactory; imp->next = imps; return imp; } else return imps; } #ifndef OBJC_NEXT_METHOD_LIST #define CLS_METHOD_ARRAY 0x100 #endif static struct objc_method_list **methodLists(manager, cls) SegmentManager *manager; Class cls; { if ( cls->info & CLS_METHOD_ARRAY ) { return WARPG( cls->methodLists ); /* WARPG or WARP */ } else { static struct objc_method_list* array[2]; array[0] = cls->methodLists; array[1] = 0; return array; } } static ClassImp * findClassImps(manager, methodName, className, onlyOne, pClass, nClasses, imps) SegmentManager *manager; char *methodName; char *className; OnlyOne onlyOne; Class *pClass; int nClasses; ClassImp *imps; { Class class, metaClass; char *clsName; struct objc_method_list **methods; for (; nClasses; nClasses--, pClass++) { class = WARPG(*pClass); if (class) { clsName = [currentManager pointerForString: class->name]; if (clsName) { if (!className || (strcmp(clsName, className) == 0)) { if ((onlyOne != CLASSONLY) && (methods = methodLists(manager, class))) imps = findImp(manager, methodName, clsName, NO, methods, imps, YES); if ((onlyOne != INSTANCEONLY) && (metaClass = WARPG(class->isa)) && (methods = methodLists(manager, metaClass))) imps = findImp(manager, methodName, clsName, YES, methods, imps, YES); if (onlyOne && imps) return imps; } } } } return imps; } static ClassImp * findCatImps(manager, methodName, className, onlyOne, pCat, nCats, imps) SegmentManager *manager; char *methodName; char *className; OnlyOne onlyOne; Category *pCat; int nCats; ClassImp *imps; { Category cat; char *clsName; struct objc_method_list *methods[2]; methods[1] = 0; for (; nCats; nCats--, pCat++) { cat = WARPG(*pCat); if (cat) { clsName = [currentManager pointerForString: cat->class_name]; if (clsName) { if (!className || (strcmp(clsName, className) == 0)) { if ((onlyOne != CLASSONLY) && (methods[0] = cat->instance_methods)) imps = findImp(manager, methodName, clsName, NO, methods, imps, NO); if ((onlyOne != INSTANCEONLY) && (methods[0] = cat->class_methods)) imps = findImp(manager, methodName, clsName, NO, methods, imps, NO); if (onlyOne && imps) return imps; } } } } return imps; } static ClassImp * findImps(methodName, className, onlyOne) char *methodName, *className; OnlyOne onlyOne; { ClassImp *imps = NULL; struct objfile *objfile; for (objfile = object_files; objfile; objfile = objfile->next) { SegmentManager *manager = objfile->manager; Module mod; int nMods; Symtab symtab; int size; if (mod = [manager getSectData: "__OBJC" section: "__module_info" size: &size]) { for (nMods = size / sizeof(struct objc_module); nMods; nMods--, mod++) { if (symtab = WARPG(mod->symtab)) { imps = findClassImps(manager, methodName, className, onlyOne, (Class *)symtab->defs, symtab->cls_def_cnt, imps); if (onlyOne && imps) return imps; imps = findCatImps(manager, methodName, className, onlyOne, (Category *)symtab->defs + symtab->cls_def_cnt, symtab->cat_def_cnt, imps); if (onlyOne && imps) return imps; } } } } return imps; } Class lookup_objc_class(className) char *className; { if (classMap && className && *className) return NXMapGet(classMap, className); } BOOL isClass(class, allowMeta) Class class; BOOL allowMeta; { struct objfile *objfile; const struct section *section; static Class stringClass = NULL; Class myClass; for (objfile = object_files; objfile; objfile = objfile->next) { SegmentManager *manager = objfile->manager; if (section = [manager getSeg: "__OBJC" sect: "__class"]) { if ((section->addr <= (unsigned)class) && ((unsigned)class < (section->addr + section->size))) return YES; } if (allowMeta && (section = [manager getSeg: "__OBJC" sect: "__meta_class"]) && (section->addr <= (unsigned)class) && ((unsigned)class < (section->addr + section->size))) return YES; } if (myClass = WARP(class)) { Class metaClass = myClass->isa; for (objfile = object_files; objfile; objfile = objfile->next) { SegmentManager *manager = objfile->manager; if ((section = [manager getSeg: "__OBJC" sect: "__meta_class"]) && (section->addr <= (unsigned)metaClass) && ((unsigned)metaClass < (section->addr + section->size))) return YES; } } if (!stringClass) { struct minimal_symbol *minSym = lookup_minimal_symbol("_NXConstantString_class", NULL, NULL); stringClass = minSym ? (Class) SYMBOL_VALUE_ADDRESS(minSym) : (Class) 0xFFFFFFFF; } if (stringClass == class) return YES; return NO; } BOOL isMetaClass(class) Class class; { struct objfile *objfile; const struct section *section; for (objfile = object_files; objfile; objfile = objfile->next) { SegmentManager *manager = objfile->manager; const struct section *section = [manager getSeg: "__OBJC" sect: "__meta_class"]; if (section) { if ((section->addr <= (unsigned)class) && ((unsigned)class < (section->addr + section->size))) return YES; } } return NO; } int lookup_child_selector(methodName) char *methodName; { value_ptr function, val, methodString; int methodSel; if (selectorHash && methodName && *methodName && NXHashMember(selectorHash, methodName)) { function = value_of_function("sel_getUid"); if (function) { methodString = value_string(methodName, strlen(methodName) + 1); val = call_function_by_hand(function, 1, &methodString); if (value_logical_not(val)) error("No method named %s.", methodName); else return value_as_long(val); } else error("Couldn't find sel_getUid in inferior."); } else error("No method named %s.", methodName); return 0; } char * lookup_child_selector_name(sel) SEL sel; { value_ptr function, val, selector; char *methodName; function = value_of_function("sel_getName"); if (function) { selector = value_from_longest(builtin_type_long, (LONGEST)sel); val = call_function_by_hand(function, 1, &selector); if (value_logical_not(val)) error("No method named %s.", methodName); else return WARPSTRING((char *)value_as_long(val)); } else error("Couldn't find sel_getName in inferior."); return 0; } #ifdef GDB_SPARC #define IS_ALIGNED(addr) (((unsigned int)(addr)) % 4 == 0) #endif /* MVS: protect sparc gdb from crashing on un-aligned objects */ Class classForObject(id object) { id myObject = WARP(object); Class class; if (myObject #ifdef GDB_SPARC && IS_ALIGNED(myObject) #endif && isClass(class = myObject->isa, YES)) return class; else return NULL; } Class classForObjectOnly(object) id object; { id myObject = WARP(object); Class class; if (myObject #ifdef GDB_SPARC && IS_ALIGNED(myObject) #endif && isClass(class = myObject->isa, NO)) return class; else return NULL; } CORE_ADDR find_implementation_from_class(class, sel) Class class; SEL sel; { Class myClass; Method method, foundMethod = NULL; struct objc_method_list **methods, *myMethods; int nMethods; for (myClass = WARP(class); !foundMethod && myClass; myClass = WARP(myClass->super_class)) { struct objc_method_list **localMethodList = methodLists(regionManager, myClass); for (foundMethod = NULL; !foundMethod && *localMethodList != -1 && (myMethods = WARP(*localMethodList)) && (myMethods = [regionManager pointerFor: *localMethodList withSize: myMethods->method_count * sizeof(struct objc_method)]); localMethodList++ ) { for (nMethods = myMethods->method_count, method = myMethods->method_list; !foundMethod && nMethods; nMethods--, method++) { if (method->method_name == sel) foundMethod = method; } } } if (foundMethod) return (CORE_ADDR)foundMethod->method_imp; else return 0; } CORE_ADDR find_implementation(object, sel) id object; SEL sel; { Class class; if (class = classForObject(object)) return find_implementation_from_class(class, sel); else return 0; } int cmpImps(i1, i2) const void *i1, *i2; { ClassImp * const *imp1 = i1, * const *imp2 = i2; return strcmp((*imp1)->className, (*imp2)->className); } int lookup_method(argPtr, values) char **argPtr; struct symtabs_and_lines *values; { int ret = 0, impIndex, i, size, j, maxLen = 0, maxNum, len, numPerLine; int num, numImps, numLines, numFullLines, line; char *p = *argPtr, *saveP, *methodName, *className; ClassImp *imps, *imp, **impArray; OnlyOne onlyOne = ANYTHING; struct minimal_symbol *minSym = NULL; #define MAX_CHOICES (100) CORE_ADDR choices[MAX_CHOICES]; int num_choices = 0; if (strchr("+-[", *p)) { switch (*p) { case '-': onlyOne = INSTANCEONLY; if (*(p + 1) == '[') p += 2; else return 0; break; case '+': onlyOne = CLASSONLY; if (*(p + 1) == '[') p += 2; else return 0; break; case '[': default: p++; break; } /* Copy class name */ for (; *p && *p == ' '; p++) ; for (saveP = p, size = 0; *p && (*p != ' '); p++, size++) ; className = alloca(size + 1); memcpy(className, saveP, size), className[size] = '\0'; /* Copy method name */ for (; *p && *p == ' '; p++) ; for (saveP = p, size = 0; *p && (*p != ' ') && (*p != ']'); p++, size++) ; methodName = alloca(size + 1); memcpy(methodName, saveP, size), methodName[size] = '\0'; if (*p == ']') p++; } else { /* Copy method name */ for (; *p && *p == ' '; p++); for (saveP = p, size = 0; *p && (*p != ' '); p++, size++); methodName = alloca(size + 1); memcpy(methodName, saveP, size), methodName[size] = '\0'; className = NULL; } if (className && !lookup_objc_class(className)) error("No class named %s.", className); else if (selectorHash && methodName && *methodName && NXHashMember(selectorHash, methodName) && (imps = findImps(methodName, className, onlyOne))) { if (!className && !onlyOne) minSym = lookup_minimal_symbol(methodName, NULL, NULL); if (imps->next || minSym) { for (imp = imps, numImps = 0; imp; imp = imp->next, numImps++); impArray = alloca(numImps * sizeof(*impArray)); for (imp = imps, i = 0; imp; imp = imp->next, i++) { impArray[i] = imp; len = strlen(imp->className); if (len > maxLen) maxLen = len; } qsort(impArray, numImps, sizeof(*impArray), cmpImps); for (maxNum = 1, num = 10; num <= numImps; maxNum++, num *= 10); numPerLine = 80 / (maxLen + maxNum + 4); if (numPerLine < 1) numPerLine = 1; if (numPerLine > i) numPerLine = numImps; maxLen += ((80 - (numPerLine * (maxLen + maxNum + 4))) / numPerLine); numLines = ((numImps - 1) / numPerLine) + 1; numFullLines = numImps - ((numImps / numPerLine) * numPerLine); if (0) { /* location for some old, decommissioned code. It used to * send the list of possible methods to a UI */ } else { char *nextBlank, *pBuf, buf[300]; if (numImps == 1) printf_filtered("The following class implements %s:\n", methodName); else printf_filtered("The following classes implement %s:\n", methodName); for (i = 0, line = 0; i < numImps; line++) { for (j = 0; i < numImps && j < numPerLine; j++, i++) { if (!numFullLines || j <= numFullLines) impIndex = (j * numLines) + line; else impIndex = (numFullLines * numLines) + ((j - numFullLines) * (numLines - 1)) + line; imp = impArray[impIndex]; printf_filtered("%*d) %c%-*s ", maxNum, impIndex + 1, imp->isFactory ? '+' : '-', maxLen, imp->className); } printf_filtered("\n"); } if (minSym) { if (numImps > 1) printf_filtered("\n"); printf_filtered("%*d) %s() as a function\n\n", maxNum, numImps + 1, methodName); } if (!isatty (fileno (stdin))) { /* reading from a pipe */ printf_filtered("Which one(s) do you want? "); fflush(stdout); /* no newline at end of prompt*/ fgets(buf, sizeof(buf), stdin); } else { /* use readline for input (for editing and completion) */ if (pBuf = (char *) readline("Which one(s) do you want? ")) { strcpy (buf, pBuf); free (pBuf); } else { /* null input */ buf[0] = '\0'; } } for (pBuf = buf; pBuf && *pBuf; pBuf = nextBlank ? nextBlank + 1 : NULL) { if (num_choices >= MAX_CHOICES) { printf_filtered("You can only choose upto %d classes.\n", MAX_CHOICES); break; } while (*pBuf && (*pBuf == ' ')) pBuf++; nextBlank = strpbrk(pBuf, " \n"); if (nextBlank) *nextBlank = '\0'; if (isdigit(*pBuf)) { impIndex = atoi(pBuf); if ((1 <= impIndex) && (impIndex <= numImps)) choices[num_choices++] = (CORE_ADDR) impArray[impIndex - 1]->function; else if (minSym && (impIndex == (numImps + 1))) choices[num_choices++] = (CORE_ADDR) SYMBOL_VALUE_ADDRESS(minSym); } else { for (i = 0; i < numImps; i++) { if (strcmp(pBuf, impArray[i]->className) == 0) { choices[num_choices++] = (CORE_ADDR) impArray[i]->function; break; } } } } /* end foreach input line */ } for (i = 0; i < numImps; i++) free(impArray[i]); } else choices[num_choices++] = (CORE_ADDR) imps->function; if (num_choices > 0) { int count = num_choices; values->sals = xmalloc(sizeof(*values->sals) * count); values->nelts = count; while (count--) { CORE_ADDR pc = choices[count]; find_pc_symtab(pc); SKIP_PROLOGUE(pc); values->sals[count] = find_pc_line(pc, 0); #warning Always using the start address, no matter what symtab says #ifdef DONTTRUSTTHISPC if (!values->sals[count].symtab) #endif values->sals[count].pc = pc; ret = 1; } } else { error("No method chosen."); } } if (ret) { *argPtr = p; return ret; } else if (className) { if (methodName && *methodName) { error("%s responds to no %smethod named %s.", className, (onlyOne == CLASSONLY) ? "class " : ((onlyOne == INSTANCEONLY) ? "instance " : ""), methodName); } else error ("No method name given."); } else return 0; } void static addMethods(manager, methods) SegmentManager *manager; struct objc_method_list *methods; { Method method; int nMethods; if (methods) { for (nMethods = methods->method_count, method = methods->method_list; nMethods; nMethods--, method++) { char *methodName = [currentManager pointerForString: (char *)method->method_name]; if (methodName) NXHashInsertIfAbsent( selectorHash, stringSave(methodName)); } } } void static addMethodDescriptions(manager, methods) SegmentManager *manager; struct objc_method_description_list *methods; { struct objc_method_description *method; int nMethods; if (methods) { for (nMethods = methods->count, method = methods->list; nMethods; nMethods--, method++) { char *methodName = [currentManager pointerForString: (char *)method->name]; if (methodName) NXHashInsertIfAbsent( selectorHash, stringSave(methodName)); } } } void objc_completion_list_hook (text) char *text; { int nameLen; char firstChar, *selector; NXHashState state; if (!selectorHash) return; nameLen = strlen(text); if (nameLen) { nameLen--; firstChar = *text; text++; state = NXInitHashState(selectorHash); while (NXNextHashState(selectorHash, &state, (void **)&selector)) { if (firstChar == *selector && strncmp(text, selector + 1, nameLen) == 0) completion_list_add_symbol_no_check(selector); } } } struct type * typeForObject(object, block) CORE_ADDR object; struct block *block; { Class class, myClass; char *myClassName; struct type *structType; if (regionManager && (class = classForObjectOnly((id)object)) && (myClass = WARP(class)) #ifdef GDB_SPARC && IS_ALIGNED(myClass) #endif && (myClassName = WARPSTRING(myClass->name)) && (structType = lookup_struct_no_error(myClassName, NULL))) return lookup_pointer_type(structType); else return NULL; } struct type * typeForClassName(className) char *className; { if (NXMapGet(classMap, className)) return lookup_struct_no_error(className, 0); else return NULL; } int isObjectType(type) struct type *type; { struct type *target_type; char *name; return ((TYPE_CODE(type) == TYPE_CODE_PTR) && (target_type = TYPE_TARGET_TYPE(type)) && (TYPE_CODE(target_type) == TYPE_CODE_STRUCT) && ( (name = TYPE_NAME(target_type)) || (name = TYPE_TAG_NAME(target_type))) && (strstr(name, "objc_object") || ((strncmp(name, "struct ", 7) == 0) && (lookup_objc_class(name + 7))))); } struct type * objectType(object) CORE_ADDR object; { Class class, myClass; char *myClassName; if (regionManager && (class = classForObject((id)object)) && (!isMetaClass(class)) && (myClass = WARP(class)) #ifdef GDB_SPARC && IS_ALIGNED(myClass) #endif && (myClassName = WARPSTRING(myClass->name))) return lookup_struct_no_error(myClassName, 0); else return NULL; } static void addModSect(manager, section) SegmentManager *manager; const struct section *section; { Class class, *pClass; int nClasses, nMods, nCats; Module mod; Symtab symtab; Category cat, *pCat; for (nMods = section->size / sizeof(struct objc_module), mod = [currentManager pointerFor: (void *)section->addr]; nMods; nMods--, mod++) { if (mod) { symtab = WARPG(mod->symtab); if (symtab) { for (nClasses = symtab->cls_def_cnt, pClass = (Class *)symtab->defs; nClasses; nClasses--, pClass++) { class = WARPG(*pClass); if (class) { struct objc_method_list *methods = WARPG(*methodLists( manager, class )); const char *className = [currentManager pointerForString: class->name]; if (className) /* MVS: name to be inserted into classMap */ NXMapInsert(classMap, stringSave(className), *pClass); if (methods) /* MVS: instance methods (I think) */ addMethods(manager, methods); class = WARPG(class->isa); if (class) { /* MVS: class methods (I think) */ methods = WARPG( *methodLists(manager, class) ); if (methods) addMethods(manager, methods); } } } for (nCats = symtab->cat_def_cnt, pCat = (Category *)pClass; nCats; nCats--, pCat++) { cat = WARPG(*pCat); if (cat) { addMethods(manager, WARPG(cat->instance_methods)); addMethods(manager, WARPG(cat->class_methods)); } } } } } } static void addProtoSect(manager, section) SegmentManager *manager; const struct section *section; { typedef struct { @defs(Protocol) } ProtocolID; ProtocolID *proto; int nProtos; for (nProtos = section->size / sizeof(ProtocolID), proto = [currentManager pointerFor: (void *)section->addr withSize: section->size]; nProtos; nProtos--, proto++) { if (proto) { addMethodDescriptions(manager, WARPG(proto->instance_methods)); addMethodDescriptions(manager, WARPG(proto->class_methods)); } } } static void addRefSect(manager, section) SegmentManager *manager; const struct section *section; { SEL *sel; int nSels; for (nSels = section->size / sizeof(SEL), sel = [currentManager pointerFor: (void *)section->addr withSize: section->size]; nSels; nSels--, sel++) { char *methodName = [currentManager pointerForString: (char *)*sel]; if (methodName) NXHashInsertIfAbsent( selectorHash, stringSave(methodName)); } } void readObjC(manager) SegmentManager *manager; { const struct section *modSect, *protoSect, *refSect; if (!selectorHash) selectorHash = NXCreateHashTable(NXStrPrototype, 0, NULL); if (!classMap) classMap = NXCreateMapTable(NXStrValueMapPrototype, 0); if (modSect = [manager getSeg: "__OBJC" sect: "__module_info"]) addModSect(manager, modSect); if (protoSect = [manager getSeg: "__OBJC" sect: "__protocol"]) addProtoSect(manager, protoSect); if (refSect = [manager getSeg: "__OBJC" sect: "__message_refs"]) addRefSect(manager, refSect); } struct type * objc_type(val, block) value_ptr val; struct block *block; { struct type *oldType, *newType; oldType = VALUE_TYPE(val); if (isObjectType(oldType)) return typeForObject(*(CORE_ADDR *)VALUE_CONTENTS(val), block); else return NULL; } void cast_to_objc_type(val, block) value_ptr val; struct block *block; { struct type *newType; if (newType = objc_type(val, block)) VALUE_TYPE(val) = newType; } value_ptr value_of_self (complain) int complain; { extern struct frame_info * selected_frame; struct symbol *func, *sym; struct block *b; static char *savedSelf = NULL; if (selected_frame == 0) if (complain) error ("no frame selected"); else return 0; func = get_frame_function (selected_frame); if (!func) return 0; b = SYMBOL_BLOCK_VALUE (func); if (BLOCK_NSYMS (b) <= 0) if (complain) error ("no args, no `self'"); else return 0; sym = BLOCK_SYM (b, 0); if (!savedSelf) savedSelf = stringSave("self"); if (savedSelf != SYMBOL_NAME (sym)) if (complain) error ("current stack frame not in method"); else return 0; sym = lookup_block_symbol(b, savedSelf, VAR_NAMESPACE); return read_var_value (sym, selected_frame); } static NXStream *hisStream = NULL; void clearPrintStream() { hisStream = NULL; } value_ptr value_nsstring(ptr, len) char *ptr; int len; { value_ptr stringValue = value_string(ptr, len), istrValue, nsstringValue; istrValue = value_of_function("_NSNewStringFromCString"); /* _NSNewStringFromCString replaces "istr" after Lantern2A */ if (!istrValue) istrValue = value_of_function("istr"); if (istrValue) { struct type *type = lookup_struct_no_error("NSString", 0); nsstringValue = call_function_by_hand(istrValue, 1, &stringValue); if (!type) type = lookup_struct_no_error("NXString", 0); if (type) { type = lookup_pointer_type(type); VALUE_TYPE(nsstringValue) = type; } return nsstringValue; } else error ("NSString: internal error. NSstring lib not linked?"); } void printObjectCommand(args, from_tty) char *args; int from_tty; { static value_ptr streamVal = NULL, zeroVal = NULL, printSelVal = NULL; static int printSel = 0; value_ptr objectVal; id hisObject; int len; if (!args || !*args) /* MVS: check for valid input */ error("PrintObject requires an argument"); if (!hisStream) { /* open a stream in the inferior */ value_ptr openFunc, Args[3]; printSel = 0; /* MVS: better force new lookup of print selector */ if (!(zeroVal = value_from_longest(builtin_type_long, 0))) error("PrintObject: internal error 1"); release_value(zeroVal); if ((openFunc = value_of_function("NXOpenMemory")) == 0) error("PrintObject: failed to find function NXOpenMemory"); Args[0] = Args[1] = zeroVal; if (!(Args[2] = value_from_longest(builtin_type_long, NX_WRITEONLY))) error("PrintObject: internal error 2"); if (!(streamVal = call_function_by_hand(openFunc, 3, Args)) || !(hisStream = (NXStream *) value_as_long(streamVal))) error("failed to open stream for output"); release_value(streamVal); } else { value_ptr seekFunc, Args[] = {streamVal, zeroVal, zeroVal}; if (!(seekFunc = value_of_function("NXSeek"))) error("PrintObject: failed to find function NXSeek"); call_function_by_hand(seekFunc, 3, Args); } if (!printSel || !printSelVal) { /* get selector for printForDebugger */ printSel = lookup_child_selector("printForDebugger:"); if (!(printSelVal = value_from_longest(builtin_type_long, (LONGEST) printSel))) error("PrintObject: internal error 3"); release_value(printSelVal); } if (!(objectVal = parse_and_eval(args))) error("PrintObject: invalid expression <%s>", args); hisObject = (id)value_as_long(objectVal); if (hisObject) { unsigned imp; value_ptr printVal, Args[] = {objectVal, printSelVal, streamVal}; NXStream *myStream; char *myBuf, *myString; if (!(imp = find_implementation(hisObject, (SEL)printSel))) /* MVS: args not an object!? */ error("%s is not an object, or does not respond to printForDebugger", args); if (!(printVal = value_from_longest(builtin_type_long, (LONGEST)imp))) error("PrintObject: internal error 5"); /* call printForDebugger */ call_function_by_hand(printVal, 3, Args); if (!(myStream = WARP(hisStream))) /* grab the output stream */ error("PrintObject: internal error 6"); len = myStream->buf_ptr - myStream->buf_base; myBuf = [regionManager pointerFor: myStream->buf_base withSize: len]; if (!myBuf) error("PrintObject: internal error 7"); fwrite(myBuf, 1, len, stdout); /* send his output to our output */ fputs("\n", stdout); } else error("<%s: nil object>", args); } #define TRIM 0177 /* Mask to strip quote bit */ static int globMatch(s, p) char *s, *p; { int scc; int ok, lc; int c, cc; for (;;) { scc = *s++ & TRIM; switch (c = *p++) { case '[': ok = 0; lc = 077777; while (cc = *p++) { if (cc == ']') { if (ok) break; return (0); } if (cc == '-') { if (lc <= scc && scc <= *p++) ok++; } else if (scc == (lc = cc)) ok++; } if (cc == 0) return 0; continue; case '*': if (!*p) return (1); for (s--; *s; s++) if (globMatch(s, p)) return (1); return (0); case 0: return (scc == 0); case '?': if (scc == 0) return (0); continue; default: if ((c & TRIM) != scc) return (0); continue; } } } static int pstrcmp(ps1, ps2) const void *ps1, *ps2; { return strcmp(*(char **)ps1, *(char **)ps2); } static void printNames(found, numFound) char **found; int numFound; { int i, maxLen = 0, len, numPerLine, j; for (i = 0; i < numFound; i++) { QUIT; len = strlen(found[i]); if (len > maxLen) maxLen = len; } numPerLine = 80 / (maxLen + 1); if (numPerLine < 1) numPerLine = 1; if (numPerLine > numFound) numPerLine = numFound; maxLen += ((80 - (numPerLine * (maxLen + 1))) / numPerLine); qsort(found, numFound, sizeof(*found), pstrcmp); for (i = 0; i < numFound;) { QUIT; for (j = 0; (i < numFound) && (j < (numPerLine-1)); j++) { printf("%-*s ", maxLen, found[i++]); } printf("%s\n", i < numFound ? found[i++] : ""); } } static void infoClassesCommand(args, from_tty) char *args; int from_tty; { if (classMap) { int numAlloced = 24, numFound = 0, i, maxLen = 0; int len, numPerLine, j, extra; char **found = malloc(numAlloced * sizeof(*found)); struct cleanup *cleanups = make_cleanup(free, found); NXMapState state; Class class; char *className; if (!args) args = "*"; state = NXInitMapState(classMap); while (NXNextMapState(classMap, &state, (void **) &className, (void **) &class)) { QUIT; if (globMatch(className, args)) { if (numFound == numAlloced) { numAlloced *= 2; found = realloc(found, numAlloced * sizeof(*found)); } found[numFound++] = className; } } if (numFound) { printf("The following classes match %s.\n", args); printNames(found, numFound); } else printf("No classes match %s.\n", args); do_cleanups(cleanups); } } static void infoSelectorsCommand(args, from_tty) char *args; int from_tty; { if (selectorHash) { int numAlloced = 24, numFound = 0, i, maxLen = 0, len, numPerLine, j; char **found = malloc(numAlloced * sizeof(*found)), *selName; struct cleanup *cleanups = make_cleanup(free, found); NXHashState state; if (!args) args = "*"; state = NXInitHashState(selectorHash); while (NXNextHashState(selectorHash, &state, (void **)&selName)) { QUIT; if (globMatch(selName, args)) { if (numFound == numAlloced) { numAlloced *= 2; found = realloc(found, numAlloced * sizeof(*found)); } found[numFound++] = selName; } } if (numFound) { printf("The following selectors match %s.\n", args); printNames(found, numFound); } else printf("No selectors match %s.\n", args); do_cleanups(cleanups); } } static NXMapTable * removeRangeFromMap(mapTable, start, size) NXMapTable *mapTable; void *start; unsigned size; { NXMapState state = NXInitMapState(mapTable); void *key, *value; while (NXNextMapState(mapTable, &state, &key, &value)) { if (start <= value && value < (start + size)) NXMapRemove(mapTable, key); } return mapTable; } static NXHashTable * removeRangeFromHash(hashTable, start, size) NXHashTable *hashTable; void *start; unsigned size; { NXHashTable *newHash = NXCreateHashTable(NXStrPrototype, NXCountHashTable(hashTable), 0); NXHashState state = NXInitHashState(hashTable); void *key; while (NXNextHashState(hashTable, &state, &key)) { if (!(start <= key && key < (start + size))) NXHashInsert(newHash, key); } return newHash; } void removeObjC(manager) SegmentManager *manager; { if (classMap) classMap = removeRangeFromMap(classMap, manager->images->header, (unsigned)manager->images->size); } void _initialize_objc() { add_com("print-object", class_vars, printObjectCommand, "Print object by sending \"printForDebugger:\" to it.\n"); add_com_alias("po", "print-object", class_vars, 2); add_info("classes", infoClassesCommand, "All Objective C classes"); add_info("selectors", infoSelectorsCommand, "All Objective C selectors"); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.