summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript.cpp')
-rw-r--r--modules/gdscript/gdscript.cpp333
1 files changed, 148 insertions, 185 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 42b08f8a68..58aa7a08d4 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -83,7 +83,7 @@ void GDScriptNativeClass::_bind_methods() {
Variant GDScriptNativeClass::_new() {
Object *o = instantiate();
- ERR_FAIL_COND_V_MSG(!o, Variant(), "Class type: '" + String(name) + "' is not instantiable.");
+ ERR_FAIL_NULL_V_MSG(o, Variant(), "Class type: '" + String(name) + "' is not instantiable.");
RefCounted *rc = Object::cast_to<RefCounted>(o);
if (rc) {
@@ -215,7 +215,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr
} else {
owner = memnew(RefCounted); //by default, no base means use reference
}
- ERR_FAIL_COND_V_MSG(!owner, Variant(), "Can't inherit from a virtual class.");
+ ERR_FAIL_NULL_V_MSG(owner, Variant(), "Can't inherit from a virtual class.");
RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
@@ -254,7 +254,7 @@ Ref<Script> GDScript::get_base_script() const {
}
StringName GDScript::get_global_name() const {
- return name;
+ return global_name;
}
StringName GDScript::get_instance_base_type() const {
@@ -278,33 +278,16 @@ struct _GDScriptMemberSort {
void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
placeholders.erase(p_placeholder);
}
+
#endif
void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const {
const GDScript *current = this;
while (current) {
for (const KeyValue<StringName, GDScriptFunction *> &E : current->member_functions) {
- GDScriptFunction *func = E.value;
- MethodInfo mi;
- mi.name = E.key;
-
- if (func->is_static()) {
- mi.flags |= METHOD_FLAG_STATIC;
- }
-
- for (int i = 0; i < func->get_argument_count(); i++) {
- PropertyInfo arginfo = func->get_argument_type(i);
-#ifdef TOOLS_ENABLED
- arginfo.name = func->get_argument_name(i);
-#endif
- mi.arguments.push_back(arginfo);
- }
-#ifdef TOOLS_ENABLED
- mi.default_arguments.append_array(func->get_default_arg_values());
-#endif
- mi.return_val = func->get_return_type();
- r_list->push_back(mi);
+ r_list->push_back(E.value->get_method_info());
}
+
if (!p_include_base) {
return;
}
@@ -323,10 +306,12 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl
while (sptr) {
Vector<_GDScriptMemberSort> msort;
- for (const KeyValue<StringName, PropertyInfo> &E : sptr->member_info) {
+ for (const KeyValue<StringName, MemberInfo> &E : sptr->member_indices) {
+ if (!sptr->members.has(E.key)) {
+ continue; // Skip base class members.
+ }
_GDScriptMemberSort ms;
- ERR_CONTINUE(!sptr->member_indices.has(E.key));
- ms.index = sptr->member_indices[E.key].index;
+ ms.index = E.value.index;
ms.name = E.key;
msort.push_back(ms);
}
@@ -334,7 +319,7 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl
msort.sort();
msort.reverse();
for (int i = 0; i < msort.size(); i++) {
- props.push_front(sptr->member_info[msort[i].name]);
+ props.push_front(sptr->member_indices[msort[i].name].property_info);
}
#ifdef TOOLS_ENABLED
@@ -362,21 +347,17 @@ bool GDScript::has_method(const StringName &p_method) const {
return member_functions.has(p_method);
}
+bool GDScript::has_static_method(const StringName &p_method) const {
+ return member_functions.has(p_method) && member_functions[p_method]->is_static();
+}
+
MethodInfo GDScript::get_method_info(const StringName &p_method) const {
HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method);
if (!E) {
return MethodInfo();
}
- GDScriptFunction *func = E->value;
- MethodInfo mi;
- mi.name = E->key;
- for (int i = 0; i < func->get_argument_count(); i++) {
- mi.arguments.push_back(func->get_argument_type(i));
- }
-
- mi.return_val = func->get_return_type();
- return mi;
+ return E->value->get_method_info();
}
bool GDScript::get_property_default_value(const StringName &p_property, Variant &r_value) const {
@@ -482,9 +463,13 @@ void GDScript::_clear_doc() {
docs.clear();
doc = DocData::ClassDoc();
}
+
+String GDScript::get_class_icon_path() const {
+ return simplified_icon_path;
+}
#endif
-bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update) {
+bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update, bool p_base_exports_changed) {
#ifdef TOOLS_ENABLED
static Vector<GDScript *> base_caches;
@@ -493,7 +478,7 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
}
base_caches.append(this);
- bool changed = false;
+ bool changed = p_base_exports_changed;
if (source_changed_cache) {
source_changed_cache = false;
@@ -553,13 +538,7 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
member_default_values_cache[member.variable->identifier->name] = default_value;
} break;
case GDScriptParser::ClassNode::Member::SIGNAL: {
- // TODO: Cache this in parser to avoid loops like this.
- Vector<StringName> parameters_names;
- parameters_names.resize(member.signal->parameters.size());
- for (int j = 0; j < member.signal->parameters.size(); j++) {
- parameters_names.write[j] = member.signal->parameters[j]->identifier->name;
- }
- _signals[member.signal->identifier->name] = parameters_names;
+ _signals[member.signal->identifier->name] = member.signal->method_info;
} break;
case GDScriptParser::ClassNode::Member::GROUP: {
members_cache.push_back(member.annotation->export_info);
@@ -626,9 +605,15 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
void GDScript::update_exports() {
#ifdef TOOLS_ENABLED
+ _update_exports_down(false);
+#endif
+}
+#ifdef TOOLS_ENABLED
+void GDScript::_update_exports_down(bool p_base_exports_changed) {
bool cyclic_error = false;
- _update_exports(&cyclic_error);
+ bool changed = _update_exports(&cyclic_error, false, nullptr, p_base_exports_changed);
+
if (cyclic_error) {
return;
}
@@ -638,14 +623,14 @@ void GDScript::update_exports() {
for (const ObjectID &E : copy) {
Object *id = ObjectDB::get_instance(E);
GDScript *s = Object::cast_to<GDScript>(id);
+
if (!s) {
continue;
}
- s->update_exports();
+ s->_update_exports_down(p_base_exports_changed || changed);
}
-
-#endif
}
+#endif
String GDScript::_get_debug_path() const {
if (is_built_in() && !get_name().is_empty()) {
@@ -973,22 +958,26 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) {
void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
- List<PropertyInfo> property_list;
-
+ List<const GDScript *> classes;
const GDScript *top = this;
while (top) {
- for (const KeyValue<StringName, MemberInfo> &E : top->static_variables_indices) {
- PropertyInfo pi = PropertyInfo(E.value.data_type);
- pi.name = E.key;
- pi.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; // For the script (as a class) it is a non-static property.
- property_list.push_back(pi);
- }
-
+ classes.push_back(top);
top = top->_base;
}
- for (const List<PropertyInfo>::Element *E = property_list.back(); E; E = E->prev()) {
- p_properties->push_back(E->get());
+ for (const List<const GDScript *>::Element *E = classes.back(); E; E = E->prev()) {
+ Vector<_GDScriptMemberSort> msort;
+ for (const KeyValue<StringName, MemberInfo> &F : E->get()->static_variables_indices) {
+ _GDScriptMemberSort ms;
+ ms.index = F.value.index;
+ ms.name = F.key;
+ msort.push_back(ms);
+ }
+ msort.sort();
+
+ for (int i = 0; i < msort.size(); i++) {
+ p_properties->push_back(E->get()->static_variables_indices[msort[i].name].property_info);
+ }
}
}
@@ -1106,7 +1095,7 @@ GDScript *GDScript::find_class(const String &p_qualified_name) {
Vector<String> class_names;
GDScript *result = nullptr;
// Empty initial name means start here.
- if (first.is_empty() || first == name) {
+ if (first.is_empty() || first == global_name) {
class_names = p_qualified_name.split("::");
result = this;
} else if (p_qualified_name.begins_with(get_root_script()->path)) {
@@ -1241,15 +1230,8 @@ bool GDScript::has_script_signal(const StringName &p_signal) const {
}
void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const {
- for (const KeyValue<StringName, Vector<StringName>> &E : _signals) {
- MethodInfo mi;
- mi.name = E.key;
- for (int i = 0; i < E.value.size(); i++) {
- PropertyInfo arg;
- arg.name = E.value[i];
- mi.arguments.push_back(arg);
- }
- r_list->push_back(mi);
+ for (const KeyValue<StringName, MethodInfo> &E : _signals) {
+ r_list->push_back(E.value);
}
if (!p_include_base) {
@@ -1270,21 +1252,6 @@ void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
_get_script_signal_list(r_signals, true);
}
-String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript) {
- ERR_FAIL_NULL_V(p_gdscript, String());
-
- String class_name;
- while (p_gdscript) {
- if (class_name.is_empty()) {
- class_name = p_gdscript->get_script_class_name();
- } else {
- class_name = p_gdscript->get_script_class_name() + "." + class_name;
- }
- p_gdscript = p_gdscript->_owner;
- }
- return class_name;
-}
-
GDScript *GDScript::_get_gdscript_from_variant(const Variant &p_variant) {
Object *obj = p_variant;
if (obj == nullptr || obj->get_instance_id().is_null()) {
@@ -1395,29 +1362,13 @@ void GDScript::_save_orphaned_subclasses(ClearData *p_clear_data) {
}
}
-void GDScript::_init_rpc_methods_properties() {
- // Copy the base rpc methods so we don't mask their IDs.
- rpc_config.clear();
- if (base.is_valid()) {
- rpc_config = base->rpc_config.duplicate();
- }
-
- // RPC Methods
- for (KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
- Variant config = E.value->get_rpc_config();
- if (config.get_type() != Variant::NIL) {
- rpc_config[E.value->get_name()] = config;
- }
- }
-}
-
#ifdef DEBUG_ENABLED
String GDScript::debug_get_script_name(const Ref<Script> &p_script) {
if (p_script.is_valid()) {
Ref<GDScript> gdscript = p_script;
if (gdscript.is_valid()) {
- if (!gdscript->get_script_class_name().is_empty()) {
- return gdscript->get_script_class_name();
+ if (gdscript->get_local_name() != StringName()) {
+ return gdscript->get_local_name();
}
return gdscript->get_fully_qualified_name().get_file();
}
@@ -1663,7 +1614,7 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
}
{
- HashMap<StringName, Vector<StringName>>::ConstIterator E = sptr->_signals.find(p_name);
+ HashMap<StringName, MethodInfo>::ConstIterator E = sptr->_signals.find(p_name);
if (E) {
r_ret = Signal(this->owner, E->key);
return true;
@@ -1711,15 +1662,11 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
}
Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
- const GDScript *sptr = script.ptr();
- while (sptr) {
- if (sptr->member_info.has(p_name)) {
- if (r_is_valid) {
- *r_is_valid = true;
- }
- return sptr->member_info[p_name].type;
+ if (script->member_indices.has(p_name)) {
+ if (r_is_valid) {
+ *r_is_valid = true;
}
- sptr = sptr->_base;
+ return script->member_indices[p_name].property_info.type;
}
if (r_is_valid) {
@@ -1728,6 +1675,25 @@ Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool
return Variant::NIL;
}
+void GDScriptInstance::validate_property(PropertyInfo &p_property) const {
+ Variant property = (Dictionary)p_property;
+ const Variant *args[1] = { &property };
+
+ const GDScript *sptr = script.ptr();
+ while (sptr) {
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._validate_property);
+ if (E) {
+ Callable::CallError err;
+ Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), args, 1, err);
+ if (err.error == Callable::CallError::CALL_OK) {
+ p_property = PropertyInfo::from_dict(property);
+ return;
+ }
+ }
+ sptr = sptr->_base;
+ }
+}
+
void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
// exported members, not done yet!
@@ -1775,10 +1741,12 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
//instance a fake script for editing the values
Vector<_GDScriptMemberSort> msort;
- for (const KeyValue<StringName, PropertyInfo> &F : sptr->member_info) {
+ for (const KeyValue<StringName, GDScript::MemberInfo> &F : sptr->member_indices) {
+ if (!sptr->members.has(F.key)) {
+ continue; // Skip base class members.
+ }
_GDScriptMemberSort ms;
- ERR_CONTINUE(!sptr->member_indices.has(F.key));
- ms.index = sptr->member_indices[F.key].index;
+ ms.index = F.value.index;
ms.name = F.key;
msort.push_back(ms);
}
@@ -1786,14 +1754,15 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
msort.sort();
msort.reverse();
for (int i = 0; i < msort.size(); i++) {
- props.push_front(sptr->member_info[msort[i].name]);
+ props.push_front(sptr->member_indices[msort[i].name].property_info);
}
#ifdef TOOLS_ENABLED
p_properties->push_back(sptr->get_class_category());
#endif // TOOLS_ENABLED
- for (const PropertyInfo &prop : props) {
+ for (PropertyInfo &prop : props) {
+ validate_property(prop);
p_properties->push_back(prop);
}
@@ -1848,12 +1817,7 @@ void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
const GDScript *sptr = script.ptr();
while (sptr) {
for (const KeyValue<StringName, GDScriptFunction *> &E : sptr->member_functions) {
- MethodInfo mi;
- mi.name = E.key;
- for (int i = 0; i < E.value->get_argument_count(); i++) {
- mi.arguments.push_back(PropertyInfo(Variant::NIL, "arg" + itos(i)));
- }
- p_list->push_back(mi);
+ p_list->push_back(E.value->get_method_info());
}
sptr = sptr->_base;
}
@@ -1897,14 +1861,23 @@ Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_ar
return Variant();
}
-void GDScriptInstance::notification(int p_notification) {
+void GDScriptInstance::notification(int p_notification, bool p_reversed) {
//notification is not virtual, it gets called at ALL levels just like in C.
Variant value = p_notification;
const Variant *args[1] = { &value };
+ List<GDScript *> pl;
GDScript *sptr = script.ptr();
while (sptr) {
- HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification);
+ if (p_reversed) {
+ pl.push_back(sptr);
+ } else {
+ pl.push_front(sptr);
+ }
+ sptr = sptr->_base;
+ }
+ for (GDScript *sc : pl) {
+ HashMap<StringName, GDScriptFunction *>::Iterator E = sc->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification);
if (E) {
Callable::CallError err;
E->value->call(this, args, 1, err);
@@ -1912,7 +1885,6 @@ void GDScriptInstance::notification(int p_notification) {
//print error about notification call
}
}
- sptr = sptr->_base;
}
}
@@ -2407,61 +2379,60 @@ void GDScriptLanguage::frame() {
/* EDITOR FUNCTIONS */
void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
- // TODO: Add annotations here?
+ // Please keep alphabetical order within categories.
static const char *_reserved_words[] = {
- // operators
+ // Control flow.
+ "break",
+ "continue",
+ "elif",
+ "else",
+ "for",
+ "if",
+ "match",
+ "pass",
+ "return",
+ "when",
+ "while",
+ // Declarations.
+ "class",
+ "class_name",
+ "const",
+ "enum",
+ "extends",
+ "func",
+ "namespace", // Reserved for potential future use.
+ "signal",
+ "static",
+ "trait", // Reserved for potential future use.
+ "var",
+ // Other keywords.
+ "await",
+ "breakpoint",
+ "self",
+ "super",
+ "yield", // Reserved for potential future use.
+ // Operators.
"and",
+ "as",
"in",
+ "is",
"not",
"or",
- // types and values
+ // Special values (tokenizer treats them as literals, not as tokens).
"false",
- "float",
- "int",
- "bool",
"null",
- "PI",
- "TAU",
+ "true",
+ // Constants.
"INF",
"NAN",
- "self",
- "true",
- "void",
- // functions
- "as",
+ "PI",
+ "TAU",
+ // Functions (highlighter uses global function color instead).
"assert",
- "await",
- "breakpoint",
- "class",
- "class_name",
- "extends",
- "is",
- "func",
"preload",
- "signal",
- "super",
- // var
- "const",
- "enum",
- "static",
- "var",
- // control flow
- "break",
- "continue",
- "if",
- "elif",
- "else",
- "for",
- "pass",
- "return",
- "match",
- "while",
- // These keywords are not implemented currently, but reserved for (potential) future use.
- // We highlight them as keywords to make errors easier to understand.
- "trait",
- "namespace",
- "yield",
- nullptr
+ // Types (highlighter uses type color instead).
+ "void",
+ nullptr,
};
const char **w = _reserved_words;
@@ -2470,25 +2441,20 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
p_words->push_back(*w);
w++;
}
-
- List<StringName> functions;
- GDScriptUtilityFunctions::get_function_list(&functions);
-
- for (const StringName &E : functions) {
- p_words->push_back(String(E));
- }
}
bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+ // Please keep alphabetical order.
return p_keyword == "break" ||
p_keyword == "continue" ||
p_keyword == "elif" ||
p_keyword == "else" ||
- p_keyword == "if" ||
p_keyword == "for" ||
+ p_keyword == "if" ||
p_keyword == "match" ||
p_keyword == "pass" ||
p_keyword == "return" ||
+ p_keyword == "when" ||
p_keyword == "while";
}
@@ -2527,13 +2493,6 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
* Before changing this function, please ask the current maintainer of EditorFileSystem.
*/
- if (r_icon_path) {
- if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) {
- *r_icon_path = c->icon_path.simplify_path();
- } else if (c->icon_path.is_relative_path()) {
- *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path();
- }
- }
if (r_base_type) {
const GDScriptParser::ClassNode *subclass = c;
String path = p_path;
@@ -2601,6 +2560,9 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
}
}
}
+ if (r_icon_path) {
+ *r_icon_path = c->simplified_icon_path;
+ }
return c->identifier != nullptr ? String(c->identifier->name) : String();
}
@@ -2616,6 +2578,7 @@ GDScriptLanguage::GDScriptLanguage() {
strings._set = StaticCString::create("_set");
strings._get = StaticCString::create("_get");
strings._get_property_list = StaticCString::create("_get_property_list");
+ strings._validate_property = StaticCString::create("_validate_property");
strings._property_can_revert = StaticCString::create("_property_can_revert");
strings._property_get_revert = StaticCString::create("_property_get_revert");
strings._script_source = StaticCString::create("script/source");