|
| 1 | +{# |
| 2 | +Note we don't use `cpdef` here since Cython would then add the Python flavour of |
| 3 | +those methods to any .pyx that include `builtins.pxd`. |
| 4 | +Worst: this would also make the compiler consider all `gdptr_*` symbols in |
| 5 | +`builtins.so` are used, hence bloating the final shared library of tons of useless |
| 6 | +symbols to resolve at runtime ! |
| 7 | +#} |
| 8 | + |
| 9 | + |
1 | 10 | {% macro render_method_signature(m) -%} |
2 | 11 | {# TODO: hack given currently event `GDObject` is lazy binded, so we have to use its parent `BaseGDObject` here... #} |
3 | 12 | {%- if m.return_type.is_nil -%}void{%- else -%}{{ "BaseGDObject" if m.return_type.cy_type == "GDObject" else m.return_type.cy_type }}{%- endif %} |
4 | | - {{ "_" + m.name if m.is_static else m.name }}( |
| 13 | + {{ m.name }}( |
5 | 14 | {%- if not m.is_static -%}self, {% endif -%} |
6 | | -{%- for arg in m.arguments -%} |
7 | | -{%- if arg.type.original_name in ("String", "StringName", "NodePath") -%} |
8 | | -object {{ arg.name }} |
| 15 | +{%- if m.is_vararg -%} |
| 16 | +{# cdef function doesn't support `*args` so we have to explicitly pass an iterable here instead #} |
| 17 | +object args |
9 | 18 | {%- else -%} |
| 19 | +{%- for arg in m.arguments -%} |
| 20 | +{%- if arg.type.original_name in ("String", "StringName", "NodePath") -%} |
| 21 | +object {{ arg.name }} |
| 22 | +{%- else -%} |
10 | 23 | {{ arg.type.cy_type }} {{ arg.name }} |
11 | | -{%- endif -%} |
12 | | -{%- if arg.default_value -%} |
| 24 | +{%- endif -%} |
| 25 | +{%- if arg.default_value -%} |
13 | 26 | ={{ arg.default_value.cy_value or "None" }} |
| 27 | +{%- endif -%} |
| 28 | +{%- if not loop.last -%}, {% endif -%} |
| 29 | +{%- endfor -%} |
14 | 30 | {%- endif -%} |
15 | | -{%- if not loop.last -%}, {% endif -%} |
16 | | -{%- endfor -%}) |
| 31 | +) |
17 | 32 | {%- endmacro %} |
18 | 33 |
|
19 | 34 |
|
20 | | -{% macro _render_non_static_method(builtin, m) %} |
21 | | -{# |
22 | | -Note we don't use `cpdef` here since Cython would then add the Python flavour of |
23 | | -those methods to any .pyx that include `builtins.pxd`. |
24 | | -Worst: this would also make the compiler consider all `gdptr_*` symbols in |
25 | | -`builtins.so` are used, hence bloating the final shared library of tons of useless |
26 | | -symbols to resolve at runtime ! |
27 | | -
|
28 | | -So instead we define each method twice: |
29 | | -- Once here that will become a static inline C function. Hence the compiler |
30 | | - will add it to any module importing `builtins.pxd` and discard automatically |
31 | | - if it is in fact never used in the module. |
32 | | -- Once in `builtins.pyx` as a regular Python method. |
33 | | -#} |
34 | | -cdef inline {{ render_method_signature(m) }}: |
35 | | -{% if m.contains_unsuported_types %} |
36 | | - raise NotImplementedError # TODO |
37 | | -{% else %} |
38 | | -{% for arg in m.arguments %} |
39 | | -{% if arg.type.is_variant %} |
40 | | - cdef gd_variant_t __arg{{ loop.index0 }} |
41 | | - __arg{{ loop.index0 }} = ensure_is_gdany_and_borrow_ref({{ arg.name }}) |
42 | | -{% elif arg.type.original_name == "String" %} |
43 | | - cdef GDString __arg{{ loop.index0 }} |
44 | | - __arg{{ loop.index0 }} = ensure_is_gdstring({{ arg.name }}) |
45 | | -{% elif arg.type.original_name == "StringName" %} |
46 | | - cdef StringName __arg{{ loop.index0 }} |
47 | | - __arg{{ loop.index0 }} = ensure_is_stringname({{ arg.name }}) |
48 | | -{% elif arg.type.original_name == "NodePath" %} |
49 | | - cdef NodePath __arg{{ loop.index0 }} |
50 | | - __arg{{ loop.index0 }} = ensure_is_nodepath({{ arg.name }}) |
| 35 | +{% macro _render_method_vararg(builtin, m) %} |
| 36 | +{% if m.is_static %} |
| 37 | +@staticmethod |
51 | 38 | {% endif %} |
52 | | -{% endfor %} |
53 | | -{% if m.return_type.is_scalar %} |
| 39 | +cdef inline {{ render_method_signature(m) }}: |
| 40 | +{# 1) Param cooking #} |
| 41 | + cdef gd_variant_t **gd_args_ptrs |
| 42 | + cdef gd_variant_t[8] gd_args_stack |
| 43 | + cdef (gd_variant_t *)[8] gd_args_ptrs_stack |
| 44 | + cdef gd_variant_t *gd_args_heap |
| 45 | + cdef gd_variant_t **gd_args_ptrs_heap |
| 46 | + cdef bint stack_args_passing = len(args) <= 8 |
| 47 | + if stack_args_passing: |
| 48 | + for i, arg in enumerate(args): |
| 49 | + gd_args_stack[i] = ensure_is_gdany_and_borrow_ref(arg) |
| 50 | + gd_args_ptrs_stack[i] = &gd_args_stack[i] |
| 51 | + gd_args_ptrs = <gd_variant_t **>&gd_args_ptrs_stack |
| 52 | + else: |
| 53 | + gd_args_heap = <gd_variant_t*>PyMem_Malloc(sizeof(gd_variant_t) * len(args)) |
| 54 | + gd_args_ptrs_heap = <gd_variant_t**>PyMem_Malloc(sizeof(gd_variant_t*) * len(args)) |
| 55 | + for i, arg in enumerate(args): |
| 56 | + gd_args_heap[i] = ensure_is_gdany_and_borrow_ref(arg) |
| 57 | + gd_args_ptrs_heap[i] = &gd_args_heap[i] |
| 58 | + gd_args_ptrs = gd_args_ptrs_heap |
| 59 | +{# 2) Actual function call with return value initialization #} |
| 60 | +{% if m.return_type.is_scalar %} |
54 | 61 | cdef {{m.return_type.c_type }} ret = gdapi.gd_{{ builtin.snake_name }}_meth_{{ m.name }}( |
55 | | -{% elif m.return_type.is_builtin %} |
| 62 | +{% elif m.return_type.is_builtin %} |
56 | 63 | # Call to __new__ bypasses __init__ constructor |
57 | 64 | cdef {{ m.return_type.cy_type }} ret = {{ m.return_type.cy_type }}.__new__({{ m.return_type.cy_type }}) |
58 | 65 | ret._gd_data = gdapi.gd_{{ builtin.snake_name }}_meth_{{ m.name }}( |
59 | | -{% elif m.return_type.is_object or m.return_type.is_variant %} |
| 66 | +{% elif m.return_type.is_object or m.return_type.is_variant %} |
60 | 67 | cdef {{ m.return_type.c_type }} ret = gdapi.gd_{{ builtin.snake_name }}_meth_{{ m.name }}( |
61 | | -{% else %} |
| 68 | +{% else %} |
62 | 69 | gdapi.gd_{{ builtin.snake_name }}_meth_{{ m.name }}( |
63 | | -{% endif %} |
| 70 | +{% endif %} |
| 71 | +{% if m.is_static %} |
| 72 | + NULL, |
| 73 | +{% else %} |
64 | 74 | &self._gd_data, |
65 | | -{% for arg in m.arguments %} |
66 | | -{% if arg.type.is_scalar %} |
67 | | - {{ arg.name }}, |
68 | | -{% elif arg.type.original_name in ("String", "StringName", "NodePath") %} |
69 | | - &__arg{{ loop.index0 }}._gd_data, |
70 | | -{% elif arg.type.is_builtin %} |
71 | | - &{{ arg.name }}._gd_data, |
72 | | -{% elif arg.type.is_object %} |
73 | | - &{{ arg.name }}._gd_ptr, |
74 | | -{% elif arg.type.is_variant %} |
75 | | - &__arg{{ loop.index0 }}, |
76 | | -{% else %} |
77 | | - <crash !!!!> # {{ arg.type }} Unsupported type, crash the compilation ! |
78 | | -{% endif %} |
79 | | -{% endfor %} |
| 75 | +{% endif %} |
| 76 | +{# 3) Parameter provided to the function call #} |
| 77 | + gd_args_ptrs, |
| 78 | + len(args), |
80 | 79 | ) |
81 | | -{% if not m.return_type.is_nil %} |
82 | | -{% if m.return_type.is_object %} |
| 80 | + # Note we don't need to destroy the variants since they have only borrowed their data from the PyObjects |
| 81 | + if not stack_args_passing: |
| 82 | + PyMem_Free(gd_args_heap) |
| 83 | + PyMem_Free(gd_args_ptrs_heap) |
| 84 | +{# 4) Optional return value conversion #} |
| 85 | +{% if not m.return_type.is_nil %} |
| 86 | +{% if m.return_type.is_object %} |
83 | 87 | return BaseGDObject.cast_from_object(ret) |
84 | | -{% elif m.return_type.is_variant %} |
85 | | - # Note `gd_variant_steal_into_pyobj(&ret)` calls `ret`'s destructor |
| 88 | +{% elif m.return_type.is_variant %} |
| 89 | + # Note the "steal" means we don't need a final `gdapi.gd_variant_del(&ret)` |
86 | 90 | return gd_variant_steal_into_pyobj(&ret) |
87 | | -{% else %} |
| 91 | +{% else %} |
88 | 92 | return ret |
89 | | -{% endif %} |
90 | | -{% endif %} |
91 | 93 | {% endif %} |
| 94 | +{% endif %} |
92 | 95 | {% endmacro %} |
93 | 96 |
|
94 | 97 |
|
95 | | -{# TODO: Cython currently doesn't support cpdef with @staticmethod #} |
96 | | -{# see: https://github.com/cython/cython/issues/3327 #} |
97 | | -{% macro _render_static_method(builtin, m) %} |
| 98 | +{% macro _render_method_standard(builtin, m) %} |
| 99 | +{% if m.is_static %} |
98 | 100 | @staticmethod |
99 | | -cdef inline {{ render_method_signature(m) }}: |
100 | | -{% if m.contains_unsuported_types %} |
101 | | - raise NotImplementedError # TODO |
102 | | -{% else %} |
103 | | -{% for arg in m.arguments %} |
104 | | -{% if arg.type.is_variant %} |
105 | | - cdef gd_variant_t __arg{{ loop.index0 }} |
106 | | - __arg{{ loop.index0 }} = ensure_is_gdany_and_borrow_ref({{ arg.name }}) |
107 | | -{% elif arg.type.original_name == "String" %} |
108 | | - cdef GDString __arg{{ loop.index0 }} |
109 | | - __arg{{ loop.index0 }} = ensure_is_gdstring({{ arg.name }}) |
110 | | -{% elif arg.type.original_name == "StringName" %} |
111 | | - cdef StringName __arg{{ loop.index0 }} |
112 | | - __arg{{ loop.index0 }} = ensure_is_stringname({{ arg.name }}) |
113 | | -{% elif arg.type.original_name == "NodePath" %} |
114 | | - cdef NodePath __arg{{ loop.index0 }} |
115 | | - __arg{{ loop.index0 }} = ensure_is_nodepath({{ arg.name }}) |
116 | 101 | {% endif %} |
| 102 | +cdef inline {{ render_method_signature(m) }}: |
| 103 | +{# 1) Param cooking #} |
| 104 | +{% for arg in m.arguments %} |
| 105 | +{% if arg.type.is_variant %} |
| 106 | +{# Note the `__` prefix in those variables to avoid clashing with the arguments #} |
| 107 | + cdef gd_variant_t __arg_{{ arg.name }} |
| 108 | + __arg_{{ arg.name }} = ensure_is_gdany_and_borrow_ref({{ arg.name }}) |
| 109 | +{% elif arg.type.original_name == "String" %} |
| 110 | + cdef GDString __arg_{{ arg.name }} |
| 111 | + __arg_{{ arg.name }} = ensure_is_gdstring({{ arg.name }}) |
| 112 | +{% elif arg.type.original_name == "StringName" %} |
| 113 | + cdef StringName __arg_{{ arg.name }} |
| 114 | + __arg_{{ arg.name }} = ensure_is_stringname({{ arg.name }}) |
| 115 | +{% elif arg.type.original_name == "NodePath" %} |
| 116 | + cdef NodePath __arg_{{ arg.name }} |
| 117 | + __arg_{{ arg.name }} = ensure_is_nodepath({{ arg.name }}) |
| 118 | +{% endif %} |
117 | 119 | {% endfor %} |
118 | | -{% if m.return_type.is_scalar %} |
119 | | - cdef {{ m.return_type.cy_type }} ret = gdapi.gd_{{ builtin.snake_name }}_meth_{{ m.name }}( |
120 | | -{% elif m.return_type.is_builtin %} |
| 120 | +{# 2) Actual function call with return value initialization #} |
| 121 | +{% if m.return_type.is_scalar %} |
| 122 | + cdef {{m.return_type.c_type }} ret = gdapi.gd_{{ builtin.snake_name }}_meth_{{ m.name }}( |
| 123 | +{% elif m.return_type.is_builtin %} |
121 | 124 | # Call to __new__ bypasses __init__ constructor |
122 | 125 | cdef {{ m.return_type.cy_type }} ret = {{ m.return_type.cy_type }}.__new__({{ m.return_type.cy_type }}) |
123 | 126 | ret._gd_data = gdapi.gd_{{ builtin.snake_name }}_meth_{{ m.name }}( |
124 | | -{% elif m.return_type.is_object or m.return_type.is_variant %} |
125 | | - cdef {{ m.return_type.c_type }} ret = gdapi.gd_{{ builtin.snake_name }}_meth_{{ m.name }}( |
126 | | -{% else %} |
| 127 | +{% elif m.return_type.is_object or m.return_type.is_variant %} |
| 128 | + cdef {{ m.return_type.c_type }} ret = gdapi.gd_{{ builtin.snake_name }}_meth_{{ m.name }}( |
| 129 | +{% else %} |
127 | 130 | gdapi.gd_{{ builtin.snake_name }}_meth_{{ m.name }}( |
128 | | -{% endif %} |
| 131 | +{% endif %} |
| 132 | +{% if m.is_static %} |
129 | 133 | NULL, |
130 | | -{% for arg in m.arguments %} |
131 | | -{% if arg.type.is_scalar %} |
| 134 | +{% else %} |
| 135 | + &self._gd_data, |
| 136 | +{% endif %} |
| 137 | +{# 3) Parameter provided to the function call #} |
| 138 | +{% for arg in m.arguments %} |
| 139 | +{% if arg.type.is_scalar %} |
132 | 140 | {{ arg.name }}, |
133 | | -{% elif arg.type.original_name in ("String", "StringName", "NodePath") %} |
134 | | - &__arg{{ loop.index0 }}._gd_data, |
135 | | -{% elif arg.type.is_builtin %} |
| 141 | +{% elif arg.type.original_name in ("String", "StringName", "NodePath") %} |
| 142 | + &__arg_{{ arg.name }}._gd_data, |
| 143 | +{% elif arg.type.is_builtin %} |
136 | 144 | &{{ arg.name }}._gd_data, |
137 | | -{% elif arg.type.is_object %} |
| 145 | +{% elif arg.type.is_object %} |
138 | 146 | &{{ arg.name }}._gd_ptr, |
139 | | -{% elif arg.type.is_variant %} |
140 | | - &__arg{{ loop.index0 }}, |
141 | | -{% else %} |
| 147 | +{% elif arg.type.is_variant %} |
| 148 | + &__arg_{{ arg.name }}, |
| 149 | +{% else %} |
142 | 150 | <crash !!!!> # {{ arg.type }} Unsupported type, crash the compilation ! |
143 | | -{% endif %} |
144 | | -{% endfor %} |
| 151 | +{% endif %} |
| 152 | +{% endfor %} |
145 | 153 | ) |
146 | | -{% if not m.return_type.is_nil %} |
147 | | -{% if m.return_type.is_object %} |
| 154 | +{# 4) Optional return value conversion #} |
| 155 | +{% if not m.return_type.is_nil %} |
| 156 | +{% if m.return_type.is_object %} |
148 | 157 | return BaseGDObject.cast_from_object(ret) |
149 | | -{% elif m.return_type.is_variant %} |
150 | | - # Note `gd_variant_steal_into_pyobj(&ret)` calls `ret`'s destructor |
| 158 | +{% elif m.return_type.is_variant %} |
| 159 | + # Note the "steal" means we don't need a final `gdapi.gd_variant_del(&ret)` |
151 | 160 | return gd_variant_steal_into_pyobj(&ret) |
152 | | -{% else %} |
| 161 | +{% else %} |
153 | 162 | return ret |
154 | | -{% endif %} |
155 | | -{% endif %} |
156 | 163 | {% endif %} |
| 164 | +{% endif %} |
157 | 165 | {% endmacro %} |
158 | 166 |
|
159 | 167 |
|
160 | 168 | {% macro render_method(builtin, m) %} |
161 | | -{% if m.is_static %} |
162 | | -{{ _render_static_method(builtin, m) }} |
| 169 | +{% if m.is_vararg %} |
| 170 | +{{ _render_method_vararg(builtin, m) }} |
163 | 171 | {% else %} |
164 | | -{{ _render_non_static_method(builtin, m) }} |
| 172 | +{{ _render_method_standard(builtin, m) }} |
165 | 173 | {% endif %} |
166 | 174 | {% endmacro %} |
0 commit comments