Trying to understand AppleObjCRuntimeV1::CreateObjectChecker


is this funny function AppleObjCRuntimeV1::CreateObjectChecker actually used ?

First of all the snprintf is wrapped in an assert (!?). If the code gets compiled with -DNDEBUG, the function would pretty much do nothing.

Second, I guess, that this is some C-code that gets compiled on the fly, It seems to do nothing. I guess the code has a chance of crashing, if the parameter is not really an object. The debugger could notice that and figure out, that it's not an object. But guessing is not knowing... :slight_smile:


struct BufStruct {
     char contents[2048];

UtilityFunction *
AppleObjCRuntimeV1::CreateObjectChecker(const char *name)
     std::unique_ptr<BufStruct> buf(new BufStruct);

     assert(snprintf(&buf->contents[0], sizeof(buf->contents),
                     "struct __objc_class                       \n"
                     "{                       \n"
                     "   struct __objc_class *isa;                       \n"
                     "   struct __objc_class *super_class;                       \n"
                     "   const char *name;                       \n"
                     "   // rest of struct elided because unused                       \n"
                     "};                       \n"
                     "                       \n"
                     "struct __objc_object                       \n"
                     "{                       \n"
                     "   struct __objc_class *isa;                       \n"
                     "};                       \n"
                     "                       \n"
                     "extern \"C\" void                       \n"
                     "%s(void *$__lldb_arg_obj, void *$__lldb_arg_selector)                  \n"
                     "{                       \n"
                     "   struct __objc_object *obj = (struct __objc_object*)$__lldb_arg_obj; \n"
                     "   (int)strlen(obj->isa->name);                       \n"
                     "}                       \n",
                     name) < (int)sizeof(buf->contents));

     Error error;
     return GetTargetRef().GetUtilityFunctionForLanguage(buf->contents, eLanguageTypeObjC, name, error);

Your guess about the use of the checkers is right. The code you are looking at compiles and inserts this function in the target, then when compiling future expressions, if we detect a reference to an ObjC object, we insert a call to the checker into the JIT'ed code before accessing the object. That way, if your expression references an invalid object, we'll crash here and be able to report that the object was bad, rather than leaving you to have to figure out from some crash later on in your expression that this was the cause.

We did this because it is pretty common for IDE's to operate on ALL the locals in a frame, and it is quite common that some of them are not yet initialized. So detecting this condition is helpful.

I think the way the assert is done in this case is just a bug. The AppleRuntimeV2 checker does the snprintf, then does:

    assert (len < (int)sizeof(check_function_code));

That's just to catch the case where somebody working on lldb decides to change the name of the checker function to something enormous that overflows the buffer, or adds code to the checker w/o making sure to enlarge the buffer. Both of those would be programmer errors, so an assert that goes away with -DNDEBUG is appropriate.