Path: blob/master/modules/gdscript/gdscript_function.cpp
10277 views
/**************************************************************************/1/* gdscript_function.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "gdscript_function.h"3132#include "gdscript.h"3334Variant GDScriptFunction::get_constant(int p_idx) const {35ERR_FAIL_INDEX_V(p_idx, constants.size(), "<errconst>");36return constants[p_idx];37}3839StringName GDScriptFunction::get_global_name(int p_idx) const {40ERR_FAIL_INDEX_V(p_idx, global_names.size(), "<errgname>");41return global_names[p_idx];42}4344struct _GDFKC {45int order = 0;46List<int> pos;47};4849struct _GDFKCS {50int order = 0;51StringName id;52int pos = 0;5354bool operator<(const _GDFKCS &p_r) const {55return order < p_r.order;56}57};5859void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const {60int oc = 0;61HashMap<StringName, _GDFKC> sdmap;62for (const StackDebug &sd : stack_debug) {63if (sd.line >= p_line) {64break;65}6667if (sd.added) {68if (!sdmap.has(sd.identifier)) {69_GDFKC d;70d.order = oc++;71d.pos.push_back(sd.pos);72sdmap[sd.identifier] = d;7374} else {75sdmap[sd.identifier].pos.push_back(sd.pos);76}77} else {78ERR_CONTINUE(!sdmap.has(sd.identifier));7980sdmap[sd.identifier].pos.pop_back();81if (sdmap[sd.identifier].pos.is_empty()) {82sdmap.erase(sd.identifier);83}84}85}8687List<_GDFKCS> stackpositions;88for (const KeyValue<StringName, _GDFKC> &E : sdmap) {89_GDFKCS spp;90spp.id = E.key;91spp.order = E.value.order;92spp.pos = E.value.pos.back()->get();93stackpositions.push_back(spp);94}9596stackpositions.sort();9798for (_GDFKCS &E : stackpositions) {99Pair<StringName, int> p;100p.first = E.id;101p.second = E.pos;102r_stackvars->push_back(p);103}104}105106GDScriptFunction::GDScriptFunction() {107name = "<anonymous>";108#ifdef DEBUG_ENABLED109{110MutexLock lock(GDScriptLanguage::get_singleton()->mutex);111GDScriptLanguage::get_singleton()->function_list.add(&function_list);112}113#endif114}115116GDScriptFunction::~GDScriptFunction() {117get_script()->member_functions.erase(name);118119for (int i = 0; i < lambdas.size(); i++) {120memdelete(lambdas[i]);121}122123for (int i = 0; i < argument_types.size(); i++) {124argument_types.write[i].script_type_ref = Ref<Script>();125}126return_type.script_type_ref = Ref<Script>();127128#ifdef DEBUG_ENABLED129MutexLock lock(GDScriptLanguage::get_singleton()->mutex);130GDScriptLanguage::get_singleton()->function_list.remove(&function_list);131#endif132}133134/////////////////////135136Variant GDScriptFunctionState::_signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {137Variant arg;138r_error.error = Callable::CallError::CALL_OK;139140if (p_argcount == 0) {141r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;142r_error.expected = 1;143return Variant();144} else if (p_argcount == 1) {145//noooneee146} else if (p_argcount == 2) {147arg = *p_args[0];148} else {149Array extra_args;150for (int i = 0; i < p_argcount - 1; i++) {151extra_args.push_back(*p_args[i]);152}153arg = extra_args;154}155156Ref<GDScriptFunctionState> self = *p_args[p_argcount - 1];157158if (self.is_null()) {159r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;160r_error.argument = p_argcount - 1;161r_error.expected = Variant::OBJECT;162return Variant();163}164165return resume(arg);166}167168bool GDScriptFunctionState::is_valid(bool p_extended_check) const {169if (function == nullptr) {170return false;171}172173if (p_extended_check) {174MutexLock lock(GDScriptLanguage::get_singleton()->mutex);175176// Script gone?177if (!scripts_list.in_list()) {178return false;179}180// Class instance gone? (if not static function)181if (state.instance && !instances_list.in_list()) {182return false;183}184}185186return true;187}188189Variant GDScriptFunctionState::resume(const Variant &p_arg) {190ERR_FAIL_NULL_V(function, Variant());191{192MutexLock lock(GDScriptLanguage::singleton->mutex);193194if (!scripts_list.in_list()) {195#ifdef DEBUG_ENABLED196ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after await, but script is gone. At script: " + state.script_path + ":" + itos(state.line));197#else198return Variant();199#endif200}201if (state.instance && !instances_list.in_list()) {202#ifdef DEBUG_ENABLED203ERR_FAIL_V_MSG(Variant(), "Resumed function '" + state.function_name + "()' after await, but class instance is gone. At script: " + state.script_path + ":" + itos(state.line));204#else205return Variant();206#endif207}208// Do these now to avoid locking again after the call209scripts_list.remove_from_list();210instances_list.remove_from_list();211}212213state.result = p_arg;214Callable::CallError err;215Variant ret = function->call(nullptr, nullptr, 0, err, &state);216217bool completed = true;218219// If the return value is a GDScriptFunctionState reference,220// then the function did await again after resuming.221if (ret.is_ref_counted()) {222GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);223if (gdfs && gdfs->function == function) {224completed = false;225// Keep the first state alive via reference.226gdfs->first_state = first_state.is_valid() ? first_state : Ref<GDScriptFunctionState>(this);227}228}229230function = nullptr; //cleaned up;231state.result = Variant();232233if (completed) {234_clear_stack();235}236237return ret;238}239240void GDScriptFunctionState::_clear_stack() {241if (state.stack_size) {242Variant *stack = (Variant *)state.stack.ptr();243// First `GDScriptFunction::FIXED_ADDRESSES_MAX` stack addresses are special244// and not copied to the state, so we skip them here.245for (int i = GDScriptFunction::FIXED_ADDRESSES_MAX; i < state.stack_size; i++) {246stack[i].~Variant();247}248state.stack_size = 0;249}250}251252void GDScriptFunctionState::_clear_connections() {253List<Object::Connection> conns;254get_signals_connected_to_this(&conns);255256for (Object::Connection &c : conns) {257c.signal.disconnect(c.callable);258}259}260261void GDScriptFunctionState::_bind_methods() {262ClassDB::bind_method(D_METHOD("resume", "arg"), &GDScriptFunctionState::resume, DEFVAL(Variant()));263ClassDB::bind_method(D_METHOD("is_valid", "extended_check"), &GDScriptFunctionState::is_valid, DEFVAL(false));264ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &GDScriptFunctionState::_signal_callback, MethodInfo("_signal_callback"));265266ADD_SIGNAL(MethodInfo("completed", PropertyInfo(Variant::NIL, "result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));267}268269GDScriptFunctionState::GDScriptFunctionState() :270scripts_list(this),271instances_list(this) {272}273274GDScriptFunctionState::~GDScriptFunctionState() {275{276MutexLock lock(GDScriptLanguage::singleton->mutex);277scripts_list.remove_from_list();278instances_list.remove_from_list();279}280}281282283