Browse Source

Merge pull request #26547 from vnen/gdscript-dependency-parse

Add a parse mode for GDScript which doesn't load dependencies
Juan Linietsky 6 years ago
parent
commit
a9fe834a8e

+ 17 - 0
core/os/file_access.cpp

@@ -409,6 +409,23 @@ int FileAccess::get_buffer(uint8_t *p_dst, int p_length) const {
 	return i;
 }
 
+String FileAccess::get_as_utf8_string() const {
+	PoolVector<uint8_t> sourcef;
+	int len = get_len();
+	sourcef.resize(len + 1);
+
+	PoolVector<uint8_t>::Write w = sourcef.write();
+	int r = get_buffer(w.ptr(), len);
+	ERR_FAIL_COND_V(r != len, String());
+	w[len] = 0;
+
+	String s;
+	if (s.parse_utf8((const char *)w.ptr())) {
+		return String();
+	}
+	return s;
+}
+
 void FileAccess::store_16(uint16_t p_dest) {
 
 	uint8_t a, b;

+ 1 - 0
core/os/file_access.h

@@ -113,6 +113,7 @@ public:
 	virtual String get_line() const;
 	virtual String get_token() const;
 	virtual Vector<String> get_csv_line(const String &p_delim = ",") const;
+	virtual String get_as_utf8_string() const;
 
 	/**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
 	 * It's not about the current CPU type but file formats.

+ 87 - 49
modules/gdscript/gdscript.cpp

@@ -1840,68 +1840,86 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
 		return String();
 	}
 
-	int len = f->get_len();
-	sourcef.resize(len + 1);
-	PoolVector<uint8_t>::Write w = sourcef.write();
-	int r = f->get_buffer(w.ptr(), len);
-	f->close();
-	memdelete(f);
-	ERR_FAIL_COND_V(r != len, String());
-	w[len] = 0;
-
-	String s;
-	if (s.parse_utf8((const char *)w.ptr())) {
-		return String();
-	}
+	String source = f->get_as_utf8_string();
 
 	GDScriptParser parser;
-
-	parser.parse(s, p_path.get_base_dir(), true, p_path);
+	parser.parse(source, p_path.get_base_dir(), true, p_path, false, NULL, true);
 
 	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());
+		if (r_icon_path) {
+			if (c->icon_path.empty() || c->icon_path.is_abs_path())
+				*r_icon_path = c->icon_path;
+			else if (c->icon_path.is_rel_path())
+				*r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path();
+		}
 		if (r_base_type) {
-			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();
+
+			const GDScriptParser::ClassNode *subclass = c;
+			String path = p_path;
+			GDScriptParser subparser;
+			while (subclass) {
+				if (subclass->extends_used) {
+					if (subclass->extends_file) {
+						if (subclass->extends_class.size() == 0) {
+							get_global_class_name(subclass->extends_file, r_base_type);
+							subclass = NULL;
+							break;
+						} else {
+							Vector<StringName> extend_classes = subclass->extends_class;
+
+							FileAccess *subfile = FileAccess::open(subclass->extends_file, FileAccess::READ);
+							if (!subfile) {
+								break;
+							}
+							String subsource = subfile->get_as_utf8_string();
+							if (subsource.empty()) {
+								break;
+							}
+							String subpath = subclass->extends_file;
+							if (subpath.is_rel_path()) {
+								subpath = path.get_base_dir().plus_file(subpath).simplify_path();
+							}
+
+							if (OK != subparser.parse(subsource, subpath.get_base_dir(), true, subpath, false, NULL, true)) {
+								break;
 							}
-						} break;
-						default: {
-							base_type = GDScriptParser::DataType();
-						} break;
+							path = subpath;
+							if (!subparser.get_parse_tree() || subparser.get_parse_tree()->type != GDScriptParser::Node::TYPE_CLASS) {
+								break;
+							}
+							subclass = static_cast<const GDScriptParser::ClassNode *>(subparser.get_parse_tree());
+
+							while (extend_classes.size() > 0) {
+								bool found = false;
+								for (int i = 0; i < subclass->subclasses.size(); i++) {
+									const GDScriptParser::ClassNode *inner_class = subclass->subclasses[i];
+									if (inner_class->name == extend_classes[0]) {
+										extend_classes.remove(0);
+										found = true;
+										subclass = inner_class;
+										break;
+									}
+								}
+								if (!found) {
+									subclass = NULL;
+									break;
+								}
+							}
+						}
+					} else if (subclass->extends_class.size() == 1) {
+						*r_base_type = subclass->extends_class[0];
+						subclass = NULL;
+					} else {
+						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) {
+				} else {
 					*r_base_type = "Reference";
+					subclass = NULL;
 				}
 			}
 		}
-		if (r_icon_path) {
-			if (c->icon_path.empty() || c->icon_path.is_abs_path())
-				*r_icon_path = c->icon_path;
-			else if (c->icon_path.is_rel_path())
-				*r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path();
-		}
 		return c->name;
 	}
 
@@ -2183,6 +2201,26 @@ String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) con
 	return "";
 }
 
+void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
+
+	FileAccess *file = FileAccess::open(p_path, FileAccess::READ);
+	ERR_FAIL_COND(!file);
+
+	String source = file->get_as_utf8_string();
+	if (source.empty()) {
+		return;
+	}
+
+	GDScriptParser parser;
+	if (OK != parser.parse(source, p_path.get_base_dir(), true, p_path, false, NULL, true)) {
+		return;
+	}
+
+	for (const List<String>::Element *E = parser.get_dependencies().front(); E; E = E->next()) {
+		p_dependencies->push_back(E->get());
+	}
+}
+
 Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
 
 	Ref<GDScript> sqscr = p_resource;

+ 1 - 0
modules/gdscript/gdscript.h

@@ -511,6 +511,7 @@ public:
 	virtual void get_recognized_extensions(List<String> *p_extensions) const;
 	virtual bool handles_type(const String &p_type) const;
 	virtual String get_resource_type(const String &p_path) const;
+	virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
 };
 
 class ResourceFormatSaverGDScript : public ResourceFormatSaver {

+ 46 - 28
modules/gdscript/gdscript_parser.cpp

@@ -473,29 +473,31 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
 			}
 
 			Ref<Resource> res;
-			if (!validating) {
+			dependencies.push_back(path);
+			if (!dependencies_only) {
+				if (!validating) {
 
-				//this can be too slow for just validating code
-				if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) {
-					res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
-				} else if (!for_completion || FileAccess::exists(path)) {
-					res = ResourceLoader::load(path);
-				}
-			} else {
+					//this can be too slow for just validating code
+					if (for_completion && ScriptCodeCompletionCache::get_singleton() && FileAccess::exists(path)) {
+						res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
+					} else if (!for_completion || FileAccess::exists(path)) {
+						res = ResourceLoader::load(path);
+					}
+				} else {
 
-				if (!FileAccess::exists(path)) {
+					if (!FileAccess::exists(path)) {
+						_set_error("Can't preload resource at path: " + path);
+						return NULL;
+					} else if (ScriptCodeCompletionCache::get_singleton()) {
+						res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
+					}
+				}
+				if (!res.is_valid()) {
 					_set_error("Can't preload resource at path: " + path);
 					return NULL;
-				} else if (ScriptCodeCompletionCache::get_singleton()) {
-					res = ScriptCodeCompletionCache::get_singleton()->get_cached_resource(path);
 				}
 			}
 
-			if (!res.is_valid()) {
-				_set_error("Can't preload resource at path: " + path);
-				return NULL;
-			}
-
 			if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) {
 				_set_error("Expected ')' after 'preload' path");
 				return NULL;
@@ -812,17 +814,19 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
 					bfn = true;
 				}
 
-				// Check parents for the constant
-				if (!bfn && cln->extends_file != StringName()) {
-					Ref<GDScript> parent = ResourceLoader::load(cln->extends_file);
-					if (parent.is_valid() && parent->is_valid()) {
-						Map<StringName, Variant> parent_constants;
-						parent->get_constants(&parent_constants);
-						if (parent_constants.has(identifier)) {
-							ConstantNode *constant = alloc_node<ConstantNode>();
-							constant->value = parent_constants[identifier];
-							expr = constant;
-							bfn = true;
+				if (!dependencies_only) {
+					// Check parents for the constant
+					if (!bfn && cln->extends_file != StringName()) {
+						Ref<GDScript> parent = ResourceLoader::load(cln->extends_file);
+						if (parent.is_valid() && parent->is_valid()) {
+							Map<StringName, Variant> parent_constants;
+							parent->get_constants(&parent_constants);
+							if (parent_constants.has(identifier)) {
+								ConstantNode *constant = alloc_node<ConstantNode>();
+								constant->value = parent_constants[identifier];
+								expr = constant;
+								bfn = true;
+							}
 						}
 					}
 				}
@@ -3378,6 +3382,13 @@ void GDScriptParser::_parse_extends(ClassNode *p_class) {
 		p_class->extends_file = constant;
 		tokenizer->advance();
 
+		// Add parent script as a dependency
+		String parent = constant;
+		if (parent.is_rel_path()) {
+			parent = base_path.plus_file(parent).simplify_path();
+		}
+		dependencies.push_back(parent);
+
 		if (tokenizer->get_token() != GDScriptTokenizer::TK_PERIOD) {
 			return;
 		} else
@@ -8152,6 +8163,10 @@ Error GDScriptParser::_parse(const String &p_base_path) {
 		return ERR_PARSE_ERROR;
 	}
 
+	if (dependencies_only) {
+		return OK;
+	}
+
 	_determine_inheritance(main_class);
 
 	if (error_set) {
@@ -8230,7 +8245,7 @@ Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const St
 	return ret;
 }
 
-Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines) {
+Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines, bool p_dependencies_only) {
 
 	clear();
 
@@ -8240,6 +8255,7 @@ Error GDScriptParser::parse(const String &p_code, const String &p_base_path, boo
 
 	validating = p_just_validate;
 	for_completion = p_for_completion;
+	dependencies_only = p_dependencies_only;
 #ifdef DEBUG_ENABLED
 	safe_lines = r_safe_lines;
 #endif // DEBUG_ENABLED
@@ -8296,6 +8312,8 @@ void GDScriptParser::clear() {
 	parenthesis = 0;
 	current_export.type = Variant::NIL;
 	check_types = true;
+	dependencies_only = false;
+	dependencies.clear();
 	error = "";
 #ifdef DEBUG_ENABLED
 	safe_lines = NULL;

+ 5 - 1
modules/gdscript/gdscript_parser.h

@@ -533,6 +533,8 @@ private:
 	int error_line;
 	int error_column;
 	bool check_types;
+	bool dependencies_only;
+	List<String> dependencies;
 #ifdef DEBUG_ENABLED
 	Set<int> *safe_lines;
 #endif // DEBUG_ENABLED
@@ -634,7 +636,7 @@ public:
 #ifdef DEBUG_ENABLED
 	const List<GDScriptWarning> &get_warnings() const { return warnings; }
 #endif // DEBUG_ENABLED
-	Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL);
+	Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL, bool p_dependencies_only = false);
 	Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = "");
 
 	bool is_tool_script() const;
@@ -653,6 +655,8 @@ public:
 	int get_completion_argument_index();
 	int get_completion_identifier_is_function();
 
+	const List<String> &get_dependencies() const { return dependencies; }
+
 	void clear();
 	GDScriptParser();
 	~GDScriptParser();