save_global(PicklerObject *self, PyObject *obj, PyObject *name)
{
    PyObject *global_name = NULL;
    PyObject *module_name = NULL;
    PyObject *module = NULL;
    PyObject *parent = NULL;
    PyObject *dotted_path = NULL;
    PyObject *lastname = NULL;
    PyObject *cls;
    PickleState *st = _Pickle_GetGlobalState();
    int status = 0;
    _Py_IDENTIFIER(__name__);
    _Py_IDENTIFIER(__qualname__);

    const char global_op = GLOBAL;

    if (name) {
        Py_INCREF(name);
        global_name = name;
    }
    else {
        if (_PyObject_LookupAttrId(obj, &PyId___qualname__, &global_name) < 0)
            goto error;
        if (global_name == NULL) {
            global_name = _PyObject_GetAttrId(obj, &PyId___name__);
            if (global_name == NULL)
                goto error;
        }
    }

    dotted_path = get_dotted_path(module, global_name);
    if (dotted_path == NULL)
        goto error;
    module_name = whichmodule(obj, dotted_path);
    if (module_name == NULL)
        goto error;

    /* XXX: Change to use the import C API directly with level=0 to disallow
       relative imports.

       XXX: PyImport_ImportModuleLevel could be used. However, this bypasses
       builtins.__import__. Therefore, _pickle, unlike pickle.py, will ignore
       custom import functions (IMHO, this would be a nice security
       feature). The import C API would need to be extended to support the
       extra parameters of __import__ to fix that. */
    module = PyImport_Import(module_name);
    if (module == NULL) {
        PyErr_Format(st->PicklingError,
                     "Can't pickle %R: import of module %R failed",
                     obj, module_name);
        goto error;
    }
    lastname = PyList_GET_ITEM(dotted_path, PyList_GET_SIZE(dotted_path)-1);
    Py_INCREF(lastname);
    cls = get_deep_attribute(module, dotted_path, &parent);
    Py_CLEAR(dotted_path);
    if (cls == NULL) {
        PyErr_Format(st->PicklingError,
                     "Can't pickle %R: attribute lookup %S on %S failed",
                     obj, global_name, module_name);
        goto error;
    }
    if (cls != obj) {
        Py_DECREF(cls);
        PyErr_Format(st->PicklingError,
                     "Can't pickle %R: it's not the same object as %S.%S",
                     obj, module_name, global_name);
        goto error;
    }
    Py_DECREF(cls);

    if (self->proto >= 2) {
        /* See whether this is in the extension registry, and if
         * so generate an EXT opcode.
         */
        PyObject *extension_key;
        PyObject *code_obj;      /* extension code as Python object */
        long code;               /* extension code as C value */
        char pdata[5];
        Py_ssize_t n;

        extension_key = PyTuple_Pack(2, module_name, global_name);
        if (extension_key == NULL) {
            goto error;
        }
        code_obj = PyDict_GetItemWithError(st->extension_registry,
                                           extension_key);
        Py_DECREF(extension_key);
        /* The object is not registered in the extension registry.
           This is the most likely code path. */
        if (code_obj == NULL) {
            if (PyErr_Occurred()) {
                goto error;
            }
            goto gen_global;
        }

        /* XXX: pickle.py doesn't check neither the type, nor the range
           of the value returned by the extension_registry. It should for
           consistency. */

        /* Verify code_obj has the right type and value. */
        if (!PyLong_Check(code_obj)) {
            PyErr_Format(st->PicklingError,
                         "Can't pickle %R: extension code %R isn't an integer",
                         obj, code_obj);
            goto error;
        }
        code = PyLong_AS_LONG(code_obj);
        if (code <= 0 || code > 0x7fffffffL) {
            if (!PyErr_Occurred())
                PyErr_Format(st->PicklingError, "Can't pickle %R: extension "
                             "code %ld is out of range", obj, code);
            goto error;
        }

        /* Generate an EXT opcode. */
        if (code <= 0xff) {
            pdata[0] = EXT1;
            pdata[1] = (unsigned char)code;
            n = 2;
        }
        else if (code <= 0xffff) {
            pdata[0] = EXT2;
            pdata[1] = (unsigned char)(code & 0xff);
            pdata[2] = (unsigned char)((code >> 8) & 0xff);
            n = 3;
        }
        else {
            pdata[0] = EXT4;
            pdata[1] = (unsigned char)(code & 0xff);
            pdata[2] = (unsigned char)((code >> 8) & 0xff);
            pdata[3] = (unsigned char)((code >> 16) & 0xff);
            pdata[4] = (unsigned char)((code >> 24) & 0xff);
            n = 5;
        }

        if (_Pickler_Write(self, pdata, n) < 0)
            goto error;
    }
    else {
  gen_global:
        if (parent == module) {
            Py_INCREF(lastname);
            Py_DECREF(global_name);
            global_name = lastname;
        }
        if (self->proto >= 4) {
            const char stack_global_op = STACK_GLOBAL;

            if (save(self, module_name, 0) < 0)
                goto error;
            if (save(self, global_name, 0) < 0)
                goto error;

            if (_Pickler_Write(self, &stack_global_op, 1) < 0)
                goto error;
        }
        else if (parent != module) {
            PickleState *st = _Pickle_GetGlobalState();
            PyObject *reduce_value = Py_BuildValue("(O(OO))",
                                        st->getattr, parent, lastname);
            if (reduce_value == NULL)
                goto error;
            status = save_reduce(self, reduce_value, NULL);
            Py_DECREF(reduce_value);
            if (status < 0)
                goto error;
        }
        else {
            /* Generate a normal global opcode if we are using a pickle
               protocol < 4, or if the object is not registered in the
               extension registry. */
            PyObject *encoded;
            PyObject *(*unicode_encoder)(PyObject *);

            if (_Pickler_Write(self, &global_op, 1) < 0)
                goto error;

            /* For protocol < 3 and if the user didn't request against doing
               so, we convert module names to the old 2.x module names. */
            if (self->proto < 3 && self->fix_imports) {
                if (fix_imports(&module_name, &global_name) < 0) {
                    goto error;
                }
            }

            /* Since Python 3.0 now supports non-ASCII identifiers, we encode
               both the module name and the global name using UTF-8. We do so
               only when we are using the pickle protocol newer than version
               3. This is to ensure compatibility with older Unpickler running
               on Python 2.x. */
            if (self->proto == 3) {
                unicode_encoder = PyUnicode_AsUTF8String;
            }
            else {
                unicode_encoder = PyUnicode_AsASCIIString;
            }
            encoded = unicode_encoder(module_name);
            if (encoded == NULL) {
                if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError))
                    PyErr_Format(st->PicklingError,
                                 "can't pickle module identifier '%S' using "
                                 "pickle protocol %i",
                                 module_name, self->proto);
                goto error;
            }
            if (_Pickler_Write(self, PyBytes_AS_STRING(encoded),
                               PyBytes_GET_SIZE(encoded)) < 0) {
                Py_DECREF(encoded);
                goto error;
            }
            Py_DECREF(encoded);
            if(_Pickler_Write(self, "\n", 1) < 0)
                goto error;

            /* Save the name of the module. */
            encoded = unicode_encoder(global_name);
            if (encoded == NULL) {
                if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError))
                    PyErr_Format(st->PicklingError,
                                 "can't pickle global identifier '%S' using "
                                 "pickle protocol %i",
                                 global_name, self->proto);
                goto error;
            }
            if (_Pickler_Write(self, PyBytes_AS_STRING(encoded),
                               PyBytes_GET_SIZE(encoded)) < 0) {
                Py_DECREF(encoded);
                goto error;
            }
            Py_DECREF(encoded);
            if (_Pickler_Write(self, "\n", 1) < 0)
                goto error;
        }
        /* Memoize the object. */
        if (memo_put(self, obj) < 0)
            goto error;
    }

    if (0) {
  error:
        status = -1;
    }
    Py_XDECREF(module_name);
    Py_XDECREF(global_name);
    Py_XDECREF(module);
    Py_XDECREF(parent);
    Py_XDECREF(dotted_path);
    Py_XDECREF(lastname);

    return status;
}