123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- /**************************************************************************/
- /* gdscript_function.cpp */
- /**************************************************************************/
- /* This file is part of: */
- /* GODOT ENGINE */
- /* https://godotengine.org */
- /**************************************************************************/
- /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
- /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
- /* */
- /* Permission is hereby granted, free of charge, to any person obtaining */
- /* a copy of this software and associated documentation files (the */
- /* "Software"), to deal in the Software without restriction, including */
- /* without limitation the rights to use, copy, modify, merge, publish, */
- /* distribute, sublicense, and/or sell copies of the Software, and to */
- /* permit persons to whom the Software is furnished to do so, subject to */
- /* the following conditions: */
- /* */
- /* The above copyright notice and this permission notice shall be */
- /* included in all copies or substantial portions of the Software. */
- /* */
- /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
- /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
- /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
- /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
- /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
- /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
- /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
- /**************************************************************************/
- #include "gdscript_function.h"
- #include "gdscript.h"
- Variant GDScriptFunction::get_constant(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, constants.size(), "<errconst>");
- return constants[p_idx];
- }
- StringName GDScriptFunction::get_global_name(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, global_names.size(), "<errgname>");
- return global_names[p_idx];
- }
- struct _GDFKC {
- int order = 0;
- List<int> pos;
- };
- struct _GDFKCS {
- int order = 0;
- StringName id;
- int pos = 0;
- bool operator<(const _GDFKCS &p_r) const {
- return order < p_r.order;
- }
- };
- void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const {
- int oc = 0;
- HashMap<StringName, _GDFKC> sdmap;
- for (const StackDebug &sd : stack_debug) {
- if (sd.line >= p_line) {
- break;
- }
- if (sd.added) {
- if (!sdmap.has(sd.identifier)) {
- _GDFKC d;
- d.order = oc++;
- d.pos.push_back(sd.pos);
- sdmap[sd.identifier] = d;
- } else {
- sdmap[sd.identifier].pos.push_back(sd.pos);
- }
- } else {
- ERR_CONTINUE(!sdmap.has(sd.identifier));
- sdmap[sd.identifier].pos.pop_back();
- if (sdmap[sd.identifier].pos.is_empty()) {
- sdmap.erase(sd.identifier);
- }
- }
- }
- List<_GDFKCS> stackpositions;
- for (const KeyValue<StringName, _GDFKC> &E : sdmap) {
- _GDFKCS spp;
- spp.id = E.key;
- spp.order = E.value.order;
- spp.pos = E.value.pos.back()->get();
- stackpositions.push_back(spp);
- }
- stackpositions.sort();
- for (_GDFKCS &E : stackpositions) {
- Pair<StringName, int> p;
- p.first = E.id;
- p.second = E.pos;
- r_stackvars->push_back(p);
- }
- }
- GDScriptFunction::GDScriptFunction() {
- name = "<anonymous>";
- #ifdef DEBUG_ENABLED
- {
- MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
- GDScriptLanguage::get_singleton()->function_list.add(&function_list);
- }
- #endif
- }
- GDScriptFunction::~GDScriptFunction() {
- get_script()->member_functions.erase(name);
- for (int i = 0; i < lambdas.size(); i++) {
- memdelete(lambdas[i]);
- }
- for (int i = 0; i < argument_types.size(); i++) {
- argument_types.write[i].script_type_ref = Ref<Script>();
- }
- return_type.script_type_ref = Ref<Script>();
- #ifdef DEBUG_ENABLED
- MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
- GDScriptLanguage::get_singleton()->function_list.remove(&function_list);
- #endif
- }
- /////////////////////
- Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- Variant arg;
- r_error.error = Callable::CallError::CALL_OK;
- if (p_argcount == 0) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.expected = 1;
- return Variant();
- } else if (p_argcount == 1) {
- //noooneee
- } else if (p_argcount == 2) {
- arg = *p_args[0];
- } else {
- Array extra_args;
- for (int i = 0; i < p_argcount - 1; i++) {
- extra_args.push_back(*p_args[i]);
- }
- arg = extra_args;
- }
- Ref<GDScriptFunctionState> self = *p_args[p_argcount - 1];
- if (self.is_null()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = p_argcount - 1;
- r_error.expected = Variant::OBJECT;
- return Variant();
- }
- return resume(arg);
- }
- bool GDScriptFunctionState::is_valid(bool p_extended_check) const {
- if (function == nullptr) {
- return false;
- }
- if (p_extended_check) {
- MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
- // Script gone?
- if (!scripts_list.in_list()) {
- return false;
- }
- // Class instance gone? (if not static function)
- if (state.instance && !instances_list.in_list()) {
- return false;
- }
- }
- return true;
- }
- Variant GDScriptFunctionState::resume(const Variant &p_arg) {
- ERR_FAIL_NULL_V(function, Variant());
- {
- MutexLock lock(GDScriptLanguage::singleton->mutex);
- if (!scripts_list.in_list()) {
- #ifdef DEBUG_ENABLED
- ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after await, but script is gone. At script: " + state.script_path + ":" + itos(state.line));
- #else
- return Variant();
- #endif
- }
- if (state.instance && !instances_list.in_list()) {
- #ifdef DEBUG_ENABLED
- 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));
- #else
- return Variant();
- #endif
- }
- // Do these now to avoid locking again after the call
- scripts_list.remove_from_list();
- instances_list.remove_from_list();
- }
- state.result = p_arg;
- Callable::CallError err;
- Variant ret = function->call(nullptr, nullptr, 0, err, &state);
- bool completed = true;
- // If the return value is a GDScriptFunctionState reference,
- // then the function did await again after resuming.
- if (ret.is_ref_counted()) {
- GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
- if (gdfs && gdfs->function == function) {
- completed = false;
- gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this);
- }
- }
- function = nullptr; //cleaned up;
- state.result = Variant();
- if (completed) {
- if (first_state.is_valid()) {
- first_state->emit_signal(SNAME("completed"), ret);
- } else {
- emit_signal(SNAME("completed"), ret);
- }
- #ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->exit_function();
- }
- _clear_stack();
- #endif
- }
- return ret;
- }
- void GDScriptFunctionState::_clear_stack() {
- if (state.stack_size) {
- Variant *stack = (Variant *)state.stack.ptr();
- // The first 3 are special addresses and not copied to the state, so we skip them here.
- for (int i = 3; i < state.stack_size; i++) {
- stack[i].~Variant();
- }
- state.stack_size = 0;
- }
- }
- void GDScriptFunctionState::_clear_connections() {
- List<Object::Connection> conns;
- get_signals_connected_to_this(&conns);
- for (Object::Connection &c : conns) {
- c.signal.disconnect(c.callable);
- }
- }
- void GDScriptFunctionState::_bind_methods() {
- ClassDB::bind_method(D_METHOD("resume", "arg"), &GDScriptFunctionState::resume, DEFVAL(Variant()));
- ClassDB::bind_method(D_METHOD("is_valid", "extended_check"), &GDScriptFunctionState::is_valid, DEFVAL(false));
- ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &GDScriptFunctionState::_signal_callback, MethodInfo("_signal_callback"));
- ADD_SIGNAL(MethodInfo("completed", PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
- }
- GDScriptFunctionState::GDScriptFunctionState() :
- scripts_list(this),
- instances_list(this) {
- }
- GDScriptFunctionState::~GDScriptFunctionState() {
- {
- MutexLock lock(GDScriptLanguage::singleton->mutex);
- scripts_list.remove_from_list();
- instances_list.remove_from_list();
- }
- }
|