1#     Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com
2#
3#     Part of "Nuitka", an optimizing Python compiler that is compatible and
4#     integrates with CPython, but also works on its own.
5#
6#     Licensed under the Apache License, Version 2.0 (the "License");
7#     you may not use this file except in compliance with the License.
8#     You may obtain a copy of the License at
9#
10#        http://www.apache.org/licenses/LICENSE-2.0
11#
12#     Unless required by applicable law or agreed to in writing, software
13#     distributed under the License is distributed on an "AS IS" BASIS,
14#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15#     See the License for the specific language governing permissions and
16#     limitations under the License.
17#
18""" Code generation for code objects.
19
20Right now only the creation is done here. But more should be added later on.
21"""
22
23import os
24
25from nuitka import Options
26
27
28def getCodeObjectsDeclCode(context):
29    statements = []
30
31    for _code_object_key, code_identifier in context.getCodeObjects():
32        declaration = "static PyCodeObject *%s;" % code_identifier
33
34        statements.append(declaration)
35
36    if context.getOwner().getFullName() == "__main__":
37        statements.append('/* For use in "MainProgram.c". */')
38        statements.append("PyCodeObject *codeobj_main = NULL;")
39
40    return statements
41
42
43def _getMakeCodeObjectArgs(code_object_handle, context):
44    """Code objects have many flags for creation.
45
46    This is also version dependent, but we hide this behind macros
47    that ignore some arguments.
48    """
49
50    co_flags = []
51
52    if code_object_handle.co_kind in ("Module", "Class", "Function"):
53        pass
54    elif code_object_handle.co_kind == "Generator":
55        co_flags.append("CO_GENERATOR")
56    elif code_object_handle.co_kind == "Coroutine":
57        co_flags.append("CO_COROUTINE")
58    elif code_object_handle.co_kind == "Asyncgen":
59        co_flags.append("CO_ASYNC_GENERATOR")
60    else:
61        assert False, code_object_handle.co_kind
62
63    if code_object_handle.is_optimized:
64        co_flags.append("CO_OPTIMIZED")
65
66    if code_object_handle.co_new_locals:
67        co_flags.append("CO_NEWLOCALS")
68
69    if code_object_handle.co_has_starlist:
70        co_flags.append("CO_VARARGS")
71
72    if code_object_handle.co_has_stardict:
73        co_flags.append("CO_VARKEYWORDS")
74
75    if not code_object_handle.co_freevars:
76        co_flags.append("CO_NOFREE")
77
78    co_flags.extend(code_object_handle.future_flags)
79
80    return (
81        code_object_handle.line_number,
82        " | ".join(co_flags) or "0",
83        context.getConstantCode(constant=code_object_handle.co_name),
84        context.getConstantCode(constant=code_object_handle.co_varnames)
85        if code_object_handle.co_varnames
86        else "NULL",
87        context.getConstantCode(constant=code_object_handle.co_freevars)
88        if code_object_handle.co_freevars
89        else "NULL",
90        code_object_handle.co_argcount,
91        code_object_handle.co_kwonlyargcount,
92        code_object_handle.co_posonlyargcount,
93    )
94
95
96def getCodeObjectsInitCode(context):
97    # There is a bit of details to this, and we are making some optimizations as
98    # well as customization to what path should be put there.
99
100    statements = []
101
102    code_objects = context.getCodeObjects()
103
104    # Create the always identical, but dynamic filename first thing.
105    module_filename = context.getOwner().getRunTimeFilename()
106
107    # We do not care about release of this object, as code object live
108    # forever anyway.
109    if Options.getFileReferenceMode() == "frozen" or os.path.isabs(module_filename):
110        template = "module_filename_obj = %s; CHECK_OBJECT(module_filename_obj);"
111    else:
112        template = "module_filename_obj = MAKE_RELATIVE_PATH(%s); CHECK_OBJECT(module_filename_obj);"
113
114    statements.append(template % (context.getConstantCode(constant=module_filename)))
115
116    for code_object_key, code_identifier in code_objects:
117        # Make sure the filename is always identical.
118        assert code_object_key.co_filename == module_filename, code_object_key
119
120        args = (
121            code_identifier,
122            ", ".join(str(s) for s in _getMakeCodeObjectArgs(code_object_key, context)),
123        )
124
125        code = "%s = MAKE_CODEOBJECT(module_filename_obj, %s);" % args
126
127        statements.append(code)
128
129        if context.getOwner().getFullName() == "__main__":
130            if code_object_key[1] == "<module>":
131                statements.append("codeobj_main = %s;" % code_identifier)
132
133    return statements
134