Преглед на файлове

Fix GDScriptCompiler bugging out with identically-named inner class

Fixes #27111
Bojidar Marinov преди 6 години
родител
ревизия
466a76ac2c
променени са 2 файла, в които са добавени 70 реда и са изтрити 129 реда
  1. 66 124
      modules/gdscript/gdscript_compiler.cpp
  2. 4 5
      modules/gdscript/gdscript_compiler.h

+ 66 - 124
modules/gdscript/gdscript_compiler.cpp

@@ -139,17 +139,32 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
 			result.native_type = result.script_type->get_instance_base_type();
 		} break;
 		case GDScriptParser::DataType::CLASS: {
-			result.kind = GDScriptDataType::GDSCRIPT;
-			if (!p_datatype.class_type->owner) {
-				result.script_type = Ref<GDScript>(main_script);
-			} else {
-				result.script_type = class_map[p_datatype.class_type->name];
+			// Locate class by constructing the path to it and following that path
+			GDScriptParser::ClassNode *class_type = p_datatype.class_type;
+			List<StringName> names;
+			while (class_type->owner) {
+				names.push_back(class_type->name);
+				class_type = class_type->owner;
 			}
-			result.native_type = result.script_type->get_instance_base_type();
+
+			Ref<GDScript> script = Ref<GDScript>(main_script);
+			while (names.back()) {
+				if (!script->subclasses.has(names.back()->get())) {
+					ERR_PRINT("Parser bug: Cannot locate datatype class.");
+					result.has_type = false;
+					return GDScriptDataType();
+				}
+				script = script->subclasses[names.back()->get()];
+				names.pop_back();
+			}
+
+			result.kind = GDScriptDataType::GDSCRIPT;
+			result.script_type = script;
+			result.native_type = script->get_instance_base_type();
 		} break;
 		default: {
 			ERR_PRINT("Parser bug: converting unresolved type.");
-			result.has_type = false;
+			return GDScriptDataType();
 		}
 	}
 
@@ -460,12 +475,14 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
 				codegen.alloc_stack(slevel);
 			}
 
-			switch (cn->cast_type.kind) {
-				case GDScriptParser::DataType::BUILTIN: {
+			GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type);
+
+			switch (cast_type.kind) {
+				case GDScriptDataType::BUILTIN: {
 					codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
 					codegen.opcodes.push_back(cn->cast_type.builtin_type);
 				} break;
-				case GDScriptParser::DataType::NATIVE: {
+				case GDScriptDataType::NATIVE: {
 					int class_idx;
 					if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) {
 
@@ -478,32 +495,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
 					codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator
 					codegen.opcodes.push_back(class_idx); // variable type
 				} break;
-				case GDScriptParser::DataType::CLASS: {
-
-					Variant script;
-					int idx = -1;
-					if (!cn->cast_type.class_type->owner) {
-						script = codegen.script;
-					} else {
-						StringName name = cn->cast_type.class_type->name;
-						if (codegen.script->subclasses.has(name) && class_map[name] == codegen.script->subclasses[name]) {
-							idx = codegen.get_name_map_pos(name);
-							idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
-						} else {
-							script = class_map[name];
-						}
-					}
-
-					if (idx < 0) {
-						idx = codegen.get_constant_pos(script);
-						idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
-					}
-
-					codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
-					codegen.opcodes.push_back(idx); // variable type
-				} break;
-				case GDScriptParser::DataType::SCRIPT:
-				case GDScriptParser::DataType::GDSCRIPT: {
+				case GDScriptDataType::SCRIPT:
+				case GDScriptDataType::GDSCRIPT: {
 
 					Variant script = cn->cast_type.script_type;
 					int idx = codegen.get_constant_pos(script);
@@ -1149,18 +1142,18 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
 						if (src_address_b < 0)
 							return -1;
 
-						GDScriptParser::DataType assign_type = on->arguments[0]->get_datatype();
+						GDScriptDataType assign_type = _gdtype_from_datatype(on->arguments[0]->get_datatype());
 
 						if (assign_type.has_type && !on->arguments[1]->get_datatype().has_type) {
 							// Typed assignment
 							switch (assign_type.kind) {
-								case GDScriptParser::DataType::BUILTIN: {
+								case GDScriptDataType::BUILTIN: {
 									codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
 									codegen.opcodes.push_back(assign_type.builtin_type); // variable type
 									codegen.opcodes.push_back(dst_address_a); // argument 1
 									codegen.opcodes.push_back(src_address_b); // argument 2
 								} break;
-								case GDScriptParser::DataType::NATIVE: {
+								case GDScriptDataType::NATIVE: {
 									int class_idx;
 									if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) {
 
@@ -1175,34 +1168,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
 									codegen.opcodes.push_back(dst_address_a); // argument 1
 									codegen.opcodes.push_back(src_address_b); // argument 2
 								} break;
-								case GDScriptParser::DataType::CLASS: {
-
-									Variant script;
-									int idx = -1;
-									if (!assign_type.class_type->owner) {
-										script = codegen.script;
-									} else {
-										StringName name = assign_type.class_type->name;
-										if (codegen.script->subclasses.has(name) && class_map[name] == codegen.script->subclasses[name]) {
-											idx = codegen.get_name_map_pos(name);
-											idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
-										} else {
-											script = class_map[name];
-										}
-									}
-
-									if (idx < 0) {
-										idx = codegen.get_constant_pos(script);
-										idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
-									}
-
-									codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
-									codegen.opcodes.push_back(idx); // variable type
-									codegen.opcodes.push_back(dst_address_a); // argument 1
-									codegen.opcodes.push_back(src_address_b); // argument 2
-								} break;
-								case GDScriptParser::DataType::SCRIPT:
-								case GDScriptParser::DataType::GDSCRIPT: {
+								case GDScriptDataType::SCRIPT:
+								case GDScriptDataType::GDSCRIPT: {
 
 									Variant script = assign_type.script_type;
 									int idx = codegen.get_constant_pos(script);
@@ -1850,22 +1817,21 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
 	return OK;
 }
 
-Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+
+	parsing_classes.insert(p_script);
 
 	if (p_class->owner && p_class->owner->owner) {
 		// Owner is not root
-		StringName owner_name = p_class->owner->name;
-		if (!parsed_classes.has(owner_name)) {
-			if (parsing_classes.has(owner_name)) {
-				_set_error("Cyclic class reference for '" + String(owner_name) + "'.", p_class);
+		if (!parsed_classes.has(p_script->_owner)) {
+			if (parsing_classes.has(p_script->_owner)) {
+				_set_error("Cyclic class reference for '" + String(p_class->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);
+			Error err = _parse_class_level(p_script->_owner, p_class->owner, p_keep_state);
 			if (err) {
 				return err;
 			}
-			parsing_classes.erase(owner_name);
 		}
 	}
 
@@ -1883,47 +1849,26 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
 	p_script->_signals.clear();
 	p_script->initializer = NULL;
 
-	p_script->subclasses.clear();
-	p_script->_owner = p_owner;
 	p_script->tool = p_class->tool;
 	p_script->name = p_class->name;
 
 	Ref<GDScriptNativeClass> native;
 
+	GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type);
 	// Inheritance
-	switch (p_class->base_type.kind) {
-		case GDScriptParser::DataType::CLASS: {
-			StringName base_name = p_class->base_type.class_type->name;
-			// Make sure dependency is parsed first
-			if (!parsed_classes.has(base_name)) {
-				if (parsing_classes.has(base_name)) {
-					_set_error("Cyclic class reference for '" + String(base_name) + "'.", p_class);
-					return ERR_PARSE_ERROR;
-				}
-				parsing_classes.insert(base_name);
-				Error err = _parse_class_level(class_map[base_name].ptr(), class_map[base_name]->_owner, p_class->base_type.class_type, p_keep_state);
-				if (err) {
-					return err;
-				}
-				parsing_classes.erase(base_name);
-			}
-			Ref<GDScript> base = class_map[base_name];
-			p_script->base = base;
-			p_script->_base = p_script->base.ptr();
-			p_script->member_indices = base->member_indices;
-		} break;
-		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];
+	switch (base_type.kind) {
+		case GDScriptDataType::NATIVE: {
+			int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[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;
 		} break;
+		case GDScriptDataType::GDSCRIPT: {
+			Ref<GDScript> base = base_type.script_type;
+			p_script->base = base;
+			p_script->_base = base.ptr();
+			p_script->member_indices = base->member_indices;
+		} break;
 		default: {
 			_set_error("Parser bug: invalid inheritance.", p_class);
 			return ERR_BUG;
@@ -2017,24 +1962,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
 		p_script->_signals[name] = p_class->_signals[i].arguments;
 	}
 
-	if (p_class->owner) {
-		parsed_classes.insert(p_class->name);
-		if (parsing_classes.has(p_class->name)) {
-			parsing_classes.erase(p_class->name);
-		}
-	}
+	parsed_classes.insert(p_script);
+	parsing_classes.erase(p_script);
 
 	//parse sub-classes
 
 	for (int i = 0; i < p_class->subclasses.size(); i++) {
 		StringName name = p_class->subclasses[i]->name;
 
-		Ref<GDScript> subclass = class_map[name];
+		GDScript *subclass = p_script->subclasses[name].ptr();
 
 		// Subclass might still be parsing, just skip it
-		if (!parsed_classes.has(name) && !parsing_classes.has(name)) {
-			parsing_classes.insert(name);
-			Error err = _parse_class_level(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
+		if (!parsed_classes.has(subclass) && !parsing_classes.has(subclass)) {
+			Error err = _parse_class_level(subclass, p_class->subclasses[i], p_keep_state);
 			if (err)
 				return err;
 		}
@@ -2045,7 +1985,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
 #endif
 
 		p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants
-		p_script->subclasses.insert(name, subclass);
 	}
 
 	return OK;
@@ -2144,9 +2083,9 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
 
 	for (int i = 0; i < p_class->subclasses.size(); i++) {
 		StringName name = p_class->subclasses[i]->name;
-		Ref<GDScript> subclass = class_map[name];
+		GDScript *subclass = p_script->subclasses[name].ptr();
 
-		Error err = _parse_class_blocks(subclass.ptr(), p_class->subclasses[i], p_keep_state);
+		Error err = _parse_class_blocks(subclass, p_class->subclasses[i], p_keep_state);
 		if (err) {
 			return err;
 		}
@@ -2156,7 +2095,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
 	return OK;
 }
 
-void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
 
 	Map<StringName, Ref<GDScript> > old_subclasses;
 
@@ -2164,6 +2103,8 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar
 		old_subclasses = p_script->subclasses;
 	}
 
+	p_script->subclasses.clear();
+
 	for (int i = 0; i < p_class->subclasses.size(); i++) {
 		StringName name = p_class->subclasses[i]->name;
 
@@ -2175,10 +2116,10 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar
 			subclass.instance();
 		}
 
-		subclass->_owner = const_cast<GDScript *>(p_script);
-		class_map.insert(name, subclass);
+		subclass->_owner = p_script;
+		p_script->subclasses.insert(name, subclass);
 
-		_make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state);
+		_make_scripts(subclass.ptr(), p_class->subclasses[i], false);
 	}
 }
 
@@ -2197,7 +2138,8 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
 	// 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);
+	p_script->_owner = NULL;
+	Error err = _parse_class_level(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
 
 	if (err)
 		return err;

+ 4 - 5
modules/gdscript/gdscript_compiler.h

@@ -38,9 +38,8 @@
 class GDScriptCompiler {
 
 	const GDScriptParser *parser;
-	Map<StringName, Ref<GDScript> > class_map;
-	Set<StringName> parsed_classes;
-	Set<StringName> parsing_classes;
+	Set<GDScript *> parsed_classes;
+	Set<GDScript *> parsing_classes;
 	GDScript *main_script;
 	struct CodeGen {
 
@@ -149,9 +148,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);
 	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_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+	Error _parse_class_level(GDScript *p_script, 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);
+	void _make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
 	int err_line;
 	int err_column;
 	StringName source;