Source: core/find_in_c_source.js

/*
 * This file is part of PKM (Persistent Knowledge Monitor).
 * Copyright (c) 2020 Capgemini Group, Commissariat à l'énergie atomique et aux énergies alternatives,
 *                    OW2, Sysgo AG, Technikon, Tree Technology, Universitat Politècnica de València.
 * 
 * PKM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation.
 * 
 * PKM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with PKM.  If not, see <https://www.gnu.org/licenses/>.
 */

/**
 * find in source
 * 
 * @memberof PKM
 * @instance
 * @param {string} dbName - database name
 * @param {string} kind - either 'function', 'variable', 'type', 'filename', 'dir'
 * @param {string} [query_value] - query value (optional)
 * 
 * @return {Promise<Array.<Object>>} a promise that passes the result (an array) to resolve callback
 */
function find_in_c_source(dbName, kind, query_value)
{
	return new Promise((resolve, reject) =>
	{
		const debug = this.debug;
		this.get_collection(dbName, 'sourcecodeC').then((source_code_collection) =>
		{
			this.get_collection(dbName, 'RawSourcecode').then((raw_source_code_collection) =>
			{
				function find_functions_definitions(source_code_collection, raw_source_code_collection, function_name)
				{
					return new Promise((resolve, reject) =>
					{
						var results = [];
						if(debug) console.warn("Searching AST files for function definition...");
						const query = {"globals.GFun.fundec.svar.varinfo.vname": function_name ? function_name : { $exists: true }};
						var source_code_cursor = source_code_collection.find(query);
						let count = 0;
						source_code_cursor.forEach((source_code_document) =>
						{
							if(Array.isArray(source_code_document.globals))
							{
								source_code_document.globals.forEach((global) =>
								{
									if(global.GFun &&
									global.GFun.fundec &&
									global.GFun.fundec.svar &&
									global.GFun.fundec.svar.varinfo &&
									(!function_name || (global.GFun.fundec.svar.varinfo.vname == function_name)))
									{
										var result = {};
										count = count + 1;
										if(debug) console.warn("> found function definition (" + count + ")");
										result.fundef = global.GFun.fundec;
									
										result.files = [];
										
										if(global.GFun.loc && global.GFun.loc.pos_start && global.GFun.loc.pos_start.pos_path)
										{
											// searching for file associated to the current element:
											let filename = global.GFun.loc.pos_start.pos_path;
											if(debug) console.warn("> located in file " + filename + " (" + count + ")");
											// Find the content of this file
											var raw_source_code_cursor = raw_source_code_collection.find({"filename":filename});
										
											if(debug) console.warn("Searching in raw files...");
											raw_source_code_cursor.forEach((raw_source_code_document) =>
											{
												result.files.push(raw_source_code_document.filename);
											}, (err) =>
											{
												if(err)
												{
													throw err;
												}
												else
												{
													results.push(result);
												}
											});
										}
									}
								});
							}
						}, (err) =>
						{
							if(err)
							{
								reject(this.Error(err));
							}
							else
							{
								resolve(results);
							}
						});
					});
				}
				
				function find_functions_declarations(source_code_collection, raw_source_code_collection, function_name)
				{
					return new Promise((resolve, reject) =>
					{
						var results = [];
						// Lookup the GFunDecl
						if(debug) console.warn("Searching AST files for function declaration...");
						const query = {"globals.GFunDecl.varinfo.vname": function_name ? function_name : { $exists: true }};
						var source_code_cursor = source_code_collection.find(query);
						// var tab2= element2.toArray(function (err,documents) {
						// 		console.log("> found function declaration (" + documents.length+")");
						// });
						let count = 0;
						source_code_cursor.forEach((source_code_document) =>
						{
							if(Array.isArray(source_code_document.globals))
							{
								source_code_document.globals.forEach((global) =>
								{
									if(global.GFunDecl &&
									global.GFunDecl.varinfo &&
									(!function_name || (global.GFunDecl.varinfo.vname == function_name)))
									{
										var result = {};
										count = count + 1;
										if(debug) console.warn("> found function declaration (" + count + ")");
										result.fundecl = global.GFunDecl.funspec;
											
										result.files = [];
										
										if(global.GFunDecl.loc && global.GFunDecl.loc.pos_start && global.GFunDecl.loc.pos_start.pos_path)
										{
											// searching for file associated to the current element:
											let filename = global.GFunDecl.loc.pos_start.pos_path;
											if(debug) console.warn("> located in file " + filename + " (" + count + ")");
											// Find the content of this file
											var raw_source_code_cursor = raw_source_code_collection.find({"filename":filename});
											if(debug) console.warn("> Searching in raw files...");
												
											raw_source_code_cursor.forEach((raw_source_code_document) =>
											{
												result.files.push(raw_source_code_document.filename);
											}, (err) =>
											{
												if(err)
												{
													throw err;
												}
												else
												{
													results.push(result);
												}
											});
										}
									}
								});
							}
						}, (err) =>
						{
							if(err)
							{
								reject(this.Error(err));
							}
							else
							{
								resolve(results);
							}
						});
					});
				}
				
				function find_functions(source_code_collection, raw_source_code_collection, function_name)
				// Search for function definitions and declarations.
				// Returns a JSON document
				// [{"fundecl|fundef": <AST>, "filename":"XXX"},...]
				// if filename is "" the no file could be found.
				{
					return new Promise((resolve, reject) =>
					{
						find_functions_definitions(source_code_collection, raw_source_code_collection, function_name).then((function_definitions_results) =>
						{
							find_functions_declarations(source_code_collection, raw_source_code_collection, function_name).then((function_declarations_results) =>
							{
								const find_functions_results = function_definitions_results.concat(function_declarations_results);
								if(find_functions_results.length)
								{
									resolve(find_functions_results);
								}
								else
								{
									reject(this.NotFound('No function named \'' + function_name + '\' found'));
								}
							}).catch((err) =>
							{
								reject(this.Error(err));
							});
						}).catch((err) =>
						{
							reject(this.Error(err));
						});
					});
				}
				
				function find_variable_definitions(source_code_collection, raw_source_code_collection, variable_name)
				{
					return new Promise((resolve, reject) =>
					{
						var results = [];
						if(debug) console.warn("Searching AST files for variable definition...");
						const query = {"globals.GVar.varinfo.vname": variable_name ? variable_name : { $exists: true }};
						var source_code_cursor = source_code_collection.find(query);
						let count = 0;
						source_code_cursor.forEach((source_code_document) =>
						{
							if(Array.isArray(source_code_document.globals))
							{
								source_code_document.globals.forEach((global) =>
								{
									if(global.GVar &&
									global.GVar.varinfo &&
									(!variable_name || (global.GVar.varinfo.vname == variable_name)))
									{
										var result = {};
										count = count+1;
										if(debug) console.warn("> found definition (" + count + ")");
										result.varinfo = global.GVar;
											
										result.files = [];
										
										if(global.GVar.loc && global.GVar.loc.pos_start && global.GVar.loc.pos_start.pos_path)
										{
											// searching for file associated to the current element:
											let filename = global.GVar.loc.pos_start.pos_path;
											if(debug) console.warn("> located in file " + filename + " (" + count + ")");
												
											// Find the content of this file
											var raw_source_code_cursor = raw_source_code_collection.find({"filename":filename});
											if(debug) console.warn("Searching in raw files...");
												
											raw_source_code_cursor.forEach((raw_source_code_document) =>
											{
												result.files.push(raw_source_code_document.filename);
											}, (err) =>
											{
												if(err)
												{
													throw err;
												}
												else
												{
													results.push(result);
												}
											});
										}
									}
								});
							}
						}, (err) =>
						{
							if(err)
							{
								reject(this.Error(err));
							}
							else
							{
								resolve(results);
							}
						});
					});
				}
				
				function find_variable_declarations(source_code_collection, raw_source_code_collection, variable_name)
				{
					return new Promise((resolve, reject) =>
					{
						var results = [];
						// Lookup the GVarDecl
						if(debug) console.warn("Searching AST files for variable declaration...");
						const query = {"globals.GVarDecl.varinfo.vname": variable_name ? variable_name : { $exists: true }};
						var source_code_cursor = source_code_collection.find(query);
						let count = 0;
						source_code_cursor.forEach((source_code_document) =>
						{
							if(Array.isArray(source_code_document.globals))
							{
								source_code_document.globals.forEach((global) =>
								{
									if(global.GVarDecl &&
									global.GVarDecl.varinfo &&
									(!variable_name || (global.GVarDecl.varinfo == variable_name)))
									{
										var result = {};
										count = count + 1;
										if(debug) console.warn("> found variable declaration (" + count + ")");
										result.varinfo = global.GVarDecl.varinfo;
											
										result.files = [];
										
										if(global.GVarDecl.loc && global.GVarDecl.loc.pos_start && global.GVarDecl.loc.pos_start.pos_path)
										{
											// searching for file associated to the current element:
											let filename = global.GVarDecl.loc.pos_start.pos_path;
											if(debug) console.warn("> located in file " + filename + " (" + count + ")");
											// Find the content of this file
											var raw_source_code_cursor = raw_source_code_collection.find({"filename":filename});
											if(debug) console.warn("> Searching in raw files...");
											raw_source_code_cursor.forEach((raw_source_code_document) =>
											{
												result.files.push(raw_source_code_document.filename);
											}, (err) =>
											{
												if(err)
												{
													throw err;
												}
												else
												{
													results.push(result);
												}
											});
										}
									}
								});
							}
						}, (err) =>
						{
							if(err)
							{
								reject(this.Error(err));
							}
							else
							{
								resolve(results);
							}
						});
					});
				}
				
				function find_variables(source_code_collection, raw_source_code_collection, variable_name)
				// Returns a JSON document
				// [{"vardef|vardecl": <AST>, "filename":"XXX"},...]
				// if filename is "" the no file could be found.
				{
					return new Promise((resolve, reject) =>
					{
						find_variable_definitions(source_code_collection, raw_source_code_collection, variable_name).then((variable_definitions_results) =>
						{
							find_variable_declarations(source_code_collection, raw_source_code_collection, variable_name).then((variable_declarations_results) =>
							{
								const find_variables_results = variable_definitions_results.concat(variable_declarations_results);
								if(find_variables_results.length)
								{
									resolve(find_variables_results);
								}
								else
								{
									reject(this.NotFound('No variable named \'' + variable_name + '\' found'));
								}
							}).catch((err) =>
							{
								reject(this.Error(err));
							});
						}).catch((err) =>
						{
							reject(this.Error(err));
						});
					});
				}
				
				function find_type_definitions(source_code_collection, raw_source_code_collection, type_name)
				// Returns a JSON document
				// [{"typedef": <AST>, "filename":"XXX"},...]
				// if filename is "" the no file could be found.
				{
					return new Promise((resolve, reject) =>
					{
						var results = [];
						if(debug) console.warn("Searching AST files for type (Comp/Enum) definition...");
						const query = {"globals.GCompTag.compinfo.cname": type_name ? type_name : { $exists: true }};
						var source_code_cursor = source_code_collection.find(query);
						let count = 0;
						source_code_cursor.forEach((source_code_document) =>
						{
							if(Array.isArray(source_code_document.globals))
							{
								source_code_document.globals.forEach((global) =>
								{
									if(global.GCompTag &&
									global.GCompTag.compinfo &&
									(!type_name || (global.GCompTag.compinfo.cname == type_name)))
									{
										var result = {};
										count = count + 1;
										if(debug) console.warn("> found type definition (" + count + ")");
										result.typedef = global.GCompTag.compinfo;
										
										result.files = [];
										
										if(global.GCompTag.loc && global.GCompTag.loc.pos_start && global.GCompTag.loc.pos_start.pos_path)
										{
											// searching for file associated to the current element:
											let filename = global.GCompTag.loc.pos_start.pos_path;
											if(debug) console.warn("> located in file " + filename + " (" + count + ")");
											// Find the content of this file
											var raw_source_code_cursor = raw_source_code_collection.find({"filename":filename});																															
											if(debug) console.warn("Searching in raw files...");
											raw_source_code_cursor.forEach((raw_source_code_document) =>
											{
												result.files.push(raw_source_code_document.filename);
											}, (err) =>
											{
												if(err)
												{
													throw err;
												}
												else
												{
													results.push(result);
												}
											});
										}
									}
								});
							}
						}, (err) =>
						{
							if(err)
							{
								reject(this.Error(err));
							}
							else
							{
								resolve(results);
							}
						});
					});
				}
				
				function find_type_declarations(source_code_collection, raw_source_code_collection, type_name)
				// Returns a JSON document
				// [{"typedecl": <AST>, "filename":"XXX"},...]
				// if filename is "" the no file could be found.
				{
					return new Promise((resolve, reject) =>
					{
						var results = [];
						// Lookup the GCompTagDecl
						if(debug) console.warn("Searching AST files for type declaration...");
						const query = {"globals.GCompTagDecl.compinfo.cname": type_name ? type_name : { $exists: true }};
						var source_code_cursor = source_code_collection.find(query);
						let count = 0;
						source_code_cursor.forEach((source_code_document) =>
						{
							if(Array.isArray(source_code_document.globals))
							{
								source_code_document.globals.forEach((global) =>
								{
									if(global.GCompTagDecl &&
									global.GCompTagDecl.compinfo &&
									(!type_name || (global.GCompTagDecl.compinfo.cname == type_name)))
									{
										var result = {};
										count = count + 1;
										if(debug) console.warn("> found type declaration (" + count + ")");
										result.typedecl = global.GCompTagDecl.compinfo;
											
										result.files = [];
										
										if(global.GCompTagDecl.loc && global.GCompTagDecl.loc.pos_start && global.GCompTagDecl.loc.pos_start.pos_path)
										{
											// searching for file associated to the current element:
											let filename = global.GCompTagDecl.loc.pos_start.pos_path;
											if(debug) console.warn("> located in file " + filename + " (" + count + ")");
											// Find the content of this file
											var raw_source_code_cursor = raw_source_code_collection.find({"filename":filename});
											if(debug) console.warn("> Searching raw files...");
											raw_source_code_cursor.forEach((raw_source_code_document) =>
											{
												result.files.push(raw_source_code_document.filename);
											}, (err) =>
											{
												if(err)
												{
													throw err;
												}
												else
												{
													results.push(result);
												}
											});
										}
									}
								});
							}
						}, (err) =>
						{
							if(err)
							{
								reject(this.Error(err));
							}
							else
							{
								resolve(results);
							}
						});
					});
				}
				
				function find_types(source_code_collection, raw_source_code_collection, type_name)
				{
					return new Promise((resolve, reject) =>
					{
						find_type_definitions(source_code_collection, raw_source_code_collection, type_name).then((type_definitions_results) =>
						{
							find_type_declarations(source_code_collection, raw_source_code_collection, type_name).then((type_declarations_results) =>
							{
								const find_types_results = type_definitions_results.concat(type_declarations_results);
								if(find_types_results.length)
								{
									resolve(find_types_results);
								}
								else
								{
									reject(this.NotFound('No type named \'' + type_name + '\' found'));
								}
							}).catch((err) =>
							{
								reject(this.Error(err));
							});
						}).catch((err) =>
						{
							reject(this.Error(err));
						});
					});
				}

				function find_files(raw_source_code_collection, filename)
				{
					return new Promise((resolve, reject) =>
					{
						var results = [];
						if(debug) console.warn("Searching for File " + filename + ".");
						const query = {"filename" : filename ? filename : { $exists: true }};
						var raw_source_code_cursor = raw_source_code_collection.find(query);

						if(debug) console.warn("> found files ");
						raw_source_code_cursor.forEach((raw_source_code_document) =>
						{
							results.push(
								{ 
									filename : raw_source_code_document.filename,
									filecontent : raw_source_code_document.filecontent
								}
							);
						}, (err) =>
						{
							if(err)
							{
								reject(this.Error(err));
							}
							else if(results.length)
							{
								resolve(results);
							}
							else
							{
								reject(this.NotFound('No file named \'' + filename + '\' found'));
							}
						});
					});
				}
				
				function find_dir(raw_source_code_collection, dir_name)
				{
					const path = require ('path');
					return new Promise((resolve, reject) =>
					{
						var results = [];
						var raw_source_code_cursor = raw_source_code_collection.find({});
						raw_source_code_cursor.forEach((raw_source_code_document) =>
						{
							let found_dir_name = path.dirname(raw_source_code_document.filename);
							if(!dir_name || found_dir_name.startsWith(dir_name))
							{
								if(debug) console.warn("Found 1 file");
								results.push(
									{ 
										filename : raw_source_code_document.filename,
										filecontent : raw_source_code_document.filecontent
									}
								);
							}
						}, (err) =>
						{
							if(err)
							{
								reject(this.Error(err));
							}
							else if(results.length)
							{
								
								resolve(results);
							}
							else
							{
								reject(this.NotFound('No directory named \'' + dir_name + '\' found'));
							}
						});
					});
				}
	
				if(kind == "function")
				{
					find_functions(source_code_collection, raw_source_code_collection, query_value)
						.then((results) => resolve(results))
						.catch((err) => reject(this.Error(err)));
				}
				else if(kind == "variable")
				{
					find_variables(source_code_collection, raw_source_code_collection, query_value)
						.then((results) => resolve(results))
						.catch((err) => reject(this.Error(err)));
				}
				else if(kind == "type")
				{
					find_types(source_code_collection, raw_source_code_collection, query_value)
						.then((results) => resolve(results))
						.catch((err) => reject(this.Error(err)));
				}
				else if(kind == "file")
				{
					// Extract the file including the raw file content in a JSON document:
					// the filename is relative to DB_ROOT. 
					// Returns: {"filename": "XXX", "filecontent": "YYYY"}
					find_files(raw_source_code_collection, query_value)
						.then((results) => resolve(results))
						.catch((err) => reject(this.Error(err)));
				}
				else if(kind == "dir")
				{
					// Extract all source files within the specified dir,in raw form:
					// all filenames are relative to DB_ROOT
					// Returns: [{"filename":"XXX", "filecontent":"YYY"}, {}, ...]
					find_dir(raw_source_code_collection, query_value)
						.then((results) => resolve(results))
						.catch((err) => reject(this.Error(err)));
				}
				else
				{
					reject(this.NotFound('kind not found'));
				}
			}).catch((err) =>
			{
				reject(this.Error(err));
			});
		}).catch((err) =>
		{
			reject(this.Error(err));
		});
	});
}

exports.find_in_c_source = find_in_c_source;