Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/gdscript/gdscript_function.cpp
10277 views
1
/**************************************************************************/
2
/* gdscript_function.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "gdscript_function.h"
32
33
#include "gdscript.h"
34
35
Variant GDScriptFunction::get_constant(int p_idx) const {
36
ERR_FAIL_INDEX_V(p_idx, constants.size(), "<errconst>");
37
return constants[p_idx];
38
}
39
40
StringName GDScriptFunction::get_global_name(int p_idx) const {
41
ERR_FAIL_INDEX_V(p_idx, global_names.size(), "<errgname>");
42
return global_names[p_idx];
43
}
44
45
struct _GDFKC {
46
int order = 0;
47
List<int> pos;
48
};
49
50
struct _GDFKCS {
51
int order = 0;
52
StringName id;
53
int pos = 0;
54
55
bool operator<(const _GDFKCS &p_r) const {
56
return order < p_r.order;
57
}
58
};
59
60
void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const {
61
int oc = 0;
62
HashMap<StringName, _GDFKC> sdmap;
63
for (const StackDebug &sd : stack_debug) {
64
if (sd.line >= p_line) {
65
break;
66
}
67
68
if (sd.added) {
69
if (!sdmap.has(sd.identifier)) {
70
_GDFKC d;
71
d.order = oc++;
72
d.pos.push_back(sd.pos);
73
sdmap[sd.identifier] = d;
74
75
} else {
76
sdmap[sd.identifier].pos.push_back(sd.pos);
77
}
78
} else {
79
ERR_CONTINUE(!sdmap.has(sd.identifier));
80
81
sdmap[sd.identifier].pos.pop_back();
82
if (sdmap[sd.identifier].pos.is_empty()) {
83
sdmap.erase(sd.identifier);
84
}
85
}
86
}
87
88
List<_GDFKCS> stackpositions;
89
for (const KeyValue<StringName, _GDFKC> &E : sdmap) {
90
_GDFKCS spp;
91
spp.id = E.key;
92
spp.order = E.value.order;
93
spp.pos = E.value.pos.back()->get();
94
stackpositions.push_back(spp);
95
}
96
97
stackpositions.sort();
98
99
for (_GDFKCS &E : stackpositions) {
100
Pair<StringName, int> p;
101
p.first = E.id;
102
p.second = E.pos;
103
r_stackvars->push_back(p);
104
}
105
}
106
107
GDScriptFunction::GDScriptFunction() {
108
name = "<anonymous>";
109
#ifdef DEBUG_ENABLED
110
{
111
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
112
GDScriptLanguage::get_singleton()->function_list.add(&function_list);
113
}
114
#endif
115
}
116
117
GDScriptFunction::~GDScriptFunction() {
118
get_script()->member_functions.erase(name);
119
120
for (int i = 0; i < lambdas.size(); i++) {
121
memdelete(lambdas[i]);
122
}
123
124
for (int i = 0; i < argument_types.size(); i++) {
125
argument_types.write[i].script_type_ref = Ref<Script>();
126
}
127
return_type.script_type_ref = Ref<Script>();
128
129
#ifdef DEBUG_ENABLED
130
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
131
GDScriptLanguage::get_singleton()->function_list.remove(&function_list);
132
#endif
133
}
134
135
/////////////////////
136
137
Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
138
Variant arg;
139
r_error.error = Callable::CallError::CALL_OK;
140
141
if (p_argcount == 0) {
142
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
143
r_error.expected = 1;
144
return Variant();
145
} else if (p_argcount == 1) {
146
//noooneee
147
} else if (p_argcount == 2) {
148
arg = *p_args[0];
149
} else {
150
Array extra_args;
151
for (int i = 0; i < p_argcount - 1; i++) {
152
extra_args.push_back(*p_args[i]);
153
}
154
arg = extra_args;
155
}
156
157
Ref<GDScriptFunctionState> self = *p_args[p_argcount - 1];
158
159
if (self.is_null()) {
160
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
161
r_error.argument = p_argcount - 1;
162
r_error.expected = Variant::OBJECT;
163
return Variant();
164
}
165
166
return resume(arg);
167
}
168
169
bool GDScriptFunctionState::is_valid(bool p_extended_check) const {
170
if (function == nullptr) {
171
return false;
172
}
173
174
if (p_extended_check) {
175
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
176
177
// Script gone?
178
if (!scripts_list.in_list()) {
179
return false;
180
}
181
// Class instance gone? (if not static function)
182
if (state.instance && !instances_list.in_list()) {
183
return false;
184
}
185
}
186
187
return true;
188
}
189
190
Variant GDScriptFunctionState::resume(const Variant &p_arg) {
191
ERR_FAIL_NULL_V(function, Variant());
192
{
193
MutexLock lock(GDScriptLanguage::singleton->mutex);
194
195
if (!scripts_list.in_list()) {
196
#ifdef DEBUG_ENABLED
197
ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after await, but script is gone. At script: " + state.script_path + ":" + itos(state.line));
198
#else
199
return Variant();
200
#endif
201
}
202
if (state.instance && !instances_list.in_list()) {
203
#ifdef DEBUG_ENABLED
204
ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after await, but class instance is gone. At script: " + state.script_path + ":" + itos(state.line));
205
#else
206
return Variant();
207
#endif
208
}
209
// Do these now to avoid locking again after the call
210
scripts_list.remove_from_list();
211
instances_list.remove_from_list();
212
}
213
214
state.result = p_arg;
215
Callable::CallError err;
216
Variant ret = function->call(nullptr, nullptr, 0, err, &state);
217
218
bool completed = true;
219
220
// If the return value is a GDScriptFunctionState reference,
221
// then the function did await again after resuming.
222
if (ret.is_ref_counted()) {
223
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
224
if (gdfs && gdfs->function == function) {
225
completed = false;
226
// Keep the first state alive via reference.
227
gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this);
228
}
229
}
230
231
function = nullptr; //cleaned up;
232
state.result = Variant();
233
234
if (completed) {
235
_clear_stack();
236
}
237
238
return ret;
239
}
240
241
void GDScriptFunctionState::_clear_stack() {
242
if (state.stack_size) {
243
Variant *stack = (Variant *)state.stack.ptr();
244
// First `GDScriptFunction::FIXED_ADDRESSES_MAX` stack addresses are special
245
// and not copied to the state, so we skip them here.
246
for (int i = GDScriptFunction::FIXED_ADDRESSES_MAX; i < state.stack_size; i++) {
247
stack[i].~Variant();
248
}
249
state.stack_size = 0;
250
}
251
}
252
253
void GDScriptFunctionState::_clear_connections() {
254
List<Object::Connection> conns;
255
get_signals_connected_to_this(&conns);
256
257
for (Object::Connection &c : conns) {
258
c.signal.disconnect(c.callable);
259
}
260
}
261
262
void GDScriptFunctionState::_bind_methods() {
263
ClassDB::bind_method(D_METHOD("resume", "arg"), &GDScriptFunctionState::resume, DEFVAL(Variant()));
264
ClassDB::bind_method(D_METHOD("is_valid", "extended_check"), &GDScriptFunctionState::is_valid, DEFVAL(false));
265
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &GDScriptFunctionState::_signal_callback, MethodInfo("_signal_callback"));
266
267
ADD_SIGNAL(MethodInfo("completed", PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
268
}
269
270
GDScriptFunctionState::GDScriptFunctionState() :
271
scripts_list(this),
272
instances_list(this) {
273
}
274
275
GDScriptFunctionState::~GDScriptFunctionState() {
276
{
277
MutexLock lock(GDScriptLanguage::singleton->mutex);
278
scripts_list.remove_from_list();
279
instances_list.remove_from_list();
280
}
281
}
282
283