Use type information to enable GDScript introspection
This makes the Script API provide accurate information when requesting property or method info.
This commit is contained in:
parent
4b18c4e448
commit
e3d72d14ff
6 changed files with 129 additions and 98 deletions
|
@ -220,16 +220,14 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
|
||||||
void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
|
void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
|
||||||
|
|
||||||
for (const Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
|
for (const Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
|
||||||
|
GDScriptFunction *func = E->get();
|
||||||
MethodInfo mi;
|
MethodInfo mi;
|
||||||
mi.name = E->key();
|
mi.name = E->key();
|
||||||
for (int i = 0; i < E->get()->get_argument_count(); i++) {
|
for (int i = 0; i < func->get_argument_count(); i++) {
|
||||||
PropertyInfo arg;
|
mi.arguments.push_back(func->get_argument_type(i));
|
||||||
arg.type = Variant::NIL; //variant
|
|
||||||
arg.name = E->get()->get_argument_name(i);
|
|
||||||
mi.arguments.push_back(arg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mi.return_val.name = "Variant";
|
mi.return_val = func->get_return_type();
|
||||||
p_list->push_back(mi);
|
p_list->push_back(mi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,16 +275,14 @@ MethodInfo GDScript::get_method_info(const StringName &p_method) const {
|
||||||
if (!E)
|
if (!E)
|
||||||
return MethodInfo();
|
return MethodInfo();
|
||||||
|
|
||||||
|
GDScriptFunction *func = E->get();
|
||||||
MethodInfo mi;
|
MethodInfo mi;
|
||||||
mi.name = E->key();
|
mi.name = E->key();
|
||||||
for (int i = 0; i < E->get()->get_argument_count(); i++) {
|
for (int i = 0; i < func->get_argument_count(); i++) {
|
||||||
PropertyInfo arg;
|
mi.arguments.push_back(func->get_argument_type(i));
|
||||||
arg.type = Variant::NIL; //variant
|
|
||||||
arg.name = E->get()->get_argument_name(i);
|
|
||||||
mi.arguments.push_back(arg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mi.return_val.name = "Variant";
|
mi.return_val = func->get_return_type();
|
||||||
return mi;
|
return mi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,7 @@ public:
|
||||||
}
|
}
|
||||||
const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
|
const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
|
||||||
const Ref<GDScriptNativeClass> &get_native() const { return native; }
|
const Ref<GDScriptNativeClass> &get_native() const { return native; }
|
||||||
|
const String &get_script_class_name() const { return name; }
|
||||||
|
|
||||||
virtual bool has_script_signal(const StringName &p_signal) const;
|
virtual bool has_script_signal(const StringName &p_signal) const;
|
||||||
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
|
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
|
||||||
|
|
|
@ -111,23 +111,50 @@ bool GDScriptCompiler::_create_binary_operator(CodeGen &codegen, const GDScriptP
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const {
|
||||||
int GDScriptCompiler::_parse_subexpression(CodeGen& codegen,const GDScriptParser::Node *p_expression) {
|
if (!p_datatype.has_type) {
|
||||||
|
return GDScriptDataType();
|
||||||
|
|
||||||
int ret = _parse_expression(codegen,p_expression);
|
|
||||||
if (ret<0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (ret&(GDScriptFunction::ADDR_TYPE_STACK<<GDScriptFunction::ADDR_BITS)) {
|
|
||||||
codegen.stack_level++;
|
|
||||||
codegen.check_max_stack_level();
|
|
||||||
//stack was used, keep value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
GDScriptDataType result;
|
||||||
|
result.has_type = true;
|
||||||
|
|
||||||
|
switch (p_datatype.kind) {
|
||||||
|
case GDScriptParser::DataType::BUILTIN: {
|
||||||
|
result.kind = GDScriptDataType::BUILTIN;
|
||||||
|
result.builtin_type = p_datatype.builtin_type;
|
||||||
|
} break;
|
||||||
|
case GDScriptParser::DataType::NATIVE: {
|
||||||
|
result.kind = GDScriptDataType::NATIVE;
|
||||||
|
result.native_type = p_datatype.native_type;
|
||||||
|
} break;
|
||||||
|
case GDScriptParser::DataType::SCRIPT: {
|
||||||
|
result.kind = GDScriptDataType::SCRIPT;
|
||||||
|
result.script_type = p_datatype.script_type;
|
||||||
|
result.native_type = result.script_type->get_instance_base_type();
|
||||||
|
}
|
||||||
|
case GDScriptParser::DataType::GDSCRIPT: {
|
||||||
|
result.kind = GDScriptDataType::GDSCRIPT;
|
||||||
|
result.script_type = p_datatype.script_type;
|
||||||
|
result.native_type = result.script_type->get_instance_base_type();
|
||||||
|
} break;
|
||||||
|
case GDScriptParser::DataType::CLASS: {
|
||||||
|
result.kind = GDScriptDataType::GDSCRIPT;
|
||||||
|
if (p_datatype.class_type->name == StringName()) {
|
||||||
|
result.script_type = Ref<GDScript>(main_script);
|
||||||
|
} else {
|
||||||
|
result.script_type = class_map[p_datatype.class_type->name];
|
||||||
|
}
|
||||||
|
result.native_type = result.script_type->get_instance_base_type();
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
ERR_PRINT("Parser bug: converting unresolved type.");
|
||||||
|
result.has_type = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level) {
|
int GDScriptCompiler::_parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level) {
|
||||||
|
|
||||||
|
@ -1883,41 +1910,41 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
|
||||||
for (int i = 0; i < p_class->variables.size(); i++) {
|
for (int i = 0; i < p_class->variables.size(); i++) {
|
||||||
|
|
||||||
StringName name = p_class->variables[i].identifier;
|
StringName name = p_class->variables[i].identifier;
|
||||||
if (p_script->member_indices.has(name)) {
|
|
||||||
_set_error("Member '" + name + "' already exists (in current or parent class)", p_class);
|
|
||||||
return ERR_ALREADY_EXISTS;
|
|
||||||
}
|
|
||||||
if (_is_class_member_property(p_script, name)) {
|
|
||||||
_set_error("Member '" + name + "' already exists as a class property.", p_class);
|
|
||||||
return ERR_ALREADY_EXISTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_class->variables[i]._export.type != Variant::NIL) {
|
|
||||||
|
|
||||||
p_script->member_info[name] = p_class->variables[i]._export;
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
if (p_class->variables[i].default_value.get_type() != Variant::NIL) {
|
|
||||||
|
|
||||||
p_script->member_default_values[name] = p_class->variables[i].default_value;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
|
|
||||||
p_script->member_info[name] = PropertyInfo(Variant::NIL, name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
//int new_idx = p_script->member_indices.size();
|
|
||||||
GDScript::MemberInfo minfo;
|
GDScript::MemberInfo minfo;
|
||||||
minfo.index = p_script->member_indices.size();
|
minfo.index = p_script->member_indices.size();
|
||||||
minfo.setter = p_class->variables[i].setter;
|
minfo.setter = p_class->variables[i].setter;
|
||||||
minfo.getter = p_class->variables[i].getter;
|
minfo.getter = p_class->variables[i].getter;
|
||||||
minfo.rpc_mode = p_class->variables[i].rpc_mode;
|
minfo.rpc_mode = p_class->variables[i].rpc_mode;
|
||||||
|
minfo.data_type = _gdtype_from_datatype(p_class->variables[i].data_type);
|
||||||
|
|
||||||
|
PropertyInfo prop_info = minfo.data_type;
|
||||||
|
prop_info.name = name;
|
||||||
|
PropertyInfo export_info = p_class->variables[i]._export;
|
||||||
|
|
||||||
|
if (export_info.type != Variant::NIL) {
|
||||||
|
|
||||||
|
if (!minfo.data_type.has_type) {
|
||||||
|
prop_info.type = export_info.type;
|
||||||
|
prop_info.class_name = export_info.class_name;
|
||||||
|
}
|
||||||
|
prop_info.hint = export_info.hint;
|
||||||
|
prop_info.hint_string = export_info.hint_string;
|
||||||
|
prop_info.usage = export_info.usage;
|
||||||
|
#ifdef TOOLS_ENABLED
|
||||||
|
if (p_class->variables[i].default_value.get_type() != Variant::NIL) {
|
||||||
|
p_script->member_default_values[name] = p_class->variables[i].default_value;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_script->member_info[name] = prop_info;
|
||||||
p_script->member_indices[name] = minfo;
|
p_script->member_indices[name] = minfo;
|
||||||
p_script->members.insert(name);
|
p_script->members.insert(name);
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
|
|
||||||
p_script->member_lines[name] = p_class->variables[i].line;
|
p_script->member_lines[name] = p_class->variables[i].line;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -1928,15 +1955,9 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
|
||||||
|
|
||||||
ERR_CONTINUE(E->get().expression->type != GDScriptParser::Node::TYPE_CONSTANT);
|
ERR_CONTINUE(E->get().expression->type != GDScriptParser::Node::TYPE_CONSTANT);
|
||||||
|
|
||||||
if (_is_class_member_property(p_script, name)) {
|
|
||||||
_set_error("Member '" + name + "' already exists as a class property.", p_class);
|
|
||||||
return ERR_ALREADY_EXISTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(E->get().expression);
|
GDScriptParser::ConstantNode *constant = static_cast<GDScriptParser::ConstantNode *>(E->get().expression);
|
||||||
|
|
||||||
p_script->constants.insert(name, constant->value);
|
p_script->constants.insert(name, constant->value);
|
||||||
//p_script->constants[constant->value].make_const();
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
|
|
||||||
p_script->member_lines[name] = E->get().expression->line;
|
p_script->member_lines[name] = E->get().expression->line;
|
||||||
|
@ -2144,6 +2165,7 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
|
||||||
err_column = -1;
|
err_column = -1;
|
||||||
error = "";
|
error = "";
|
||||||
parser = p_parser;
|
parser = p_parser;
|
||||||
|
main_script = p_script;
|
||||||
const GDScriptParser::Node *root = parser->get_parse_tree();
|
const GDScriptParser::Node *root = parser->get_parse_tree();
|
||||||
ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA);
|
ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, ERR_INVALID_DATA);
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ class GDScriptCompiler {
|
||||||
Map<StringName, Ref<GDScript> > class_map;
|
Map<StringName, Ref<GDScript> > class_map;
|
||||||
Set<StringName> parsed_classes;
|
Set<StringName> parsed_classes;
|
||||||
Set<StringName> parsing_classes;
|
Set<StringName> parsing_classes;
|
||||||
|
GDScript *main_script;
|
||||||
struct CodeGen {
|
struct CodeGen {
|
||||||
|
|
||||||
GDScript *script;
|
GDScript *script;
|
||||||
|
@ -142,6 +143,8 @@ class GDScriptCompiler {
|
||||||
bool _create_unary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level);
|
bool _create_unary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level);
|
||||||
bool _create_binary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level, bool p_initializer = false);
|
bool _create_binary_operator(CodeGen &codegen, const GDScriptParser::OperatorNode *on, Variant::Operator op, int p_stack_level, bool p_initializer = false);
|
||||||
|
|
||||||
|
GDScriptDataType _gdtype_from_datatype(const GDScriptParser::DataType &p_datatype) const;
|
||||||
|
|
||||||
int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level);
|
int _parse_assign_right_expression(CodeGen &codegen, const GDScriptParser::OperatorNode *p_expression, int p_stack_level);
|
||||||
int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false);
|
int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false);
|
||||||
Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);
|
Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);
|
||||||
|
|
|
@ -4957,6 +4957,56 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String GDScriptParser::DataType::to_string() const {
|
||||||
|
if (!has_type) return "var";
|
||||||
|
switch (kind) {
|
||||||
|
case BUILTIN: {
|
||||||
|
if (builtin_type == Variant::NIL) return "null";
|
||||||
|
return Variant::get_type_name(builtin_type);
|
||||||
|
} break;
|
||||||
|
case NATIVE: {
|
||||||
|
if (is_meta_type) {
|
||||||
|
return "GDScriptNativeClass";
|
||||||
|
}
|
||||||
|
return native_type.operator String();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case GDSCRIPT: {
|
||||||
|
Ref<GDScript> gds = script_type;
|
||||||
|
const String &gds_class = gds->get_script_class_name();
|
||||||
|
if (!gds_class.empty()) {
|
||||||
|
return gds_class;
|
||||||
|
}
|
||||||
|
} // fallthrough
|
||||||
|
case SCRIPT: {
|
||||||
|
if (is_meta_type) {
|
||||||
|
return script_type->get_class_name().operator String();
|
||||||
|
}
|
||||||
|
String name = script_type->get_name();
|
||||||
|
if (name != String()) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
name = script_type->get_path().get_file();
|
||||||
|
if (name != String()) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return native_type.operator String();
|
||||||
|
} break;
|
||||||
|
case CLASS: {
|
||||||
|
ERR_FAIL_COND_V(!class_type, String());
|
||||||
|
if (is_meta_type) {
|
||||||
|
return "GDScript";
|
||||||
|
}
|
||||||
|
if (class_type->name == StringName()) {
|
||||||
|
return "self";
|
||||||
|
}
|
||||||
|
return class_type->name.operator String();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unresolved";
|
||||||
|
}
|
||||||
|
|
||||||
bool GDScriptParser::_parse_type(DataType &r_type, bool p_can_be_void) {
|
bool GDScriptParser::_parse_type(DataType &r_type, bool p_can_be_void) {
|
||||||
tokenizer->advance();
|
tokenizer->advance();
|
||||||
r_type.has_type = true;
|
r_type.has_type = true;
|
||||||
|
|
|
@ -62,48 +62,7 @@ public:
|
||||||
Ref<Script> script_type;
|
Ref<Script> script_type;
|
||||||
ClassNode *class_type;
|
ClassNode *class_type;
|
||||||
|
|
||||||
String to_string() const {
|
String to_string() const;
|
||||||
if (!has_type) return "var";
|
|
||||||
switch (kind) {
|
|
||||||
case BUILTIN: {
|
|
||||||
if (builtin_type == Variant::NIL) return "null";
|
|
||||||
return Variant::get_type_name(builtin_type);
|
|
||||||
} break;
|
|
||||||
case NATIVE: {
|
|
||||||
if (is_meta_type) {
|
|
||||||
return "GDScriptNativeClass";
|
|
||||||
}
|
|
||||||
return native_type.operator String();
|
|
||||||
} break;
|
|
||||||
case SCRIPT:
|
|
||||||
case GDSCRIPT: {
|
|
||||||
if (is_meta_type) {
|
|
||||||
return script_type->get_class_name().operator String();
|
|
||||||
}
|
|
||||||
String name = script_type->get_name();
|
|
||||||
if (name != String()) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
name = script_type->get_path().get_file();
|
|
||||||
if (name != String()) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
return native_type.operator String();
|
|
||||||
} break;
|
|
||||||
case CLASS: {
|
|
||||||
ERR_FAIL_COND_V(!class_type, String());
|
|
||||||
if (is_meta_type) {
|
|
||||||
return "GDScript";
|
|
||||||
}
|
|
||||||
if (class_type->name == StringName()) {
|
|
||||||
return "self";
|
|
||||||
}
|
|
||||||
return class_type->name.operator String();
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Unresolved";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const DataType &other) const {
|
bool operator==(const DataType &other) const {
|
||||||
if (!has_type || !other.has_type) {
|
if (!has_type || !other.has_type) {
|
||||||
|
|
Loading…
Reference in a new issue