//     Copyright 2023, Kay Hayen, mailto:kay.hayen@gmail.com
//
//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     Licensed under the Apache License, Version 2.0 (the "License");
//     you may not use this file except in compliance with the License.
//     You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//     Unless required by applicable law or agreed to in writing, software
//     distributed under the License is distributed on an "AS IS" BASIS,
//     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//     See the License for the specific language governing permissions and
//     limitations under the License.
//
/* The main program for a compiled program.
 *
 * It needs to prepare the interpreter and then loads and executes
 * the "__main__" module.
 *
 */

#if defined(_WIN32)
#include <windows.h>
#endif

#include "nuitka/prelude.h"

#ifndef __IDE_ONLY__
// Generated during build with optional defines.
#include "build_definitions.h"
#else
// For the IDE to know these exist.
#define SYSFLAG_PY3K_WARNING 0
#define SYSFLAG_DIVISION_WARNING 0
#define SYSFLAG_UNICODE 0
#define SYSFLAG_OPTIMIZE 0
#define SYSFLAG_NO_SITE 0
#define SYSFLAG_VERBOSE 0
#define SYSFLAG_BYTES_WARNING 0
#define SYSFLAG_UTF8 0
#define SYSFLAG_UNBUFFERED 0
#endif

#ifndef NUITKA_MAIN_MODULE_NAME
#define NUITKA_MAIN_MODULE_NAME "__main__"
#endif

// It doesn't work for MinGW64 to update the standard output handles early on,
// so make a difference here.
#if defined(NUITKA_FORCED_STDOUT_PATH) || defined(NUITKA_FORCED_STDERR_PATH)
#if defined(__MINGW64__) || defined(__MINGW32__)
#define NUITKA_STANDARD_HANDLES_EARLY 0
#else
#define NUITKA_STANDARD_HANDLES_EARLY 1
#endif
#else
#define NUITKA_STANDARD_HANDLES_EARLY 0
#endif

extern PyCodeObject *codeobj_main;

/* For later use in "Py_GetArgcArgv" we expose the needed value  */
#if PYTHON_VERSION >= 0x300
static wchar_t **orig_argv;
#else
static char **orig_argv;
#endif
static int orig_argc;

#if _NUITKA_FROZEN > 0
extern void copyFrozenModulesTo(struct _frozen *destination);

// The original frozen modules list.
#if PYTHON_VERSION < 0x340
static struct _frozen *old_frozen = NULL;
#else
static struct _frozen const *old_frozen = NULL;
#endif

static void prepareFrozenModules(void) {
    // Tell the CPython library to use our pre-compiled modules as frozen
    // modules. This for those modules/packages like "encoding" that will be
    // loaded during "Py_Initialize" already, for the others they may be
    // compiled.

    // The CPython library before 3.11 has some pre-existing frozen modules, we
    // only append to those to keep compatible.
    struct _frozen const *search = PyImport_FrozenModules;
    int pre_existing_count;

    if (search) {
        while (search->name) {
            search++;
        }
        pre_existing_count = (int)(search - PyImport_FrozenModules);
    } else {
        pre_existing_count = 0;
    }

    // Allocate new memory and merge the tables. Keeping the old ones has the
    // advantage that e.g. "import this" is going to be compatible, and there
    // might be Python flavors that add more.
    struct _frozen *merged =
        (struct _frozen *)malloc(sizeof(struct _frozen) * (_NUITKA_FROZEN + pre_existing_count + 1));

    memcpy(merged, PyImport_FrozenModules, pre_existing_count * sizeof(struct _frozen));
    copyFrozenModulesTo(merged + pre_existing_count);
    old_frozen = PyImport_FrozenModules;
    PyImport_FrozenModules = merged;
}
#endif

#if SYSFLAG_NO_RANDOMIZATION == 1 || SYSFLAG_UNBUFFERED == 1 || defined(_NUITKA_STANDALONE)

#if defined(_WIN32)
#define environment_char_t wchar_t

static environment_char_t const *getEnvironmentVariable(char const *name) {
    // Max size for environment variables according to docs.
    wchar_t buffer[32768];
    buffer[0] = 0;

    wchar_t name_wide[40];
    name_wide[0] = 0;
    appendStringSafeW(name_wide, name, sizeof(name_wide) / sizeof(wchar_t));

    // Size must be in bytes apparently, not in characters. Cannot be larger anyway.
    DWORD res = GetEnvironmentVariableW(name_wide, buffer, 65536);

    if (res == 0 || res > sizeof(buffer)) {
        return NULL;
    }

    return wcsdup(buffer);
}

static void setEnvironmentVariable(char const *name, environment_char_t const *value) {
    assert(name != NULL);
    assert(value != NULL);

    wchar_t name_wide[40];
    name_wide[0] = 0;
    appendStringSafeW(name_wide, name, sizeof(name_wide) / sizeof(wchar_t));

    DWORD res = SetEnvironmentVariableW(name_wide, value);
    assert(wcscmp(getEnvironmentVariable(name), value) == 0);

    assert(res != 0);
}

static void unsetEnvironmentVariable(char const *name) {
    wchar_t name_wide[40];
    name_wide[0] = 0;
    appendStringSafeW(name_wide, name, sizeof(name_wide) / sizeof(wchar_t));

    DWORD res = SetEnvironmentVariableW(name_wide, NULL);

    assert(res != 0);
}

#define makeEnvironmentLiteral(x) L##x

#else
#define environment_char_t char

#define makeEnvironmentLiteral(x) x

static environment_char_t const *getEnvironmentVariable(char const *name) { return getenv(name); }

static void setEnvironmentVariable(char const *name, environment_char_t const *value) { setenv(name, value, 1); }

static void unsetEnvironmentVariable(char const *name) { unsetenv(name); }

#endif

static void undoEnvironmentVariable(char const *variable_name, environment_char_t const *old_value) {
    PyObject *os_module = IMPORT_HARD_OS();
    CHECK_OBJECT(os_module);

    PyObject *os_environ = PyObject_GetAttrString(os_module, "environ");
    CHECK_OBJECT(os_environ);

    PyObject *variable_name_str = Nuitka_String_FromString(variable_name);
    CHECK_OBJECT(variable_name_str);

    if (old_value) {
        setEnvironmentVariable(variable_name, old_value);

#ifdef _WIN32
        PyObject *env_value = NuitkaUnicode_FromWideChar(old_value, -1);
#else
        PyObject *env_value = Nuitka_String_FromString(old_value);
#endif
        CHECK_OBJECT(env_value);

        int res = PyObject_SetItem(os_environ, variable_name_str, env_value);

        if (unlikely(res != 0)) {
            PyErr_PrintEx(1);
            Py_Exit(1);
        }

        Py_DECREF(env_value);
    } else {
        unsetEnvironmentVariable(variable_name);

        int res = PyObject_DelItem(os_environ, variable_name_str);

        if (unlikely(res != 0)) {
            DROP_ERROR_OCCURRED();
        }
    }

    Py_DECREF(variable_name_str);
    Py_DECREF(os_environ);
}
#endif

#ifdef _NUITKA_STANDALONE

#include "pythonrun.h"

static environment_char_t const *old_env_path;
static environment_char_t const *old_env_pythonhome;

static void prepareStandaloneEnvironment(void) {
    /* Setup environment variables to tell CPython that we would like it to use
     * the provided binary directory as the place to look for DLLs and for
     * extension modules.
     */
    old_env_path = getEnvironmentVariable("PATH");
    // Remove the PATH during Python init, so it won't pick up stuff from there.
    setEnvironmentVariable("PATH", makeEnvironmentLiteral("/"));

    old_env_pythonhome = getEnvironmentVariable("PYTHONHOME");
#if defined(_WIN32)
    setEnvironmentVariable("PYTHONHOME", getBinaryDirectoryWideChars(true));
#else
    setEnvironmentVariable("PYTHONHOME", getBinaryDirectoryHostEncoded(true));
#endif

#if defined(_WIN32)
    SetDllDirectoryW(getBinaryDirectoryWideChars(true));
#endif

#if PYTHON_VERSION < 0x300
    char *binary_directory = (char *)getBinaryDirectoryHostEncoded(true);
    NUITKA_PRINTF_TRACE("main(): Binary dir is %s\n", binary_directory);

    Py_SetPythonHome(binary_directory);
#elif PYTHON_VERSION < 0x370
    wchar_t *binary_directory = (wchar_t *)getBinaryDirectoryWideChars(true);
    NUITKA_PRINTF_TRACE("main(): Binary dir is %S\n", binary_directory);

    Py_SetPythonHome(binary_directory);
    Py_SetPath(binary_directory);

#endif

#if PYTHON_VERSION >= 0x380 && PYTHON_VERSION < 0x3b0 && defined(_WIN32)
    _Py_path_config.isolated = 1;
#endif
}

static void restoreStandaloneEnvironment(void) {
    /* Make sure to use the optimal value for standalone mode only. */
#if PYTHON_VERSION < 0x300
    PySys_SetPath((char *)getBinaryDirectoryHostEncoded(true));
    // NUITKA_PRINTF_TRACE("Final PySys_GetPath is 's'.\n", PySys_GetPath());
#elif PYTHON_VERSION < 0x370
    PySys_SetPath(getBinaryDirectoryWideChars(true));
    Py_SetPath(getBinaryDirectoryWideChars(true));
    // NUITKA_PRINTF_TRACE("Final Py_GetPath is '%ls'.\n", Py_GetPath());
#endif

#ifdef _NUITKA_EXPERIMENTAL_DUMP_PY_PATH_CONFIG
    wprintf(L"_Py_path_config.program_full_path='%lS'\n", _Py_path_config.program_full_path);
    wprintf(L"_Py_path_config.program_name='%lS'\n", _Py_path_config.program_name);
    wprintf(L"_Py_path_config.prefix='%lS'\n", _Py_path_config.prefix);
    wprintf(L"_Py_path_config.exec_prefix='%lS'\n", _Py_path_config.exec_prefix);
    wprintf(L"_Py_path_config.module_search_path='%lS'\n", _Py_path_config.module_search_path);
    wprintf(L"_Py_path_config.home='%lS'\n", _Py_path_config.home);
#endif
}

#endif

extern void _initCompiledCellType();
extern void _initCompiledGeneratorType();
extern void _initCompiledFunctionType();
extern void _initCompiledMethodType();
extern void _initCompiledFrameType();

#include <locale.h>

#ifdef _WIN32
#define _NUITKA_NATIVE_WCHAR_ARGV 1
#else
#define _NUITKA_NATIVE_WCHAR_ARGV 0
#endif

// Types of command line arguments are different between Python2/3.
#if PYTHON_VERSION >= 0x300 && _NUITKA_NATIVE_WCHAR_ARGV == 0
static wchar_t **convertCommandLineParameters(int argc, char **argv) {
    // Originally taken from CPython3: There seems to be no sane way to use
    static wchar_t **argv_copy;
    argv_copy = (wchar_t **)PyMem_MALLOC(sizeof(wchar_t *) * argc);

    // Temporarily disable locale for conversions to not use it.
    char *old_locale = strdup(setlocale(LC_ALL, NULL));
    setlocale(LC_ALL, "");

    for (int i = 0; i < argc; i++) {
#if PYTHON_VERSION >= 0x350
        argv_copy[i] = Py_DecodeLocale(argv[i], NULL);
#elif defined(__APPLE__) && PYTHON_VERSION >= 0x300
        argv_copy[i] = _Py_DecodeUTF8_surrogateescape(argv[i], strlen(argv[i]));
#else
        argv_copy[i] = _Py_char2wchar(argv[i], NULL);
#endif

        assert(argv_copy[i]);
    }

    setlocale(LC_ALL, old_locale);
    free(old_locale);

    return argv_copy;
}
#endif

static int HANDLE_PROGRAM_EXIT(void) {
    int exit_code;

    PyThreadState *thread_state = PyThreadState_GET();

    if (HAS_ERROR_OCCURRED(thread_state)) {
#if PYTHON_VERSION >= 0x300
        /* Remove the frozen importlib traceback part, which would not be compatible. */

        while (thread_state->curexc_traceback) {
            PyTracebackObject *tb = (PyTracebackObject *)thread_state->curexc_traceback;
            PyFrameObject *frame = tb->tb_frame;

            if (0 == strcmp(PyUnicode_AsUTF8(Nuitka_Frame_GetCodeObject(frame)->co_filename),
                            "<frozen importlib._bootstrap>")) {
                thread_state->curexc_traceback = (PyObject *)tb->tb_next;
                Py_INCREF(tb->tb_next);

                continue;
            }

            break;
        }
#endif

        PyErr_PrintEx(0);

        exit_code = 1;
    } else {
        exit_code = 0;
    }

    return exit_code;
}

static PyObject *EXECUTE_MAIN_MODULE(char const *module_name) {
    NUITKA_INIT_PROGRAM_LATE(module_name);

#if NUITKA_MAIN_PACKAGE_MODE
    {
        char const *w = module_name;

        for (;;) {
            char const *s = strchr(w, '.');

            if (s == NULL) {
                break;
            }

            w = s + 1;

            char buffer[1024];
            memset(buffer, 0, sizeof(buffer));
            memcpy(buffer, module_name, s - module_name);

            PyObject *result = IMPORT_EMBEDDED_MODULE(buffer);

            if (ERROR_OCCURRED()) {
                return result;
            }
        }
    }
#endif

    return IMPORT_EMBEDDED_MODULE(module_name);
}

#ifdef _NUITKA_PLUGIN_WINDOWS_SERVICE_ENABLED
#include "nuitka_windows_service.h"

// Callback from Windows Service logic.
bool SvcStartPython(void) {
    EXECUTE_MAIN_MODULE(NUITKA_MAIN_MODULE_NAME);

    if (ERROR_OCCURRED()) {
        return true;
    } else {
        return false;
    }
}

void SvcStopPython(void) { PyErr_SetInterrupt(); }

#endif

// This is a multiprocessing fork
static bool is_multiprocessing_fork = false;
// This is a multiprocessing resource tracker if not -1
static int multiprocessing_resource_tracker_arg = -1;

// Parse the command line parameters and provide it to "sys" built-in module,
// as well as decide if it's a multiprocessing usage.
#if _NUITKA_NATIVE_WCHAR_ARGV == 0
static void setCommandLineParameters(int argc, char **argv, bool initial) {
#else
static void setCommandLineParameters(int argc, wchar_t **argv, bool initial) {
#endif
    if (initial) {
        /* We might need to handle special parameters from plugins that are
           very deeply woven into command line handling. These are right now
           multiprocessing, which indicates that it's forking via extra
           command line argument. And Windows Service indicates need to
           install and exit here too.
         */
        for (int i = 1; i < argc; i++) {
#if _NUITKA_NATIVE_WCHAR_ARGV == 0
            if ((strcmp(argv[i], "--multiprocessing-fork")) == 0 && (i + 1 < argc))
#else
            if ((wcscmp(argv[i], L"--multiprocessing-fork")) == 0 && (i + 1 < argc))
#endif
            {
                is_multiprocessing_fork = true;
                break;
            }

#if _NUITKA_NATIVE_WCHAR_ARGV == 0
            if ((strcmp(argv[i], "--multiprocessing-resource-tracker")) == 0 && (i + 1 < argc))
#else
            if ((wcscmp(argv[i], L"--multiprocessing-resource-tracker")) == 0 && (i + 1 < argc))
#endif
            {
#if _NUITKA_NATIVE_WCHAR_ARGV == 0
                multiprocessing_resource_tracker_arg = atoi(argv[i + 1]);
#else
                multiprocessing_resource_tracker_arg = _wtoi(argv[i + 1]);
#endif
                break;
            }

#ifdef _NUITKA_PLUGIN_WINDOWS_SERVICE_ENABLED
            if (i == 1) {
#if PYTHON_VERSION < 0x300
                if (strcmp(argv[i], "install") == 0)
#else
                if (wcscmp(argv[i], L"install") == 0)
#endif
                {
                    NUITKA_PRINT_TRACE("main(): Calling plugin SvcInstall().");

                    SvcInstall();
                    NUITKA_CANNOT_GET_HERE("SvcInstall must not return");
                }
            }
#endif
        }
    }
}

#if _DEBUG_REFCOUNTS
static void PRINT_REFCOUNTS(void) {
    PRINT_STRING("REFERENCE counts at program end:\n");
    PRINT_STRING("active | allocated | released\n");
#if PYTHON_VERSION >= 0x350
    PRINT_FORMAT("Compiled Coroutines: %d | %d | %d\n", count_active_Nuitka_Coroutine_Type,
                 count_allocated_Nuitka_Coroutine_Type, count_released_Nuitka_Coroutine_Type);
    PRINT_FORMAT("Compiled Coroutines Wrappers: %d | %d | %d\n", count_active_Nuitka_CoroutineWrapper_Type,
                 count_allocated_Nuitka_CoroutineWrapper_Type, count_released_Nuitka_CoroutineWrapper_Type);

    PRINT_FORMAT("Compiled Coroutines AIter Wrappers: %d | %d | %d\n", count_active_Nuitka_AIterWrapper_Type,
                 count_allocated_Nuitka_AIterWrapper_Type, count_released_Nuitka_AIterWrapper_Type);
#endif
#if PYTHON_VERSION >= 0x360
    PRINT_FORMAT("Compiled Asyncgen: %d | %d | %d\n", count_active_Nuitka_Asyncgen_Type,
                 count_allocated_Nuitka_Asyncgen_Type, count_released_Nuitka_Asyncgen_Type);
    PRINT_FORMAT("Compiled Asyncgen Wrappers: %d | %d | %d\n", count_active_Nuitka_AsyncgenValueWrapper_Type,
                 count_allocated_Nuitka_AsyncgenValueWrapper_Type, count_released_Nuitka_AsyncgenValueWrapper_Type);
    PRINT_FORMAT("Compiled Asyncgen Asend: %d | %d | %d\n", count_active_Nuitka_AsyncgenAsend_Type,
                 count_allocated_Nuitka_AsyncgenAsend_Type, count_released_Nuitka_AsyncgenAsend_Type);
    PRINT_FORMAT("Compiled Asyncgen Athrow: %d | %d | %d\n", count_active_Nuitka_AsyncgenAthrow_Type,
                 count_allocated_Nuitka_AsyncgenAthrow_Type, count_released_Nuitka_AsyncgenAthrow_Type);
#endif

    PRINT_FORMAT("Compiled Frames: %d | %d | %d (cache usage may occur)\n", count_active_Nuitka_Frame_Type,
                 count_allocated_Nuitka_Frame_Type, count_released_Nuitka_Frame_Type);
    PRINT_STRING("CACHED counts at program end:\n");
    PRINT_STRING("active | allocated | released | hits\n");
    PRINT_FORMAT("Cached Frames: %d | %d | %d | %d\n", count_active_frame_cache_instances,
                 count_allocated_frame_cache_instances, count_released_frame_cache_instances,
                 count_hit_frame_cache_instances);
}
#endif

#if defined(_NUITKA_ONEFILE_MODE) && defined(_WIN32)

static long onefile_ppid;

DWORD WINAPI doOnefileParentMonitoring(LPVOID lpParam) {
    NUITKA_PRINT_TRACE("Onefile parent monitoring starts.");

    for (;;) {
        Sleep(1000);

        HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, onefile_ppid);

        if (handle == NULL) {
            if (GetLastError() == ERROR_INVALID_PARAMETER) {
                break;
            } else {
                continue;
            }
        } else {
            DWORD ret = WaitForSingleObject(handle, 0);

            CloseHandle(handle);

            if (ret == WAIT_OBJECT_0) {
                break;
            }
        }
    }

    NUITKA_PRINT_TRACE("Onefile parent monitoring causes KeyboardInterrupt.");
    PyErr_SetInterrupt();

    Sleep(_NUITKA_ONEFILE_CHILD_GRACE_TIME_INT);

    NUITKA_PRINT_TRACE("Onefile parent monitoring hard kills after ignored KeyboardInterrupt.");
    ExitProcess(1);

    return 0;
}
#endif

#if defined(_WIN32) && PYTHON_VERSION < 0x300
static char **getCommandLineToArgvA(char *lpCmdline) {
    char *s = lpCmdline;

    int argc = 1;

    if (*s == '"') {
        s++;

        while (*s != 0) {
            if (*s++ == '"') {
                break;
            }
        }
    } else {
        while (*s != 0 && *s != ' ' && *s != '\t') {
            s++;
        }
    }

    while (*s == ' ' || *s == '\t') {
        s++;
    }

    if (*s != 0) {
        argc++;
    }

    int quote_count = 0;
    int slash_count = 0;

    while (*s != 0) {
        if ((*s == ' ' || *s == '\t') && quote_count == 0) {
            while (*s == ' ' || *s == '\t') {
                s++;
            }

            if (*s != 0) {
                argc++;
            }
            slash_count = 0;
        } else if (*s == '\\') {
            slash_count++;
            s++;
        } else if (*s == '"') {
            if ((slash_count & 1) == 0) {
                quote_count++;
            }

            slash_count = 0;
            s++;

            while (*s == '"') {
                quote_count++;
                s++;
            }

            quote_count = quote_count % 3;

            if (quote_count == 2) {
                quote_count = 0;
            }
        } else {
            slash_count = 0;
            s++;
        }
    }

    char **argv = (char **)malloc((argc + 1) * sizeof(char *) + (strlen(lpCmdline) + 1));
    assert(argv);

    char *cmdline = (char *)(argv + argc + 1);
    strcpy(cmdline, lpCmdline);

    argv[0] = cmdline;
    argc = 1;

    char *d = cmdline;

    if (*d == '"') {
        s = d + 1;

        while (*s != 0) {
            if (*s == '"') {
                s++;
                break;
            }

            *d++ = *s++;
        }
    } else {
        while (*d && *d != ' ' && *d != '\t') {
            d++;
        }

        s = d;

        if (*s) {
            s++;
        }
    }

    *d++ = 0;

    while (*s == ' ' || *s == '\t') {
        s++;
    }

    if (*s == 0) {
        argv[argc] = NULL;
        return argv;
    }

    argv[argc++] = d;
    quote_count = 0;
    slash_count = 0;

    while (*s != 0) {
        if ((*s == ' ' || *s == '\t') && quote_count == 0) {
            *d++ = 0;
            slash_count = 0;

            do {
                s++;
            } while (*s == ' ' || *s == '\t');

            if (*s) {
                argv[argc++] = d;
            }

        } else if (*s == '\\') {
            *d++ = *s++;
            slash_count++;
        } else if (*s == '"') {
            if ((slash_count & 1) == 0) {
                d -= slash_count / 2;
                quote_count++;
            } else {
                d = d - slash_count / 2 - 1;
                *d++ = '"';
            }
            s++;
            slash_count = 0;

            while (*s == '"') {
                if (++quote_count == 3) {
                    *d++ = '"';
                    quote_count = 0;
                }
                s++;
            }
            if (quote_count == 2)
                quote_count = 0;
        } else {
            *d++ = *s++;
            slash_count = 0;
        }
    }

    *d = '\0';
    argv[argc] = NULL;

    return argv;
}
#endif

// Disable wild card expansion for MinGW64, spell-checker: ignore _dowildcard
#if defined(__MINGW64__) || defined(__MINGW32__)
int _dowildcard = 0;
#endif

#ifdef _WIN32
static void setStdFileHandleNumber(DWORD std_handle_id, PyObject *file_handle) {
    PyObject *file_no_value = CALL_METHOD_NO_ARGS(file_handle, const_str_plain_fileno);

    if (unlikely(file_no_value == NULL)) {
        DROP_ERROR_OCCURRED();
        return;
    }

    long file_number = PyLong_AsLong(file_no_value);

    Py_DECREF(file_no_value);

    if (unlikely(file_number == -1 && ERROR_OCCURRED())) {
        DROP_ERROR_OCCURRED();
        return;
    }

    // Casting from long to handle gives warnings if not using a suitable
    // sized integer type in between.
    if (std_handle_id != STD_INPUT_HANDLE) {
        SetStdHandle(std_handle_id, (HANDLE)(intptr_t)file_number);
    }
}
#endif

static bool shallSetOutputHandleToNull(char const *name) {
#if NUITKA_FORCED_STDOUT_NULL_BOOL
    if (strcmp(name, "stdout") == 0) {
        return true;
    }
#endif

#if NUITKA_FORCED_STDERR_NULL_BOOL
    if (strcmp(name, "stderr") == 0) {
        return true;
    }
#endif

    PyObject *sys_std_handle = Nuitka_SysGetObject(name);
    if (sys_std_handle == NULL || sys_std_handle == Py_None) {
        return true;
    }

    return false;
}

static void setStdinHandle(PyObject *stdin_file) {

    CHECK_OBJECT(stdin_file);
    Nuitka_SysSetObject("stdin", stdin_file);

#ifdef _WIN32
    setStdFileHandleNumber(STD_INPUT_HANDLE, stdin_file);
#endif
}

static void setStdoutHandle(PyObject *stdout_file) {
    CHECK_OBJECT(stdout_file);
    Nuitka_SysSetObject("stdout", stdout_file);

#ifdef _WIN32
    setStdFileHandleNumber(STD_OUTPUT_HANDLE, stdout_file);
#endif
}

static void setStderrHandle(PyObject *stderr_file) {
    CHECK_OBJECT(stderr_file);

    Nuitka_SysSetObject("stderr", stderr_file);

#ifdef _WIN32
    setStdFileHandleNumber(STD_ERROR_HANDLE, stderr_file);
#endif
}

#if NUITKA_STANDARD_HANDLES_EARLY == 0
#if defined(NUITKA_FORCED_STDOUT_PATH) || defined(NUITKA_FORCED_STDERR_PATH)
#ifdef _WIN32
static PyObject *getExpandedTemplatePath(wchar_t const *template_path) {
    wchar_t filename_buffer[1024];
    bool res = expandTemplatePathW(filename_buffer, template_path, sizeof(filename_buffer) / sizeof(wchar_t));

    if (res == false) {
        puts("Error, couldn't expand pattern:");
        abort();
    }

    return NuitkaUnicode_FromWideChar(filename_buffer, -1);
}
#else
static PyObject *getExpandedTemplatePath(char const *template) {
    char filename_buffer[1024];
    bool res = expandTemplatePath(filename_buffer, template, sizeof(filename_buffer));

    if (res == false) {
        printf("Error, couldn't expand pattern: %s\n", template);
        abort();
    }

    return Nuitka_String_FromString(filename_buffer);
}
#endif
#endif
#endif

static void setInputOutputHandles(void) {
    // We support disabling the stdout/stderr through options as well as
    // building for GUI on Windows, which has inputs disabled by default, this
    // code repairs that by setting or forcing them to "os.devnull"
    // input/outputs

    // This defaults to "utf-8" internally. We may add an argument of use
    // platform ones in the future.
    PyObject *encoding = NULL;

// Reconfigure stdout for line buffering, for mixing traces and Python IO
// better, and force it to utf-8, it often becomes platform IO for no good
// reason.
#if NUITKA_STANDARD_HANDLES_EARLY == 1 && PYTHON_VERSION >= 0x370
#if defined(NUITKA_FORCED_STDOUT_PATH) || defined(NUITKA_FORCED_STDERR_PATH)
    PyObject *args = MAKE_DICT_EMPTY();

    DICT_SET_ITEM(args, const_str_plain_encoding, Nuitka_String_FromString("utf-8"));
    DICT_SET_ITEM(args, const_str_plain_line_buffering, Py_True);

#if defined(NUITKA_FORCED_STDOUT_PATH)
    {
        PyObject *sys_stdout = Nuitka_SysGetObject("stdout");

        PyObject *method = LOOKUP_ATTRIBUTE(sys_stdout, const_str_plain_reconfigure);
        CHECK_OBJECT(method);

        PyObject *result = CALL_FUNCTION_WITH_KEYARGS(method, args);
        CHECK_OBJECT(result);
    }
#endif

#if defined(NUITKA_FORCED_STDERR_PATH)
    {
        PyObject *sys_stderr = Nuitka_SysGetObject("stderr");

        PyObject *method = LOOKUP_ATTRIBUTE(sys_stderr, const_str_plain_reconfigure);
        CHECK_OBJECT(method);

        PyObject *result = CALL_FUNCTION_WITH_KEYARGS(method, args);
        CHECK_OBJECT(result);
    }
#endif

    Py_DECREF(args);
#endif
#endif

#if NUITKA_STANDARD_HANDLES_EARLY == 0
#if defined(NUITKA_FORCED_STDOUT_PATH)
    {
#ifdef _WIN32
        PyObject *filename = getExpandedTemplatePath(L"" NUITKA_FORCED_STDOUT_PATH);
#else
        PyObject *filename = getExpandedTemplatePath(NUITKA_FORCED_STDOUT_PATH);
#endif
        PyObject *stdout_file = BUILTIN_OPEN_SIMPLE(filename, "w", SYSFLAG_UNBUFFERED != 1, encoding);
        if (unlikely(stdout_file == NULL)) {
            PyErr_PrintEx(1);
            Py_Exit(1);
        }

        setStdoutHandle(stdout_file);
    }
#endif

#if defined(NUITKA_FORCED_STDERR_PATH)
    {
#ifdef _WIN32
        PyObject *filename = getExpandedTemplatePath(L"" NUITKA_FORCED_STDERR_PATH);
#else
        PyObject *filename = getExpandedTemplatePath(NUITKA_FORCED_STDERR_PATH);
#endif
        PyObject *stderr_file = BUILTIN_OPEN_SIMPLE(filename, "w", false, encoding);
        if (unlikely(stderr_file == NULL)) {
            PyErr_PrintEx(1);
            Py_Exit(1);
        }

        setStderrHandle(stderr_file);
    }
#endif
#endif

    {
#if defined(_WIN32)
        PyObject *devnull_filename = Nuitka_String_FromString("NUL:");
#else
        PyObject *devnull_filename = Nuitka_String_FromString("/dev/null");
#endif

        if (shallSetOutputHandleToNull("stdin")) {
            // CPython core requires stdin to be buffered due to methods usage, and it won't matter
            // here much.
            PyObject *stdin_file = BUILTIN_OPEN_SIMPLE(devnull_filename, "r", true, encoding);

            setStdinHandle(stdin_file);
        }

        if (shallSetOutputHandleToNull("stdout")) {
            PyObject *stdout_file = BUILTIN_OPEN_SIMPLE(devnull_filename, "w", false, encoding);

            setStdoutHandle(stdout_file);
        }

        if (shallSetOutputHandleToNull("stderr")) {
            PyObject *stderr_file = BUILTIN_OPEN_SIMPLE(devnull_filename, "w", false, encoding);

            setStderrHandle(stderr_file);
        }

        Py_DECREF(devnull_filename);
    }

#if NUITKA_FORCED_STDOUT_NONE_BOOL
    setStdoutHandle(Py_None);
#endif

#if NUITKA_FORCED_STDERR_NONE_BOOL
    setStderrHandle(Py_None);
#endif

    Py_XDECREF(encoding);
}

static void Nuitka_Py_Initialize(void) {
#if PYTHON_VERSION < 0x380 || defined(_NUITKA_EXPERIMENTAL_OLD_PY_INITIALIZE)
    Py_Initialize();
#else
    PyStatus status = _PyRuntime_Initialize();
    if (unlikely(status._type != 0)) {
        Py_ExitStatusException(status);
    }
    _PyRuntimeState *runtime = &_PyRuntime;

    assert(!runtime->initialized);

    PyConfig config;
    _PyConfig_InitCompatConfig(&config);

    assert(orig_argv[0]);
    status = PyConfig_SetArgv(&config, orig_argc, orig_argv);

    if (PyStatus_Exception(status)) {
        Py_ExitStatusException(status);
    }

#ifdef _NUITKA_STANDALONE
    wchar_t *binary_directory = (wchar_t *)getBinaryDirectoryWideChars(true);

    PyConfig_SetString(&config, &config.executable, orig_argv[0]);
    PyConfig_SetString(&config, &config.prefix, binary_directory);
    PyConfig_SetString(&config, &config.exec_prefix, binary_directory);
    PyConfig_SetString(&config, &config.base_prefix, binary_directory);
    PyConfig_SetString(&config, &config.base_exec_prefix, binary_directory);
    PyConfig_SetString(&config, &config.home, binary_directory);
#if PYTHON_VERSION >= 0x390
    PyConfig_SetString(&config, &config.platlibdir, binary_directory);
#endif

    PyWideStringList_Append(&config.module_search_paths, binary_directory);
    config.module_search_paths_set = 1;
#endif

    config.install_signal_handlers = 1;

    NUITKA_PRINT_TIMING("Nuitka_Py_Initialize(): Calling Py_InitializeFromConfig.");

    status = Py_InitializeFromConfig(&config);
    if (unlikely(status._type != 0)) {
        Py_ExitStatusException(status);
    }

#ifdef _NUITKA_STANDALONE
    assert(wcscmp(config.exec_prefix, binary_directory) == 0);

// Empty "sys.path" first time, will be revived, but keep it
// short lived.
#if SYSFLAG_ISOLATED
    Nuitka_SysSetObject("path", PyList_New(0));
#endif
#endif

#endif
}

#include <fcntl.h>

#if NUITKA_STANDARD_HANDLES_EARLY == 1
#if defined(_WIN32)

static void changeStandardHandleTarget(int std_handle_id, FILE *std_handle, filename_char_t const *template) {
    filename_char_t filename_buffer[1024];

    // TODO: We should only have one that works with filename_char_t rather than having
    // to make a difference here.
#ifdef _WIN32
    bool res = expandTemplatePathW(filename_buffer, template, sizeof(filename_buffer) / sizeof(filename_char_t));

    if (res == false) {
        wprintf(L"Error, couldn't expand pattern '%lS'\n", template);
        abort();
    }
#else
    bool res = expandTemplatePath(filename_buffer, template, sizeof(filename_buffer) / sizeof(filename_char_t));

    if (res == false) {
        printf("Error, couldn't expand pattern: '%s'\n", template);
        abort();
    }
#endif

    if (GetStdHandle(std_handle_id) == 0) {
        FILE *file_handle;

        if (std_handle_id == STD_INPUT_HANDLE) {
            file_handle = _wfreopen(filename_buffer, L"rb", std_handle);
        } else {
            file_handle = _wfreopen(filename_buffer, L"wb", std_handle);
        }

        if (file_handle == NULL) {
            perror("_wfreopen");
            abort();
        }

        BOOL r = SetStdHandle(std_handle_id, (HANDLE)_get_osfhandle(fileno(file_handle)));
        assert(r);

        *std_handle = *file_handle;

        assert(fileno(file_handle) == fileno(std_handle));

        int stdout_dup = dup(fileno(std_handle));
        if (stdout_dup >= 0) {
            close(stdout_dup);
        }

        DWORD mode = 0;
        if (GetConsoleMode((HANDLE)_get_osfhandle(fileno(std_handle)), &mode)) {
            exit(66);
        }
    } else {
        HANDLE w = CreateFileW(filename_buffer, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                               CREATE_ALWAYS, 0, NULL);

        if (w == INVALID_HANDLE_VALUE) {
            printOSErrorMessage("standard handle failed to create", GetLastError());
            abort();
        }
        BOOL r = SetStdHandle(std_handle_id, w);
        assert(r);

        int os_handle = _open_osfhandle((intptr_t)GetStdHandle(std_handle_id), O_WRONLY | O_TEXT);
        if (os_handle == -1) {
            perror("_open_osfhandle");
            abort();
        }

        int int_res = dup2(os_handle, fileno(std_handle));

        // Without a console, this is normal.
        if (int_res == -1) {
            perror("_open_osfhandle");
            abort();
        }

        close(os_handle);
    }

    setvbuf(std_handle, NULL, _IOLBF, 4096);
}
#else
static void changeStandardHandleTarget(FILE *std_handle, filename_char_t const *template) {
    filename_char_t filename_buffer[1024];

    bool res = expandTemplatePath(filename_buffer, template, sizeof(filename_buffer) / sizeof(filename_char_t));

    if (res == false) {
        printf("Error, couldn't expand pattern: '%s'\n", template);
        abort();
    }

    int os_handle = open(filename_buffer, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (os_handle == -1) {
        perror("open");
        abort();
    }

    int int_res = dup2(os_handle, fileno(std_handle));
    if (int_res == -1) {
        perror("dup2");
        abort();
    }

    close(os_handle);
}
#endif
#endif

#ifdef _NUITKA_WINMAIN_ENTRY_POINT
int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpCmdLine, int nCmdShow) {
    /* MSVC, MINGW64 */
    int argc = __argc;
    wchar_t **argv = __wargv;
#else
#if defined(_WIN32)
int wmain(int argc, wchar_t **argv) {
#else
int main(int argc, char **argv) {
#endif
#endif

    // First things, set up stdout/stderr according to user specification.
#if NUITKA_STANDARD_HANDLES_EARLY == 1
#if defined(NUITKA_FORCED_STDOUT_PATH)
#ifdef _WIN32
    changeStandardHandleTarget(STD_OUTPUT_HANDLE, stdout, L"" NUITKA_FORCED_STDOUT_PATH);
#else
    changeStandardHandleTarget(stdout, NUITKA_FORCED_STDOUT_PATH);
#endif
#endif
#if defined(NUITKA_FORCED_STDERR_PATH)
#ifdef _WIN32
    changeStandardHandleTarget(STD_ERROR_HANDLE, stderr, L"" NUITKA_FORCED_STDERR_PATH);
#else
    changeStandardHandleTarget(stderr, NUITKA_FORCED_STDERR_PATH);
#endif
#endif
#if defined(NUITKA_FORCED_STDIN_PATH)
#ifdef _WIN32
    changeStandardHandleTarget(STD_INPUT_HANDLE, stdin, L"" NUITKA_FORCED_STDIN_PATH);
#else
    changeStandardHandleTarget(stdin, NUITKA_FORCED_STDIN_PATH);
#endif
#endif
#endif

#if SYSFLAG_UNBUFFERED == 1
    setbuf(stdin, (char *)NULL);
    setbuf(stdout, (char *)NULL);
    setbuf(stderr, (char *)NULL);

#if PYTHON_VERSION >= 0x300
    environment_char_t const *old_env_unbuffered = getEnvironmentVariable("PYTHONUNBUFFERED");
    setEnvironmentVariable("PYTHONUNBUFFERED", makeEnvironmentLiteral("1"));
#endif
#endif

    NUITKA_PRINT_TIMING("main(): Entered.");
    NUITKA_INIT_PROGRAM_EARLY(argc, argv);

#ifdef __FreeBSD__
    /* FP exceptions run in "no stop" mode by default */

    fp_except_t m;

    m = fpgetmask();
    fpsetmask(m & ~FP_X_OFL);
#endif

#ifdef _NUITKA_STANDALONE
    NUITKA_PRINT_TIMING("main(): Prepare standalone environment.");
    prepareStandaloneEnvironment();
#else

#endif

#if _NUITKA_FROZEN > 0
    prepareFrozenModules();
#endif

    /* Initialize CPython library environment. */
    Py_DebugFlag = 0;
#if PYTHON_VERSION < 0x300
    Py_Py3kWarningFlag = SYSFLAG_PY3K_WARNING;
    Py_DivisionWarningFlag = SYSFLAG_DIVISION_WARNING;
    Py_UnicodeFlag = SYSFLAG_UNICODE;
    Py_TabcheckFlag = 0;
#endif
    Py_InspectFlag = 0;
    Py_InteractiveFlag = 0;
    Py_OptimizeFlag = SYSFLAG_OPTIMIZE;
    Py_DontWriteBytecodeFlag = 0;
    Py_NoUserSiteDirectory = SYSFLAG_NO_SITE;
    Py_IgnoreEnvironmentFlag = 0;
    Py_VerboseFlag = SYSFLAG_VERBOSE;
    Py_BytesWarningFlag = SYSFLAG_BYTES_WARNING;
#if PYTHON_VERSION >= 0x300 && SYSFLAG_UNBUFFERED == 1
    Py_UnbufferedStdioFlag = SYSFLAG_UNBUFFERED;
#endif
#if SYSFLAG_NO_RANDOMIZATION == 1
    Py_HashRandomizationFlag = 0;
#if PYTHON_VERSION < 0x300
    // For Python2 this is all it takes to have static hashes.
    _PyRandom_Init();
#endif
#endif
#if PYTHON_VERSION >= 0x370
    Py_UTF8Mode = SYSFLAG_UTF8;

    if (Py_UTF8Mode) {
        if (Py_FileSystemDefaultEncoding == NULL) {
            Py_FileSystemDefaultEncoding = "utf-8";
            Py_HasFileSystemDefaultEncoding = 1;
        }
    }
#endif

#ifdef NUITKA_PYTHON_STATIC
    Py_InitStaticModules();
#endif

    /* This suppresses warnings from getpath.c */
    Py_FrozenFlag = 1;

    /* We want to import the site module, but only after we finished our own
     * setup. The site module import will be the first thing, the main module
     * does.
     */
    Py_NoSiteFlag = 1;

    /* Initial command line handling only. */

#if PYTHON_VERSION >= 0x300 && _NUITKA_NATIVE_WCHAR_ARGV == 0
    NUITKA_PRINT_TRACE("main(): Calling convertCommandLineParameters.");
    orig_argv = convertCommandLineParameters(argc, argv);
#elif PYTHON_VERSION < 0x300 && _NUITKA_NATIVE_WCHAR_ARGV == 1
    orig_argv = getCommandLineToArgvA(GetCommandLineA());
#else
orig_argv = argv;
#endif

    // Make sure the compiled path of Python is replaced.
    Py_SetProgramName(orig_argv[0]);

    orig_argc = argc;

    // Early command line parsing.
    NUITKA_PRINT_TRACE("main(): Calling initial setCommandLineParameters.");
    setCommandLineParameters(argc, argv, true);

    /* For Python installations that need the home set, we inject it back here. */
#if defined(PYTHON_HOME_PATH)
#if PYTHON_VERSION < 0x300
    NUITKA_PRINT_TRACE("main(): Prepare run environment '" PYTHON_HOME_PATH "'.");
    Py_SetPythonHome(PYTHON_HOME_PATH);
#else
    NUITKA_PRINTF_TRACE("main(): Prepare run environment '%S'.\n", L"" PYTHON_HOME_PATH);
    Py_SetPythonHome(L"" PYTHON_HOME_PATH);
    // Make sure the above Py_SetPythonHome call has effect already.
    Py_GetPath();
#endif
#endif

#if PYTHON_VERSION >= 0x300 && SYSFLAG_NO_RANDOMIZATION == 1
    environment_char_t const *old_env_hash_seed = getEnvironmentVariable("PYTHONHASHSEED");
    setEnvironmentVariable("PYTHONHASHSEED", makeEnvironmentLiteral("0"));
#endif

    /* Disable CPython warnings if requested to. */
#if NO_PYTHON_WARNINGS
    NUITKA_PRINT_TRACE("main(): Disabling Python warnings.");
    {
#if PYTHON_VERSION >= 0x300
        wchar_t ignore[] = L"ignore";
#else
        char ignore[] = "ignore";
#endif

        PySys_ResetWarnOptions();
        PySys_AddWarnOption(ignore);
    }
#endif

// Workaround older Python not handling stream setup on redirected files properly.
#if PYTHON_VERSION >= 0x340 && PYTHON_VERSION < 0x380
    {
        char const *encoding = NULL;

        if (SYSFLAG_UTF8) {
            encoding = "utf-8";
        } else {
            encoding = getenv("PYTHONIOENCODING");
            if (encoding == NULL) {
                encoding = "utf-8";
            }
        }

        Py_SetStandardStreamEncoding(encoding, NULL);
    }
#endif

    /* Initialize the embedded CPython interpreter. */
    NUITKA_PRINT_TIMING("main(): Calling Nuitka_Py_Initialize to initialize interpreter.");
    Nuitka_Py_Initialize();

#ifdef _NUITKA_STANDALONE
    NUITKA_PRINT_TRACE("main(): Restore standalone environment.");
    restoreStandaloneEnvironment();
#endif

    /* Lie about it, believe it or not, there are "site" files, that check
     * against later imports, see below.
     */
    Py_NoSiteFlag = SYSFLAG_NO_SITE;

    /* Set the command line parameters for run time usage. */
    NUITKA_PRINT_TRACE("main(): Calling setCommandLineParameters.");

    setCommandLineParameters(argc, argv, false);

    PySys_SetArgv(argc, orig_argv);
// Empty "sys.path" again, the above adds program directory to it.
#if SYSFLAG_ISOLATED
    Nuitka_SysSetObject("path", PyList_New(0));
#endif

    /* Initialize the built-in module tricks used and builtin-type methods */
    NUITKA_PRINT_TRACE("main(): Calling _initBuiltinModule().");
    _initBuiltinModule();

    /* Initialize the Python constant values used. This also sets
     * "sys.executable" while at it.
     */
    NUITKA_PRINT_TIMING("main(): Calling createGlobalConstants().");
    createGlobalConstants();
    NUITKA_PRINT_TIMING("main(): Returned createGlobalConstants().");

    /* Complex call helpers need "__main__" constants, even if we only
     * go into "__parents__main__" module as a start point.
     */
    NUITKA_PRINT_TIMING("main(): Calling createMainModuleConstants().");
    createMainModuleConstants();
    NUITKA_PRINT_TIMING("main(): Returned createMainModuleConstants().");

    NUITKA_PRINT_TRACE("main(): Calling _initBuiltinOriginalValues().");
    _initBuiltinOriginalValues();

    /* Revert the wrong "sys.flags" value, it's used by "site" on at least
     * Debian for Python 3.3, more uses may exist.
     */
#if SYSFLAG_NO_SITE == 0
#if PYTHON_VERSION < 0x300
    PyStructSequence_SET_ITEM(Nuitka_SysGetObject("flags"), 9, const_int_0);
#else
    PyStructSequence_SetItem(Nuitka_SysGetObject("flags"), 6, const_int_0);
#endif
#endif

    /* Initialize the compiled types of Nuitka. */
    _initCompiledCellType();
    _initCompiledGeneratorType();
    _initCompiledFunctionType();
    _initCompiledMethodType();
    _initCompiledFrameType();

    _initSlotCompare();
#if PYTHON_VERSION >= 0x270
    _initSlotIterNext();
#endif

    NUITKA_PRINT_TRACE("main(): Calling enhancePythonTypes().");
    enhancePythonTypes();

    NUITKA_PRINT_TRACE("main(): Calling patchTypeComparison().");
    patchTypeComparison();

    NUITKA_PRINT_TRACE("main(): Calling patchTracebackDealloc().");
    patchTracebackDealloc();

#ifndef NUITKA_USE_PYCORE_THREAD_STATE
    /* Allow to override the ticker value, to remove checks for threads in
     * CPython core from impact on benchmarks. */
    char const *ticker_value = getenv("NUITKA_TICKER");
    if (ticker_value != NULL) {
        _Py_Ticker = atoi(ticker_value);
        assert(_Py_Ticker >= 20);
    }
#endif

    NUITKA_PRINT_TRACE("main(): Setting Python input/output handles.");
    setInputOutputHandles();

#ifdef _NUITKA_STANDALONE

#if PYTHON_VERSION >= 0x300
    // Make sure the importlib fully bootstraps as we couldn't load it with the
    // standard loader.
    PyObject *importlib_module = getImportLibBootstrapModule();
    CHECK_OBJECT(importlib_module);
#endif

    NUITKA_PRINT_TRACE("main(): Calling setEarlyFrozenModulesFileAttribute().");

    setEarlyFrozenModulesFileAttribute();
#endif

#if _NUITKA_FROZEN > 0
    NUITKA_PRINT_TRACE("main(): Removing early frozen module table again.");
    PyImport_FrozenModules = old_frozen;
#endif

    NUITKA_PRINT_TRACE("main(): Calling setupMetaPathBasedLoader().");
    /* Enable meta path based loader. */
    setupMetaPathBasedLoader();

    /* Initialize warnings module. */
    _PyWarnings_Init();

#if NO_PYTHON_WARNINGS && PYTHON_VERSION >= 0x342 && PYTHON_VERSION < 0x3a0 && defined(_NUITKA_FULL_COMPAT)
    // For full compatibility bump the warnings registry version,
    // otherwise modules "__warningsregistry__" will mismatch.
    PyObject *warnings_module = PyImport_ImportModule("warnings");
    PyObject *meth = PyObject_GetAttrString(warnings_module, "_filters_mutated");

    CALL_FUNCTION_NO_ARGS(meth);
#if PYTHON_VERSION < 0x380
    // Two times, so "__warningregistry__" version matches.
    CALL_FUNCTION_NO_ARGS(meth);
#endif
#endif

#if PYTHON_VERSION >= 0x300
    NUITKA_PRINT_TRACE("main(): Calling patchInspectModule().");
    patchInspectModule();
#endif

#if PYTHON_VERSION >= 0x300 && SYSFLAG_NO_RANDOMIZATION == 1
    NUITKA_PRINT_TRACE("main(): Reverting to initial 'PYTHONHASHSEED' value.");
    undoEnvironmentVariable("PYTHONHASHSEED", old_env_hash_seed);
#endif

#if PYTHON_VERSION >= 0x300 && SYSFLAG_UNBUFFERED == 1
    NUITKA_PRINT_TRACE("main(): Reverting to initial 'PYTHONUNBUFFERED' value.");
    undoEnvironmentVariable("PYTHONUNBUFFERED", old_env_unbuffered);
#endif

#ifdef _NUITKA_STANDALONE
    // Restore the PATH, so the program can use it.
    NUITKA_PRINT_TRACE("main(): Reverting to initial 'PATH' value.");
    undoEnvironmentVariable("PATH", old_env_path);
    undoEnvironmentVariable("PYTHONHOME", old_env_pythonhome);
#endif

#if _NUITKA_PROFILE
    // Profiling with "vmprof" if enabled.
    startProfiling();
#endif

#if _NUITKA_PGO_PYTHON
    // Profiling with our own Python PGO if enabled.
    PGO_Initialize();
#endif

    /* Execute the main module unless plugins want to do something else. In case of
       multiprocessing making a fork on Windows, we should execute "__parents_main__"
       instead. And for Windows Service we call the plugin C code to call us back
       to launch main code in a callback. */
#ifdef _NUITKA_PLUGIN_MULTIPROCESSING_ENABLED
    if (unlikely(is_multiprocessing_fork)) {
        NUITKA_PRINT_TRACE("main(): Calling __parents_main__.");
        EXECUTE_MAIN_MODULE("__parents_main__");

        int exit_code = HANDLE_PROGRAM_EXIT();

        NUITKA_PRINT_TRACE("main(): Calling __parents_main__ Py_Exit.");

        // TODO: Should maybe call Py_Exit here, but there were issues with that.
        exit(exit_code);
    } else if (unlikely(multiprocessing_resource_tracker_arg != -1)) {
        NUITKA_PRINT_TRACE("main(): Calling resource_tracker.");
        PyObject *resource_tracker_module = EXECUTE_MAIN_MODULE("multiprocessing.resource_tracker");

        PyObject *main_function = PyObject_GetAttrString(resource_tracker_module, "main");

        CALL_FUNCTION_WITH_SINGLE_ARG(main_function, PyInt_FromLong(multiprocessing_resource_tracker_arg));

        int exit_code = HANDLE_PROGRAM_EXIT();

        NUITKA_PRINT_TRACE("main(): Calling resource_tracker Py_Exit.");
        // TODO: Should maybe call Py_Exit here, but there were issues with that.
        exit(exit_code);
    } else {
#endif
#if defined(_NUITKA_ONEFILE_MODE) && defined(_WIN32)
        {
            char buffer[128] = {0};
            DWORD size = GetEnvironmentVariableA("NUITKA_ONEFILE_PARENT", buffer, sizeof(buffer));

            if (size > 0 && size < 127) {
                onefile_ppid = atol(buffer);

                CreateThread(NULL, 0, doOnefileParentMonitoring, NULL, 0, NULL);
            }
        }
#endif
        PyDict_DelItemString(Nuitka_GetSysModules(), NUITKA_MAIN_MODULE_NAME);

#if _NUITKA_PLUGIN_WINDOWS_SERVICE_ENABLED
        NUITKA_PRINT_TRACE("main(): Calling plugin SvcLaunchService() entry point.");
        SvcLaunchService();
#else
    /* Execute the "__main__" module. */
    NUITKA_PRINT_TIMING("main(): Calling " NUITKA_MAIN_MODULE_NAME ".");
    EXECUTE_MAIN_MODULE(NUITKA_MAIN_MODULE_NAME);
    NUITKA_PRINT_TIMING("main(): Exited from " NUITKA_MAIN_MODULE_NAME ".");

#endif
#ifdef _NUITKA_PLUGIN_MULTIPROCESSING_ENABLED
    }
#endif

#if _NUITKA_PROFILE
    stopProfiling();
#endif

#if _NUITKA_PGO_PYTHON
    // Write out profiling with our own Python PGO if enabled.
    PGO_Finalize();
#endif

#ifndef __NUITKA_NO_ASSERT__
    checkGlobalConstants();

    /* TODO: Walk over all loaded compiled modules, and make this kind of checks. */
#if !NUITKA_MAIN_PACKAGE_MODE
    checkModuleConstants___main__();
#endif

#endif

    int exit_code = HANDLE_PROGRAM_EXIT();

#if _DEBUG_REFCOUNTS
    PRINT_REFCOUNTS();
#endif

    NUITKA_PRINT_TIMING("main(): Calling Py_Exit.");
    Py_Exit(exit_code);

    // The "Py_Exit()" calls is not supposed to return.
    NUITKA_CANNOT_GET_HERE("Py_Exit does not return");
}

/* This is an unofficial API, not available on Windows, but on Linux and others
 * it is exported, and has been used by some code.
 */
#if !defined(_WIN32) && !defined(__MSYS__)
#ifdef __cplusplus
extern "C" {
#endif

#if PYTHON_VERSION >= 0x300
#if defined(__GNUC__)
__attribute__((weak))
__attribute__((visibility("default")))
#endif
void Py_GetArgcArgv(int *argc, wchar_t ***argv) {
    *argc = orig_argc;

    *argv = orig_argv;
}
#else
#if defined(__GNUC__)
__attribute__((weak))
__attribute__((visibility("default")))
#endif
void Py_GetArgcArgv(int *argc, char ***argv) {
    *argc = orig_argc;
    *argv = orig_argv;
}
#endif

#ifdef __cplusplus
}
#endif
#endif
