Move inheritance resolution to the parser

This commit is contained in:
George Marques 2018-05-29 23:16:52 -03:00
parent 8aab9a06d4
commit b7a00aead0
No known key found for this signature in database
GPG Key ID: 046BD46A3201E43D
5 changed files with 396 additions and 235 deletions

View File

@ -1826,8 +1826,40 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) { if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) {
const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree()); const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree());
if (r_base_type && c->extends_used && c->extends_class.size() == 1) { if (r_base_type) {
*r_base_type = c->extends_class[0]; //todo, should work much better GDScriptParser::DataType base_type;
if (c->base_type.has_type) {
base_type = c->base_type;
while (base_type.has_type && base_type.kind != GDScriptParser::DataType::NATIVE) {
switch (base_type.kind) {
case GDScriptParser::DataType::CLASS: {
base_type = base_type.class_type->base_type;
} break;
case GDScriptParser::DataType::GDSCRIPT: {
Ref<GDScript> gds = base_type.script_type;
if (gds.is_valid()) {
base_type.kind = GDScriptParser::DataType::NATIVE;
base_type.native_type = gds->get_instance_base_type();
} else {
base_type = GDScriptParser::DataType();
}
} break;
default: {
base_type = GDScriptParser::DataType();
} break;
}
}
}
if (base_type.has_type) {
*r_base_type = base_type.native_type;
} else {
// Fallback
if (c->extends_used && c->extends_class.size() == 1) {
*r_base_type = c->extends_class[0];
} else if (!c->extends_used) {
*r_base_type = "Reference";
}
}
} }
return c->name; return c->name;
} }

View File

@ -263,15 +263,6 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
owner = owner->_owner; owner = owner->_owner;
} }
/*
handled in constants now
if (codegen.script->subclasses.has(identifier)) {
//same with a subclass, make it a local constant.
int idx = codegen.get_constant_pos(codegen.script->subclasses[identifier]);
return idx|(GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT<<GDScriptFunction::ADDR_BITS); //make it a local constant (faster access)
}*/
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) { if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier]; int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
@ -1657,12 +1648,23 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
return OK; return OK;
} }
Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
Map<StringName, Ref<GDScript> > old_subclasses; if (p_class->owner && p_class->owner->owner) {
// Owner is not root
if (p_keep_state) { StringName owner_name = p_class->owner->name;
old_subclasses = p_script->subclasses; if (!parsed_classes.has(owner_name)) {
if (parsing_classes.has(owner_name)) {
_set_error("Cyclic class reference for '" + String(owner_name) + "'.", p_class);
return ERR_PARSE_ERROR;
}
parsing_classes.insert(owner_name);
Error err = _parse_class_level(class_map[owner_name].ptr(), class_map[owner_name]->_owner, p_class->owner, p_keep_state);
if (err) {
return err;
}
parsing_classes.erase(owner_name);
}
} }
p_script->native = Ref<GDScriptNativeClass>(); p_script->native = Ref<GDScriptNativeClass>();
@ -1686,177 +1688,46 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
Ref<GDScriptNativeClass> native; Ref<GDScriptNativeClass> native;
if (p_class->extends_used) { // Inheritance
//do inheritance switch (p_class->base_type.kind) {
String path = p_class->extends_file; case GDScriptParser::DataType::CLASS: {
StringName base_name = p_class->base_type.class_type->name;
Ref<GDScript> script; // Make sure dependency is parsed first
if (!parsed_classes.has(base_name)) {
if (path != "") { if (parsing_classes.has(base_name)) {
//path (and optionally subclasses) _set_error("Cyclic class reference for '" + String(base_name) + "'.", p_class);
return ERR_PARSE_ERROR;
if (path.is_rel_path()) {
String base;
if (p_owner) {
GDScript *current_class = p_owner;
while (current_class != NULL) {
base = current_class->get_path();
if (base == "")
current_class = current_class->_owner;
else
break;
}
} else {
base = p_script->get_path();
} }
parsing_classes.insert(base_name);
if (base == "" || base.is_rel_path()) { Error err = _parse_class_level(class_map[base_name].ptr(), class_map[base_name]->_owner, p_class->base_type.class_type, p_keep_state);
_set_error("Could not resolve relative path for parent class: " + path, p_class); if (err) {
return ERR_FILE_NOT_FOUND; return err;
} }
path = base.get_base_dir().plus_file(path).simplify_path(); parsing_classes.erase(base_name);
} }
script = ResourceLoader::load(path); Ref<GDScript> base = class_map[base_name];
if (script.is_null()) { p_script->base = base;
_set_error("Could not load base class: " + path, p_class);
return ERR_FILE_NOT_FOUND;
}
if (!script->valid) {
_set_error("Script not fully loaded (cyclic preload?): " + path, p_class);
return ERR_BUSY;
}
//print_line("EXTENDS PATH: "+path+" script is "+itos(script.is_valid())+" indices is "+itos(script->member_indices.size())+" valid? "+itos(script->valid));
if (p_class->extends_class.size()) {
for (int i = 0; i < p_class->extends_class.size(); i++) {
String sub = p_class->extends_class[i];
if (script->subclasses.has(sub)) {
Ref<Script> subclass = script->subclasses[sub]; //avoid reference from disappearing
script = subclass;
} else {
_set_error("Could not find subclass: " + sub, p_class);
return ERR_FILE_NOT_FOUND;
}
}
}
} else {
ERR_FAIL_COND_V(p_class->extends_class.size() == 0, ERR_BUG);
//look around for the subclasses
String base = p_class->extends_class[0];
GDScript *p = p_owner;
Ref<GDScript> base_class;
while (p) {
if (p->subclasses.has(base)) {
base_class = p->subclasses[base];
break;
}
if (p->constants.has(base)) {
base_class = p->constants[base];
if (base_class.is_null()) {
_set_error("Constant is not a class: " + base, p_class);
return ERR_SCRIPT_FAILED;
}
break;
}
p = p->_owner;
}
if (base_class.is_valid()) {
String ident = base;
for (int i = 1; i < p_class->extends_class.size(); i++) {
String subclass = p_class->extends_class[i];
ident += ("." + subclass);
if (base_class->subclasses.has(subclass)) {
base_class = base_class->subclasses[subclass];
} else if (base_class->constants.has(subclass)) {
Ref<GDScript> new_base_class = base_class->constants[subclass];
if (new_base_class.is_null()) {
_set_error("Constant is not a class: " + ident, p_class);
return ERR_SCRIPT_FAILED;
}
base_class = new_base_class;
} else {
_set_error("Could not find subclass: " + ident, p_class);
return ERR_FILE_NOT_FOUND;
}
}
script = base_class;
} else {
if (p_class->extends_class.size() > 1) {
_set_error("Invalid inheritance (unknown class+subclasses)", p_class);
return ERR_FILE_NOT_FOUND;
}
//if not found, try engine classes
if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
_set_error("Unknown class: '" + base + "'", p_class);
return ERR_FILE_NOT_FOUND;
}
int base_idx = GDScriptLanguage::get_singleton()->get_global_map()[base];
native = GDScriptLanguage::get_singleton()->get_global_array()[base_idx];
if (!native.is_valid()) {
_set_error("Global not a class: '" + base + "'", p_class);
return ERR_FILE_NOT_FOUND;
}
}
}
if (script.is_valid()) {
p_script->base = script;
p_script->_base = p_script->base.ptr(); p_script->_base = p_script->base.ptr();
p_script->member_indices = script->member_indices; p_script->member_indices = base->member_indices;
} break;
} else if (native.is_valid()) { case GDScriptParser::DataType::GDSCRIPT: {
Ref<GDScript> base = p_class->base_type.script_type;
p_script->base = base;
p_script->_base = p_script->base.ptr();
p_script->member_indices = base->member_indices;
} break;
case GDScriptParser::DataType::NATIVE: {
int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_class->base_type.native_type];
native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
p_script->native = native; p_script->native = native;
} else { } break;
default: {
_set_error("Could not determine inheritance", p_class); _set_error("Parser bug: invalid inheritance.", p_class);
return ERR_FILE_NOT_FOUND; return ERR_BUG;
} } break;
} else {
// without extends, implicitly extend Reference
int native_idx = GDScriptLanguage::get_singleton()->get_global_map()["Reference"];
native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
p_script->native = native;
} }
//print_line("Script: "+p_script->get_path()+" indices: "+itos(p_script->member_indices.size()));
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;
@ -1902,6 +1773,7 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
for (int i = 0; i < p_class->constant_expressions.size(); i++) { for (int i = 0; i < p_class->constant_expressions.size(); i++) {
StringName name = p_class->constant_expressions[i].identifier; StringName name = p_class->constant_expressions[i].identifier;
ERR_CONTINUE(p_class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT); ERR_CONTINUE(p_class->constant_expressions[i].expression->type != GDScriptParser::Node::TYPE_CONSTANT);
if (_is_class_member_property(p_script, name)) { if (_is_class_member_property(p_script, name)) {
@ -1948,23 +1820,27 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
p_script->_signals[name] = p_class->_signals[i].arguments; p_script->_signals[name] = p_class->_signals[i].arguments;
} }
if (p_class->name != StringName()) {
parsed_classes.insert(p_class->name);
}
//parse sub-classes //parse sub-classes
for (int i = 0; i < p_class->subclasses.size(); i++) { for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name; StringName name = p_class->subclasses[i]->name;
Ref<GDScript> subclass; Ref<GDScript> subclass = class_map[name];
if (old_subclasses.has(name)) { // Subclass might still be parsing, just skip it
subclass = old_subclasses[name]; if (!parsed_classes.has(name) && !parsing_classes.has(name)) {
} else { parsing_classes.insert(name);
subclass.instance(); Error err = _parse_class_level(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
if (err)
return err;
parsing_classes.erase(name);
} }
Error err = _parse_class(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
if (err)
return err;
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
p_script->member_lines[name] = p_class->subclasses[i]->line; p_script->member_lines[name] = p_class->subclasses[i]->line;
@ -1974,6 +1850,11 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
p_script->subclasses.insert(name, subclass); p_script->subclasses.insert(name, subclass);
} }
p_script->valid = true;
return OK;
}
Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
//parse methods //parse methods
bool has_initializer = false; bool has_initializer = false;
@ -2014,44 +1895,6 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
} }
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
//validate setters/getters if debug is enabled
for (int i = 0; i < p_class->variables.size(); i++) {
if (p_class->variables[i].setter) {
const Map<StringName, GDScriptFunction *>::Element *E = p_script->get_member_functions().find(p_class->variables[i].setter);
if (!E) {
_set_error("Setter function '" + String(p_class->variables[i].setter) + "' not found in class.", NULL);
err_line = p_class->variables[i].line;
err_column = 0;
return ERR_PARSE_ERROR;
}
if (E->get()->is_static()) {
_set_error("Setter function '" + String(p_class->variables[i].setter) + "' is static.", NULL);
err_line = p_class->variables[i].line;
err_column = 0;
return ERR_PARSE_ERROR;
}
}
if (p_class->variables[i].getter) {
const Map<StringName, GDScriptFunction *>::Element *E = p_script->get_member_functions().find(p_class->variables[i].getter);
if (!E) {
_set_error("Getter function '" + String(p_class->variables[i].getter) + "' not found in class.", NULL);
err_line = p_class->variables[i].line;
err_column = 0;
return ERR_PARSE_ERROR;
}
if (E->get()->is_static()) {
_set_error("Getter function '" + String(p_class->variables[i].getter) + "' is static.", NULL);
err_line = p_class->variables[i].line;
err_column = 0;
return ERR_PARSE_ERROR;
}
}
}
//validate instances if keeping state //validate instances if keeping state
@ -2104,10 +1947,45 @@ Error GDScriptCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, cons
} }
#endif #endif
p_script->valid = true; for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
Ref<GDScript> subclass = class_map[name];
Error err = _parse_class_blocks(subclass.ptr(), p_class->subclasses[i], p_keep_state);
if (err) {
return err;
}
}
return OK; return OK;
} }
void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
Map<StringName, Ref<GDScript> > old_subclasses;
if (p_keep_state) {
old_subclasses = p_script->subclasses;
}
for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
Ref<GDScript> subclass;
if (old_subclasses.has(name)) {
subclass = old_subclasses[name];
} else {
subclass.instance();
}
subclass->_owner = const_cast<GDScript *>(p_script);
class_map.insert(name, subclass);
_make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state);
}
}
Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) { Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) {
err_line = -1; err_line = -1;
@ -2119,7 +1997,15 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
source = p_script->get_path(); source = p_script->get_path();
Error err = _parse_class(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state); // Create scripts for subclasses beforehand so they can be referenced
_make_scripts(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
Error err = _parse_class_level(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
if (err)
return err;
err = _parse_class_blocks(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
if (err) if (err)
return err; return err;

View File

@ -31,12 +31,16 @@
#ifndef GDSCRIPT_COMPILER_H #ifndef GDSCRIPT_COMPILER_H
#define GDSCRIPT_COMPILER_H #define GDSCRIPT_COMPILER_H
#include "core/set.h"
#include "gdscript.h" #include "gdscript.h"
#include "gdscript_parser.h" #include "gdscript_parser.h"
class GDScriptCompiler { class GDScriptCompiler {
const GDScriptParser *parser; const GDScriptParser *parser;
Map<StringName, Ref<GDScript> > class_map;
Set<StringName> parsed_classes;
Set<StringName> parsing_classes;
struct CodeGen { struct CodeGen {
GDScript *script; GDScript *script;
@ -142,7 +146,9 @@ class GDScriptCompiler {
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);
Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false); Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
Error _parse_class(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state); Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
int err_line; int err_line;
int err_column; int err_column;
StringName source; StringName source;

View File

@ -3188,10 +3188,25 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
name = tokenizer->get_token_identifier(1); name = tokenizer->get_token_identifier(1);
tokenizer->advance(2); tokenizer->advance(2);
// Check if name is shadowing something else
if (ClassDB::class_exists(name)) {
_set_error("Class '" + String(name) + "' shadows a native class.");
return;
}
if (ScriptServer::is_global_class(name)) { if (ScriptServer::is_global_class(name)) {
_set_error("Can't override name of unique global class '" + name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name)); _set_error("Can't override name of unique global class '" + name + "' already exists at path: " + ScriptServer::get_global_class_path(p_class->name));
return; return;
} }
if (class_map.has(name)) {
_set_error("Class '" + String(name) + "' shadows another class in the file.");
return;
}
for (int i = 0; i < p_class->constant_expressions.size(); i++) {
if (p_class->constant_expressions[i].identifier == name) {
_set_error("Class '" + String(name) + "' shadows a constant of the outer class.");
return;
}
}
ClassNode *newclass = alloc_node<ClassNode>(); ClassNode *newclass = alloc_node<ClassNode>();
newclass->initializer = alloc_node<BlockNode>(); newclass->initializer = alloc_node<BlockNode>();
@ -3202,6 +3217,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
newclass->owner = p_class; newclass->owner = p_class;
p_class->subclasses.push_back(newclass); p_class->subclasses.push_back(newclass);
class_map.insert(name, newclass);
if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_EXTENDS) { if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_EXTENDS) {
@ -4337,6 +4353,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return; return;
} }
int line = tokenizer->get_token_line();
tokenizer->advance(); tokenizer->advance();
Node *subexpr = _parse_and_reduce_expression(p_class, true, true); Node *subexpr = _parse_and_reduce_expression(p_class, true, true);
@ -4348,14 +4366,15 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
} }
if (subexpr->type != Node::TYPE_CONSTANT) { if (subexpr->type != Node::TYPE_CONSTANT) {
_set_error("Expected constant expression"); _set_error("Expected constant expression", line);
return;
} }
constant.expression = subexpr; constant.expression = subexpr;
p_class->constant_expressions.push_back(constant); p_class->constant_expressions.push_back(constant);
if (!_end_statement()) { if (!_end_statement()) {
_set_error("Expected end of statement (constant)"); _set_error("Expected end of statement (constant)", line);
return; return;
} }
@ -4415,17 +4434,19 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (subexpr->type != Node::TYPE_CONSTANT) { if (subexpr->type != Node::TYPE_CONSTANT) {
_set_error("Expected constant expression"); _set_error("Expected constant expression");
return;
} }
const ConstantNode *subexpr_const = static_cast<const ConstantNode *>(subexpr); ConstantNode *subexpr_const = static_cast<ConstantNode *>(subexpr);
if (subexpr_const->value.get_type() != Variant::INT) { if (subexpr_const->value.get_type() != Variant::INT) {
_set_error("Expected an int value for enum"); _set_error("Expected an int value for enum");
return;
} }
last_assign = subexpr_const->value; last_assign = subexpr_const->value;
constant.expression = subexpr; constant.expression = subexpr_const;
} else { } else {
last_assign = last_assign + 1; last_assign = last_assign + 1;
@ -4483,6 +4504,212 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
} }
} }
void GDScriptParser::_determine_inheritance(ClassNode *p_class) {
if (p_class->extends_used) {
//do inheritance
String path = p_class->extends_file;
Ref<GDScript> script;
StringName native;
ClassNode *base_class = NULL;
if (path != "") {
//path (and optionally subclasses)
if (path.is_rel_path()) {
String base = self_path;
if (base == "" || base.is_rel_path()) {
_set_error("Could not resolve relative path for parent class: " + path, p_class->line);
return;
}
path = base.get_base_dir().plus_file(path).simplify_path();
}
script = ResourceLoader::load(path);
if (script.is_null()) {
_set_error("Could not load base class: " + path, p_class->line);
return;
}
if (!script->is_valid()) {
_set_error("Script not fully loaded (cyclic preload?): " + path, p_class->line);
return;
}
if (p_class->extends_class.size()) {
for (int i = 0; i < p_class->extends_class.size(); i++) {
String sub = p_class->extends_class[i];
if (script->get_subclasses().has(sub)) {
Ref<Script> subclass = script->get_subclasses()[sub]; //avoid reference from disappearing
script = subclass;
} else {
_set_error("Could not find subclass: " + sub, p_class->line);
return;
}
}
}
} else {
if (p_class->extends_class.size() == 0) {
_set_error("Parser bug: undecidable inheritance.", p_class->line);
ERR_FAIL();
}
//look around for the subclasses
int extend_iter = 1;
String base = p_class->extends_class[0];
ClassNode *p = p_class->owner;
Ref<GDScript> base_script;
if (ScriptServer::is_global_class(base)) {
base_script = ResourceLoader::load(ScriptServer::get_global_class_path(base));
if (!base_script.is_valid()) {
_set_error("Class '" + base + "' could not be fully loaded (script error or cyclic inheritance).", p_class->line);
return;
}
p = NULL;
}
while (p) {
bool found = false;
for (int i = 0; i < p->subclasses.size(); i++) {
if (p->subclasses[i]->name == base) {
ClassNode *test = p->subclasses[i];
while (test) {
if (test == p_class) {
_set_error("Cyclic inheritance.", test->line);
return;
}
if (test->base_type.kind == DataType::CLASS) {
test = test->base_type.class_type;
} else {
break;
}
}
found = true;
if (extend_iter < p_class->extends_class.size()) {
// Keep looking at current classes if possible
base = p_class->extends_class[extend_iter++];
p = p->subclasses[i];
} else {
base_class = p->subclasses[i];
}
break;
}
}
if (base_class) break;
if (found) continue;
for (int i = 0; i < p->constant_expressions.size(); i++) {
if (p->constant_expressions[i].identifier == base) {
if (!p->constant_expressions[i].expression->type == Node::TYPE_CONSTANT) {
_set_error("Could not resolve constant '" + base + "'.", p_class->line);
return;
}
const ConstantNode *cn = static_cast<const ConstantNode *>(p->constant_expressions[i].expression);
base_script = cn->value;
if (base_script.is_null()) {
_set_error("Constant is not a class: " + base, p_class->line);
return;
}
found = true;
}
}
if (found) break;
p = p->owner;
}
if (base_script.is_valid()) {
String ident = base;
for (int i = extend_iter; i < p_class->extends_class.size(); i++) {
String subclass = p_class->extends_class[i];
ident += ("." + subclass);
if (base_script->get_subclasses().has(subclass)) {
base_script = base_script->get_subclasses()[subclass];
} else if (base_script->get_constants().has(subclass)) {
Ref<GDScript> new_base_class = base_script->get_constants()[subclass];
if (new_base_class.is_null()) {
_set_error("Constant is not a class: " + ident, p_class->line);
return;
}
base_script = new_base_class;
} else {
_set_error("Could not find subclass: " + ident, p_class->line);
return;
}
}
script = base_script;
} else if (!base_class) {
if (p_class->extends_class.size() > 1) {
_set_error("Invalid inheritance (unknown class + subclasses)", p_class->line);
return;
}
//if not found, try engine classes
if (!GDScriptLanguage::get_singleton()->get_global_map().has(base)) {
_set_error("Unknown class: '" + base + "'", p_class->line);
return;
}
native = base;
}
}
if (base_class) {
p_class->base_type.has_type = true;
p_class->base_type.kind = DataType::CLASS;
p_class->base_type.class_type = base_class;
} else if (script.is_valid()) {
p_class->base_type.has_type = true;
p_class->base_type.kind = DataType::GDSCRIPT;
p_class->base_type.script_type = script;
p_class->base_type.native_type = script->get_instance_base_type();
} else if (native != StringName()) {
p_class->base_type.has_type = true;
p_class->base_type.kind = DataType::NATIVE;
p_class->base_type.native_type = native;
} else {
_set_error("Could not determine inheritance", p_class->line);
return;
}
} else {
// without extends, implicitly extend Reference
p_class->base_type.has_type = true;
p_class->base_type.kind = DataType::NATIVE;
p_class->base_type.native_type = "Reference";
}
// Recursively determine subclasses
for (int i = 0; i < p_class->subclasses.size(); i++) {
_determine_inheritance(p_class->subclasses[i]);
}
}
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();
@ -4553,9 +4780,15 @@ Error GDScriptParser::_parse(const String &p_base_path) {
} }
if (error_set) { if (error_set) {
return ERR_PARSE_ERROR; return ERR_PARSE_ERROR;
} }
_determine_inheritance(main_class);
if (error_set) {
return ERR_PARSE_ERROR;
}
return OK; return OK;
} }

View File

@ -107,6 +107,7 @@ public:
struct FunctionNode; struct FunctionNode;
struct BlockNode; struct BlockNode;
struct ConstantNode;
struct ClassNode : public Node { struct ClassNode : public Node {
@ -115,6 +116,7 @@ public:
bool extends_used; bool extends_used;
StringName extends_file; StringName extends_file;
Vector<StringName> extends_class; Vector<StringName> extends_class;
DataType base_type;
struct Member { struct Member {
PropertyInfo _export; PropertyInfo _export;
@ -446,6 +448,7 @@ private:
ClassNode *current_class; ClassNode *current_class;
FunctionNode *current_function; FunctionNode *current_function;
BlockNode *current_block; BlockNode *current_block;
Map<StringName, ClassNode *> class_map;
bool _get_completable_identifier(CompletionType p_type, StringName &identifier); bool _get_completable_identifier(CompletionType p_type, StringName &identifier);
void _make_completable_call(int p_arg); void _make_completable_call(int p_arg);
@ -487,6 +490,7 @@ private:
void _parse_class(ClassNode *p_class); void _parse_class(ClassNode *p_class);
bool _end_statement(); bool _end_statement();
void _determine_inheritance(ClassNode *p_class);
bool _parse_type(DataType &r_type, bool p_can_be_void = false); bool _parse_type(DataType &r_type, bool p_can_be_void = false);
Error _parse(const String &p_base_path); Error _parse(const String &p_base_path);