// Filename: interrogateBuilder.cxx // Created by: drose (01Aug00) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://etc.cmu.edu/panda3d/docs/license/ . // // To contact the maintainers of this program write to // panda3d-general@lists.sourceforge.net . // //////////////////////////////////////////////////////////////////// #include "interrogateBuilder.h" #include "interrogate.h" #include "parameterRemap.h" #include "typeManager.h" #include "functionWriters.h" #include "interfaceMakerC.h" #include "interfaceMakerPythonObj.h" #include "interfaceMakerPythonSimple.h" #include "functionRemap.h" #include "interrogateType.h" #include "interrogateDatabase.h" #include "indexRemapper.h" #include "cppParser.h" #include "cppDeclaration.h" #include "cppFunctionGroup.h" #include "cppFunctionType.h" #include "cppParameterList.h" #include "cppInstance.h" #include "cppSimpleType.h" #include "cppPointerType.h" #include "cppReferenceType.h" #include "cppArrayType.h" #include "cppConstType.h" #include "cppExtensionType.h" #include "cppStructType.h" #include "cppExpression.h" #include "cppTypedef.h" #include "cppTypeDeclaration.h" #include "cppEnumType.h" #include "cppCommentBlock.h" #include "notify.h" #include #include InterrogateBuilder builder; /* static string upcase_string(const string &str) { string result; for (string::const_iterator si = str.begin(); si != str.end(); ++si) { result += toupper(*si); } return result; } */ //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::add_source_file // Access: Public // Description: Adds the given source filename to the list of files // that we are scanning. Those source files that appear // to be header files will be #included in the generated // code file. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: add_source_file(const string &filename) { if (filename.empty()) { return; } _include_files[filename] = '"'; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::read_command_file // Access: Public // Description: Reads a .N file that might contain control // information for the interrogate process. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: read_command_file(istream &in) { string line; getline(in, line); while (!in.fail() && !in.eof()) { // Strip out the comment. size_t hash = line.find('#'); if (hash != string::npos) { line = line.substr(0, hash); } // Skip leading whitespace. size_t p = 0; while (p < line.length() && isspace(line[p])) { p++; } if (p < line.length()) { // Get the first word. size_t q = p; while (q < line.length() && !isspace(line[q])) { q++; } string command = line.substr(p, q - p); // Get the rest. p = q; while (p < line.length() && isspace(line[p])) { p++; } // Except for the trailing whitespace. q = line.length(); while (q > p && isspace(line[q - 1])) { q--; } string params = line.substr(p, q - p); do_command(command, params); } getline(in, line); } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::do_command // Access: Public // Description: Executes a single command as read from the .N file. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: do_command(const string &command, const string ¶ms) { if (command == "forcetype") { // forcetype explicitly exports the given type. CPPType *type = parser.parse_type(params); if (type == (CPPType *)NULL) { nout << "Unknown type: forcetype " << params << "\n"; } else { type = type->resolve_type(&parser, &parser); _forcetype.insert(type->get_local_name(&parser)); } } else if (command == "renametype") { // rename exports the type as the indicated name. We strip off // the last word as the new name; the new name may not contain // spaces (although the original type name may). size_t space = params.rfind(' '); if (space == string::npos) { nout << "No new name specified for renametype " << params << "\n"; } else { string orig_name = params.substr(0, space); string new_name = params.substr(space + 1); CPPType *type = parser.parse_type(orig_name); if (type == (CPPType *)NULL) { nout << "Unknown type: renametype " << orig_name << "\n"; } else { type = type->resolve_type(&parser, &parser); _renametype[type->get_local_name(&parser)] = new_name; } } } else if (command == "ignoretype") { // ignoretype explicitly ignores the given type. CPPType *type = parser.parse_type(params); if (type == (CPPType *)NULL) { nout << "Unknown type: ignoretype " << params << "\n"; } else { type = type->resolve_type(&parser, &parser); _ignoretype.insert(type->get_local_name(&parser)); } } else if (command == "ignoreinvolved") { _ignoreinvolved.insert(params); } else if (command == "ignorefile") { insert_param_list(_ignorefile, params); } else if (command == "ignoremember") { insert_param_list(_ignoremember, params); } else if (command == "noinclude") { insert_param_list(_noinclude, params); } else { nout << "Ignoring " << command << " " << params << "\n"; } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::build // Access: Public // Description: Builds all of the interrogate data. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: build() { _library_hash_name = hash_string(library_name, 5); // Make sure we have the complete set of #includes we need. CPPParser::Includes::const_iterator ii; for (ii = parser._quote_includes.begin(); ii != parser._quote_includes.end(); ++ii) { const string &filename = (*ii); _include_files[filename] = '"'; } for (ii = parser._angle_includes.begin(); ii != parser._angle_includes.end(); ++ii) { const string &filename = (*ii); _include_files[filename] = '<'; } // First, get all the types that were explicitly forced. Commands::const_iterator ci; for (ci = _forcetype.begin(); ci != _forcetype.end(); ++ci) { CPPType *type = parser.parse_type(*ci); assert(type != (CPPType *)NULL); get_type(type, true); } // Now go through all of the top-level declarations in the file(s). CPPScope::Declarations::const_iterator di; for (di = parser._declarations.begin(); di != parser._declarations.end(); ++di) { if ((*di)->get_subtype() == CPPDeclaration::ST_instance) { CPPInstance *inst = (*di)->as_instance(); if (inst->_type->get_subtype() == CPPDeclaration::ST_function) { // Here's a function declaration. scan_function(inst); } else { // Here's a data element declaration. scan_element(inst, (CPPStructType *)NULL, &parser); } } else if ((*di)->get_subtype() == CPPDeclaration::ST_typedef) { CPPTypedef *tdef = (*di)->as_typedef(); if (tdef->_type->get_subtype() == CPPDeclaration::ST_struct) { // A typedef counts as a declaration. This lets us pick up // most template instantiations. CPPStructType *struct_type = tdef->_type->resolve_type(&parser, &parser)->as_struct_type(); scan_struct_type(struct_type); } } else if ((*di)->get_subtype() == CPPDeclaration::ST_type_declaration) { CPPType *type = (*di)->as_type_declaration()->_type; type->_vis = (*di)->_vis; if (type->get_subtype() == CPPDeclaration::ST_struct) { CPPStructType *struct_type = type->as_type()->resolve_type(&parser, &parser)->as_struct_type(); scan_struct_type(struct_type); } else if (type->get_subtype() == CPPDeclaration::ST_enum) { CPPEnumType *enum_type = type->as_type()->resolve_type(&parser, &parser)->as_enum_type(); scan_enum_type(enum_type); } } } CPPPreprocessor::Manifests::const_iterator mi; for (mi = parser._manifests.begin(); mi != parser._manifests.end(); ++mi) { CPPManifest *manifest = (*mi).second; scan_manifest(manifest); } // Now that we've gone through all the code and generated all the // functions and types, build the function wrappers. // make_wrappers(); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::write_code // Access: Public // Description: Generates all the code necessary to the indicated // output stream. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: write_code(ostream &out, InterrogateModuleDef *def) { typedef vector InterfaceMakers; InterfaceMakers makers; if (build_c_wrappers) { InterfaceMaker *maker = new InterfaceMakerC(def); makers.push_back(maker); } if (build_python_wrappers) { InterfaceMaker *maker = new InterfaceMakerPythonSimple(def); makers.push_back(maker); } if (build_python_obj_wrappers) { InterfaceMaker *maker = new InterfaceMakerPythonObj(def); makers.push_back(maker); } InterfaceMakers::iterator mi; // First, make all the wrappers. for (mi = makers.begin(); mi != makers.end(); ++mi) { (*mi)->generate_wrappers(); } // Now generate all the function bodies to a temporary buffer. By // generating these first, we ensure that we know all of the // pointers we'll be using ahead of time (and can therefore generate // correct prototypes). ostringstream function_bodies; for (mi = makers.begin(); mi != makers.end(); ++mi) { (*mi)->write_functions(function_bodies); } // Now, begin the actual output. Start with the #include lines. if (!no_database) { out << "#include \"dtoolbase.h\"\n" << "#include \"interrogate_request.h\"\n" << "#include \"dconfig.h\"\n"; } if (watch_asserts) { out << "#include \"notify.h\"\n"; } out << "\n"; IncludeFiles::const_iterator ifi; for (ifi = _include_files.begin(); ifi != _include_files.end(); ++ifi) { const string &filename = (*ifi).first; char delimiter = (*ifi).second; if (should_include(filename)) { if (delimiter == '"') { out << "#include \"" << filename << "\"\n"; } else { out << "#include <" << filename << ">\n"; } } } out << "\n"; for (mi = makers.begin(); mi != makers.end(); ++mi) { (*mi)->write_includes(out); } if (generate_spam) { out << "#include \"config_interrogatedb.h\"\n" << "#include \"notifyCategoryProxy.h\"\n\n" << "NotifyCategoryDeclNoExport(in_" << library_name << ");\n" << "NotifyCategoryDef(in_" << library_name << ", interrogatedb_cat);\n\n"; } out << "\n"; // And now the prototypes. for (mi = makers.begin(); mi != makers.end(); ++mi) { (*mi)->write_prototypes(out); } out << "\n"; // Followed by the function bodies. out << function_bodies.str() << "\n"; if (output_module_specific) { // Output whatever stuff we should output if this were a module. for (mi = makers.begin(); mi != makers.end(); ++mi) { (*mi)->write_module(out, def); } } // Now collect all the function wrappers. vector remaps; for (mi = makers.begin(); mi != makers.end(); ++mi) { (*mi)->get_function_remaps(remaps); } // Make sure all of the function wrappers appear first in the set of // indices, and that they occupy consecutive index numbers, so we // can build a simple array of function pointers by index. remap_indices(remaps); // Get the function wrappers in index-number order. int num_wrappers = 0; map wrappers_by_index; vector::iterator ri; for (ri = remaps.begin(); ri != remaps.end(); ++ri) { FunctionRemap *remap = (*ri); wrappers_by_index[remap->_wrapper_index] = remap; num_wrappers++; } if (output_function_pointers) { // Write out the table of function pointers. out << "static void *_in_fptrs[" << num_wrappers << "] = {\n"; int next_index = 1; map::iterator ii; for (ii = wrappers_by_index.begin(); ii != wrappers_by_index.end(); ++ii) { int this_index = (*ii).first; while (next_index < this_index) { out << " (void *)0,\n"; next_index++; } assert(next_index == this_index); FunctionRemap *remap = (*ii).second; out << " (void *)&" << remap->_wrapper_name << ",\n"; next_index++; } while (next_index < num_wrappers + 1) { out << " (void *)0,\n"; next_index++; } out << "};\n\n"; } if (save_unique_names) { // Write out the table of unique names, in no particular order. out << "static InterrogateUniqueNameDef _in_unique_names[" << num_wrappers << "] = {\n"; for (ri = remaps.begin(); ri != remaps.end(); ++ri) { FunctionRemap *remap = (*ri); out << " { \"" << remap->_unique_name << "\", " << remap->_wrapper_index - 1 << " },\n"; } out << "};\n\n"; } if (!no_database) { // Now build the module definition structure to add ourselves to // the global interrogate database. out << "static InterrogateModuleDef _in_module_def = {\n" << " " << def->file_identifier << ", /* file_identifier */\n" << " \"" << def->library_name << "\", /* library_name */\n" << " \"" << def->library_hash_name << "\", /* library_hash_name */\n" << " \"" << def->module_name << "\", /* module_name */\n"; if (def->database_filename != (const char *)NULL) { out << " \"" << def->database_filename << "\", /* database_filename */\n"; } else { out << " (const char *)0, /* database_filename */\n"; } if (save_unique_names) { out << " _in_unique_names,\n" << " " << num_wrappers << ", /* num_unique_names */\n"; } else { out << " (InterrogateUniqueNameDef *)0, /* unique_names */\n" << " 0, /* num_unique_names */\n"; } if (output_function_pointers) { out << " _in_fptrs,\n" << " " << num_wrappers << ", /* num_fptrs */\n"; } else { out << " (void **)0, /* fptrs */\n" << " 0, /* num_fptrs */\n"; } out << " 1, /* first_index */\n" << " " << InterrogateDatabase::get_ptr()->get_next_index() << " /* next_index */\n" << "};\n\n"; // And now write the static-init code that tells the interrogate // database to load up this module. out << "Configure(_in_configure_" << library_name << ");\n" << "ConfigureFn(_in_configure_" << library_name << ") {\n" << " interrogate_request_module(&_in_module_def);\n" << "}\n\n"; } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::make_module_def // Access: Public // Description: Allocates and returns a new InterrogateModuleDef // structure that reflects the data we have just build, // or at least that subset of the InterrogateModuleDef // data that we have available at this time. // // The data in this structure may include pointers that // reference directly into the InterrogateBuilder // object; thus, this structure is only valid for as // long as the builder itself remains in scope. //////////////////////////////////////////////////////////////////// InterrogateModuleDef *InterrogateBuilder:: make_module_def(int file_identifier) { InterrogateModuleDef *def = new InterrogateModuleDef; memset(def, 0, sizeof(InterrogateModuleDef)); def->file_identifier = file_identifier; def->library_name = library_name.c_str(); def->library_hash_name = _library_hash_name.c_str(); def->module_name = module_name.c_str(); if (!output_data_filename.empty()) { def->database_filename = output_data_basename.c_str(); } return def; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::clean_identifier // Access: Public, Static // Description: Adjusts the given string to remove any characters we // don't want to export as part of an identifier name. // Returns the cleaned string. // // This replaces any consecutive invalid characters with // an underscore. //////////////////////////////////////////////////////////////////// string InterrogateBuilder:: clean_identifier(const string &name) { string result; bool last_invalid = false; string::const_iterator ni; for (ni = name.begin(); ni != name.end(); ++ni) { if (isalnum(*ni)) { if (last_invalid) { result += '_'; last_invalid = false; } result += (*ni); } else { last_invalid = true; } } return result; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::descope // Access: Public, Static // Description: Removes the leading "::", if present, from a // fully-scoped name. Sometimes CPPParser throws this // on, and sometimes it doesn't. //////////////////////////////////////////////////////////////////// string InterrogateBuilder:: descope(const string &name) { if (name.length() >= 2 && name.substr(0, 2) == "::") { return name.substr(2); } return name; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::get_destructor_for // Access: Public // Description: Returns the FunctionIndex for the destructor // appropriate to destruct an instance of the indicated // type, or 0 if no suitable destructor exists. //////////////////////////////////////////////////////////////////// FunctionIndex InterrogateBuilder:: get_destructor_for(CPPType *type) { TypeIndex type_index = get_type(type, false); const InterrogateType &itype = InterrogateDatabase::get_ptr()->get_type(type_index); return itype.get_destructor(); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::get_preferred_name // Access: Public // Description: Returns the name of the type as it should be reported // to the database. This is either the name indicated // by the user via a renametype command, or the // "preferred name" of the type itself (i.e. the typedef // name within the C++ code), or failing that, the // type's true name. //////////////////////////////////////////////////////////////////// string InterrogateBuilder:: get_preferred_name(CPPType *type) { string true_name = type->get_local_name(&parser); string name = in_renametype(true_name); if (!name.empty()) { return name; } return type->get_preferred_name(); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::hash_string // Access: Public, Static // Description: Hashes an arbitrary string into a four-character // string using only the characters legal in a C // identifier. //////////////////////////////////////////////////////////////////// string InterrogateBuilder:: hash_string(const string &name, int shift_offset) { int hash = 0; int shift = 0; string::const_iterator ni; for (ni = name.begin(); ni != name.end(); ++ni) { int c = (int)(unsigned char)(*ni); int shifted_c = (c << shift) & 0xffffff; if (shift > 16) { // We actually want a circular shift, not an arithmetic shift. shifted_c |= ((c >> (24 - shift)) & 0xff) ; } hash = (hash + shifted_c) & 0xffffff; shift = (shift + shift_offset) % 24; } // Now multiply the hash by a biggish prime number and apply the // high-order bits back at the bottom, to scramble up the bits a // bit. This helps reduce hash conflicts from names that are // similar to each other, by separating adjacent hash codes. int prime = 4999; int low_order = (hash * prime) & 0xffffff; int high_order = (int)((double)hash * (double)prime / (double)(1 << 24)); hash = low_order ^ high_order; // Also add in the additional_number, times some prime factor. // hash = (hash + additional_number * 1657) & 0xffffff; // Now turn the hash code into a four-character string. For each // six bits, we choose a character in the set [A-Za-z0-9_]. Note // that there are only 63 characters to choose from; we have to // duplicate '_' for values 62 and 63. This introduces a small // additional chance of hash conflicts. No big deal, since we have // to resolve hash conflicts anyway. string result; int extract_h = hash; for (int i = 0; i < 4; i++) { int value = (extract_h & 0x3f); extract_h >>= 6; if (value < 26) { result += (char)('A' + value); } else if (value < 52) { result += (char)('a' + value - 26); } else if (value < 62) { result += (char)('0' + value - 52); } else { result += '_'; } } return result; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::insert_param_list // Access: Public // Description: Inserts a list of space-separated parameters into the // given command parameter list. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: insert_param_list(InterrogateBuilder::Commands &commands, const string ¶ms) { size_t p = 0; while (p < params.length()) { while (p < params.length() && isspace(params[p])) { p++; } size_t q = p; while (q < params.length() && !isspace(params[q])) { q++; } if (p < q) { commands.insert(params.substr(p, q - p)); } p = q; } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::in_forcetype // Access: Private // Description: Returns true if the indicated name is one that the // user identified with a forcetype command. //////////////////////////////////////////////////////////////////// bool InterrogateBuilder:: in_forcetype(const string &name) const { return (_forcetype.count(name) != 0); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::in_renametype // Access: Private // Description: If the user requested an explicit name for this type // via the renametype command, returns that name; // otherwise, returns the empty string. //////////////////////////////////////////////////////////////////// string InterrogateBuilder:: in_renametype(const string &name) const { CommandParams::const_iterator pi; pi = _renametype.find(name); if (pi != _renametype.end()) { return (*pi).second; } return string(); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::in_ignoretype // Access: Private // Description: Returns true if the indicated name is one that the // user identified with an ignoretype command. //////////////////////////////////////////////////////////////////// bool InterrogateBuilder:: in_ignoretype(const string &name) const { return (_ignoretype.count(name) != 0); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::in_ignoreinvolved // Access: Private // Description: Returns true if the indicated name is one that the // user identified with an ignoreinvolved command. //////////////////////////////////////////////////////////////////// bool InterrogateBuilder:: in_ignoreinvolved(const string &name) const { return (_ignoreinvolved.count(name) != 0); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::in_ignoreinvolved // Access: Private // Description: Returns true if the indicated type involves some type // name that the user identified with an ignoreinvolved // command. //////////////////////////////////////////////////////////////////// bool InterrogateBuilder:: in_ignoreinvolved(CPPType *type) const { switch (type->get_subtype()) { case CPPDeclaration::ST_pointer: { CPPPointerType *ptr = type->as_pointer_type(); return in_ignoreinvolved(ptr->_pointing_at); } case CPPDeclaration::ST_array: { CPPArrayType *ary = type->as_array_type(); return in_ignoreinvolved(ary->_element_type); } case CPPDeclaration::ST_reference: { CPPReferenceType *ref = type->as_reference_type(); return in_ignoreinvolved(ref->_pointing_at); } case CPPDeclaration::ST_const: { CPPConstType *cnst = type->as_const_type(); return in_ignoreinvolved(cnst->_wrapped_around); } case CPPDeclaration::ST_function: { CPPFunctionType *ftype = type->as_function_type(); if (in_ignoreinvolved(ftype->_return_type)) { return true; } const CPPParameterList::Parameters ¶ms = ftype->_parameters->_parameters; CPPParameterList::Parameters::const_iterator pi; for (pi = params.begin(); pi != params.end(); ++pi) { if (in_ignoreinvolved((*pi)->_type)) { return true; } } return false; } default: return in_ignoreinvolved(type->get_simple_name()); } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::in_ignorefile // Access: Private // Description: Returns true if the indicated name is one that the // user identified with an ignorefile command. //////////////////////////////////////////////////////////////////// bool InterrogateBuilder:: in_ignorefile(const string &name) const { return (_ignorefile.count(name) != 0); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::in_ignoremember // Access: Private // Description: Returns true if the indicated name is one that the // user identified with an ignoremember command. //////////////////////////////////////////////////////////////////// bool InterrogateBuilder:: in_ignoremember(const string &name) const { return (_ignoremember.count(name) != 0); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::in_noinclude // Access: Private // Description: Returns true if the indicated filename is one that // the user identified with a noinclude command. //////////////////////////////////////////////////////////////////// bool InterrogateBuilder:: in_noinclude(const string &name) const { return (_noinclude.count(name) != 0); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::should_include // Access: Private // Description: Returns true if the indicated filename is a valid // file to explicitly #include in the generated .cxx // file, false otherwise. //////////////////////////////////////////////////////////////////// bool InterrogateBuilder:: should_include(const string &filename) const { // Don't directly include any .cxx or .I files. if (CPPFile::is_c_or_i_file(filename)) { return false; } // Also, don't include any files specifically forbidden in a .N // file. if (in_noinclude(filename)) { return false; } // Much as I hate to do it, I'm going to code in a special-case // for two particularly nasty header files that we probably don't // want to actually ever include. if (filename == "winbase.h" || filename == "windows.h") { return false; } // Finally, don't include *_src.h or *_src.cxx. These are special // "template" files that should not generally be included directly. if (filename.length() > 6 && filename.substr(filename.length() - 6) == "_src.h") { return false; } if (filename.length() > 8 && filename.substr(filename.length() - 8) == "_src.cxx") { return false; } // Otherwise, no problem. return true; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::remap_indices // Access: Private // Description: Resequences all of the index numbers so that // function wrappers start at 1 and occupy consecutive // positions, and everything else follows. This allows // us to build a table of function wrappers by index // number. // // The "remaps" member is a list of FunctionRemap // pointers. The collision in naming is unfortunate; // the FunctionRemap objects are so named because they // remap synthesized function wrappers to actual C++ // methods and functions. It has nothing to do with the // remapping of index numbers, which is the purpose of // this function. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: remap_indices(vector &remaps) { IndexRemapper index_remap; InterrogateDatabase::get_ptr()->remap_indices(1, index_remap); TypesByName::iterator ti; for (ti = _types_by_name.begin(); ti != _types_by_name.end(); ++ti) { (*ti).second = index_remap.map_from((*ti).second); } FunctionsByName::iterator fi; for (fi = _functions_by_name.begin(); fi != _functions_by_name.end(); ++fi) { (*fi).second = index_remap.map_from((*fi).second); } vector::iterator ri; for (ri = remaps.begin(); ri != remaps.end(); ++ri) { FunctionRemap *remap = (*ri); remap->_wrapper_index = index_remap.map_from(remap->_wrapper_index); } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::scan_function // Access: Private // Description: Adds the indicated global function to the database, // if warranted. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: scan_function(CPPFunctionGroup *fgroup) { CPPFunctionGroup::Instances::const_iterator fi; for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { CPPInstance *function = (*fi); scan_function(function); } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::scan_function // Access: Private // Description: Adds the indicated global function to the database, // if warranted. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: scan_function(CPPInstance *function) { assert(function != (CPPInstance *)NULL); assert(function->_type != (CPPType *)NULL && function->_type->as_function_type() != (CPPFunctionType *)NULL); CPPFunctionType *ftype = function->_type->resolve_type(&parser, &parser)->as_function_type(); assert(ftype != (CPPFunctionType *)NULL); CPPScope *scope = &parser; if (function->is_scoped()) { scope = function->get_scope(&parser, &parser); if (scope == (CPPScope *)NULL) { // Invalid scope. nout << "Invalid scope: " << *function->_ident << "\n"; return; } if (scope->get_struct_type() != (CPPStructType *)NULL) { // Wait, this is a method, not a function. This must be the // declaration for the method (since it's appearing // out-of-scope). We don't need to define a new method for it, // but we'd like to update the comment, if we have a comment. update_method_comment(function, scope->get_struct_type(), scope); return; } } if (function->is_template()) { // The function is a template function, not a true function. return; } if (function->_file.is_c_file()) { // This function declaration appears in a .C file. We can only // export functions whose prototypes appear in an .h file. return; } if (function->_file._source != CPPFile::S_local || in_ignorefile(function->_file._filename_as_referenced)) { // The function is defined in some other package or in an // ignorable file. return; } if (function->_vis > min_vis) { // The function is not marked to be exported. return; } if ((function->_storage_class & CPPInstance::SC_static) != 0) { // The function is static, so can't be exported. return; } if (TypeManager::involves_protected(ftype)) { // We can't export the function because it involves parameter // types that are protected or private. return; } if (in_ignoreinvolved(ftype)) { // The function or its parameters involves something that the // user requested we ignore. return; } get_function(function, "", (CPPStructType *)NULL, scope, InterrogateFunction::F_global); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::scan_struct_type // Access: Private // Description: Adds the indicated struct type to the database, if // warranted. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: scan_struct_type(CPPStructType *type) { if (type == (CPPStructType *)NULL) { return; } if (type->is_template()) { // The type is a template declaration, not a true type. return; } if (type->_file.is_c_file()) { // This type declaration appears in a .C file. We can only export // types defined in a .h file. return; } if (type->_file._source != CPPFile::S_local || in_ignorefile(type->_file._filename_as_referenced)) { // The type is defined in some other package or in an // ignorable file. return; } // Check if any of the members are exported. If none of them are, // and the type itself is not marked for export, then never mind. if (type->_vis > min_vis) { CPPScope *scope = type->_scope; bool any_exported = false; CPPScope::Declarations::const_iterator di; for (di = scope->_declarations.begin(); di != scope->_declarations.end() && !any_exported; ++di) { if ((*di)->_vis <= min_vis) { any_exported = true; } } if (!any_exported) { return; } } get_type(type, true); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::scan_enum_type // Access: Private // Description: Adds the indicated enum type to the database, if // warranted. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: scan_enum_type(CPPEnumType *type) { if (type == (CPPEnumType *)NULL) { return; } if (type->is_template()) { // The type is a template declaration, not a true type. return; } if (type->_file.is_c_file()) { // This type declaration appears in a .C file. We can only export // types defined in a .h file. return; } if (type->_file._source != CPPFile::S_local || in_ignorefile(type->_file._filename_as_referenced)) { // The type is defined in some other package or in an // ignorable file. return; } if (type->_vis > min_vis) { // The type is not marked to be exported. return; } get_type(type, true); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::scan_manifest // Access: Private // Description: Adds the indicated manifest constant to the database, // if warranted. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: scan_manifest(CPPManifest *manifest) { if (manifest == (CPPManifest *)NULL) { return; } if (manifest->_file.is_c_file()) { // This #define appears in a .C file. We can only export // manifests defined in a .h file. return; } if (manifest->_file._source != CPPFile::S_local || in_ignorefile(manifest->_file._filename_as_referenced)) { // The manifest is defined in some other package or in an // ignorable file. return; } if (manifest->_vis > min_vis) { // The manifest is not marked for export. return; } if (manifest->_has_parameters) { // We can't export manifest functions. return; } InterrogateManifest imanifest; imanifest._name = manifest->_name; imanifest._definition = manifest->expand(); CPPType *type = manifest->determine_type(); if (type != (CPPType *)NULL) { imanifest._flags |= InterrogateManifest::F_has_type; imanifest._type = get_type(type, false); CPPExpression *expr = manifest->_expr; CPPExpression::Result result = expr->evaluate(); if (result._type == CPPExpression::RT_integer) { // We have an integer-valued expression. imanifest._flags |= InterrogateManifest::F_has_int_value; imanifest._int_value = result.as_integer(); } else { // We have a more complex expression. Generate a getter // function. FunctionIndex getter = get_getter(type, manifest->_name, (CPPStructType *)NULL, &parser, (CPPInstance *)NULL); if (getter != 0) { imanifest._flags |= InterrogateManifest::F_has_getter; imanifest._getter = getter; } } } ManifestIndex index = InterrogateDatabase::get_ptr()->get_next_index(); InterrogateDatabase::get_ptr()->add_manifest(index, imanifest); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::scan_element // Access: Private // Description: Adds the indicated data element to the database, // if warranted. //////////////////////////////////////////////////////////////////// ElementIndex InterrogateBuilder:: scan_element(CPPInstance *element, CPPStructType *struct_type, CPPScope *scope) { if (element == (CPPInstance *)NULL) { return 0; } if (element->is_template()) { // The element is a template element, not a true element. return 0; } if (element->is_scoped()) { if (element->get_scope(scope, &parser) != scope) { // This is an element defined out-of-scope. It's probably the // definition for a data member. Ignore it. return 0; } } if (element->_file.is_c_file()) { // This element declaration appears in a .C file. We can only // export elements declared in a .h file. return 0; } if (element->_file._source != CPPFile::S_local || in_ignorefile(element->_file._filename_as_referenced)) { // The element is defined in some other package or in an // ignorable file. return 0; } if (element->_vis > min_vis) { // The element is not marked for export. return 0; } if ((element->_storage_class & CPPInstance::SC_static) != 0) { // The element is static, so can't be exported. return 0; } // Make sure the element knows what its scope is. if (element->_ident->_native_scope != scope) { element = new CPPInstance(*element); element->_ident = new CPPIdentifier(*element->_ident); element->_ident->_native_scope = scope; } CPPType *element_type = TypeManager::resolve_type(element->_type, scope); CPPType *parameter_type = element_type; InterrogateElement ielement; ielement._name = element->get_local_name(scope); ielement._scoped_name = descope(element->get_local_name(&parser)); ielement._type = get_type(TypeManager::unwrap_reference(element_type), false); if (ielement._type == 0) { // If we can't understand what type it is, forget it. return 0; } if (!TypeManager::involves_protected(element_type)) { // We can only generate a getter and a setter if we can talk about // the type it is. if (parameter_type->as_struct_type() != (CPPStructType *)NULL) { // Wrap the type in a const reference. parameter_type = TypeManager::wrap_const_reference(parameter_type); } // Generate a getter and setter function for the element. FunctionIndex getter = get_getter(parameter_type, element->get_local_name(scope), struct_type, scope, element); if (getter != 0) { ielement._flags |= InterrogateElement::F_has_getter; ielement._getter = getter; } if (TypeManager::is_assignable(element_type)) { FunctionIndex setter = get_setter(parameter_type, element->get_local_name(scope), struct_type, scope, element); if (setter != 0) { ielement._flags |= InterrogateElement::F_has_setter; ielement._setter = setter; } } } if (struct_type == (CPPStructType *)NULL) { // This is a global data element: not a data member. ielement._flags |= InterrogateElement::F_global; } ElementIndex index = InterrogateDatabase::get_ptr()->get_next_index(); InterrogateDatabase::get_ptr()->add_element(index, ielement); return index; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::get_getter // Access: Private // Description: Adds a function to return the value for the indicated // expression. Returns the new function index. //////////////////////////////////////////////////////////////////// FunctionIndex InterrogateBuilder:: get_getter(CPPType *expr_type, string expression, CPPStructType *struct_type, CPPScope *scope, CPPInstance *element) { // Make up a name for the function. string fname = clean_identifier("get_" + expression); // Unroll the "const" from the expr_type, since that doesn't matter // for a return type. while (expr_type->as_const_type() != (CPPConstType *)NULL) { expr_type = expr_type->as_const_type()->_wrapped_around; assert(expr_type != (CPPType *)NULL); } // Make up a CPPFunctionType. CPPParameterList *params = new CPPParameterList; CPPFunctionType *ftype = new CPPFunctionType(expr_type, params, 0); // Now make up an instance for the function. CPPInstance *function = new CPPInstance(ftype, fname); function->_ident->_native_scope = scope; int getter_flags = InterrogateFunction::F_getter; if (struct_type != (CPPStructType *)NULL) { // This is a data member for some class. assert(element != (CPPInstance *)NULL); assert(scope != (CPPScope *)NULL); if ((element->_storage_class & CPPInstance::SC_static) != 0) { // This is a static data member; therefore, the synthesized // getter is also static. function->_storage_class |= CPPInstance::SC_static; // And the expression is fully scoped. expression = element->get_local_name(&parser); } else { // This is a non-static data member, so it has a const // synthesized getter method. ftype->_flags |= CPPFunctionType::F_const_method; // And the expression is locally scoped. expression = element->get_local_name(scope); getter_flags |= InterrogateFunction::F_method; } } // Now check to see if there's already a function matching this // name. If there is, we can't define a getter, and we // shouldn't mistake this other function for a synthesized getter. string function_name = TypeManager::get_function_name(function); if (_functions_by_name.count(function_name) != 0) { return 0; } ostringstream desc; desc << "getter for "; if (element != (CPPInstance *)NULL) { element->_initializer = (CPPExpression *)NULL; element->output(desc, 0, &parser, false); desc << ";"; } else { desc << expression; } string description = desc.str(); // It's clear; make a getter. FunctionIndex index = get_function(function, description, struct_type, scope, getter_flags, expression); InterrogateDatabase::get_ptr()->update_function(index)._comment = description; return index; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::get_setter // Access: Private // Description: Adds a function to return the value for the indicated // expression. Returns the new function index. //////////////////////////////////////////////////////////////////// FunctionIndex InterrogateBuilder:: get_setter(CPPType *expr_type, string expression, CPPStructType *struct_type, CPPScope *scope, CPPInstance *element) { // Make up a name for the function. string fname = clean_identifier("set_" + expression); // Make up a CPPFunctionType. CPPParameterList *params = new CPPParameterList; CPPInstance *param0 = new CPPInstance(expr_type, "value"); params->_parameters.push_back(param0); CPPType *void_type = TypeManager::get_void_type(); CPPFunctionType *ftype = new CPPFunctionType(void_type, params, 0); // Now make up an instance for the function. CPPInstance *function = new CPPInstance(ftype, fname); function->_ident->_native_scope = scope; int setter_flags = InterrogateFunction::F_setter; if (struct_type != (CPPStructType *)NULL) { // This is a data member for some class. assert(element != (CPPInstance *)NULL); assert(scope != (CPPScope *)NULL); if ((element->_storage_class & CPPInstance::SC_static) != 0) { // This is a static data member; therefore, the synthesized // setter is also static. function->_storage_class |= CPPInstance::SC_static; // And the expression is fully scoped. expression = element->get_local_name(&parser); } else { // This is a non-static data member. The expression is locally // scoped. expression = element->get_local_name(scope); setter_flags |= InterrogateFunction::F_method; } } // Now check to see if there's already a function matching this // name. If there is, we can't define a setter, and we // shouldn't mistake this other function for a synthesized setter. string function_name = TypeManager::get_function_name(function); if (_functions_by_name.count(function_name) != 0) { return 0; } ostringstream desc; desc << "setter for "; if (element != (CPPInstance *)NULL) { element->_initializer = (CPPExpression *)NULL; element->output(desc, 0, &parser, false); desc << ";"; } else { desc << expression; } string description = desc.str(); // It's clear; make a setter. FunctionIndex index = get_function(function, description, struct_type, scope, setter_flags, expression); InterrogateDatabase::get_ptr()->update_function(index)._comment = description; return index; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::get_cast_function // Access: Private // Description: Adds a function to cast from a pointer of the // indicated type to a pointer of the indicated type to // the database. Returns the new function index. //////////////////////////////////////////////////////////////////// FunctionIndex InterrogateBuilder:: get_cast_function(CPPType *to_type, CPPType *from_type, const string &prefix) { CPPInstance *function; CPPStructType *struct_type = from_type->as_struct_type(); CPPScope *scope = &parser; if (struct_type != (CPPStructType *)NULL) { // We'll make this a method of the from type. scope = struct_type->get_scope(); // Make up a name for the method. string fname = clean_identifier(prefix + "_to_" + get_preferred_name(to_type)); // Make up a CPPFunctionType. CPPType *to_ptr_type = CPPType::new_type(new CPPPointerType(to_type)); CPPParameterList *params = new CPPParameterList; CPPFunctionType *ftype = new CPPFunctionType(to_ptr_type, params, 0); // Now make up an instance for the function. function = new CPPInstance(ftype, fname); } else { // The from type isn't a struct or a class, so this has to be an // external function. // Make up a name for the function. string fname = clean_identifier(prefix + "_" + get_preferred_name(from_type) + "_to_" + get_preferred_name(to_type)); // Make up a CPPFunctionType. CPPType *from_ptr_type = CPPType::new_type(new CPPPointerType(from_type)); CPPType *to_ptr_type = CPPType::new_type(new CPPPointerType(to_type)); CPPInstance *param0 = new CPPInstance(from_ptr_type, "this"); CPPParameterList *params = new CPPParameterList; params->_parameters.push_back(param0); CPPFunctionType *ftype = new CPPFunctionType(to_ptr_type, params, 0); // Now make up an instance for the function. function = new CPPInstance(ftype, fname); } ostringstream desc; desc << prefix << " from " << *from_type << " to " << *to_type; string description = desc.str(); FunctionIndex index = get_function(function, description, struct_type, scope, InterrogateFunction::F_typecast); InterrogateDatabase::get_ptr()->update_function(index)._comment = description; return index; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::get_function // Access: Private // Description: Adds the indicated function to the database, if it is // not already present. In either case, returns the // FunctionIndex of the function within the database. //////////////////////////////////////////////////////////////////// FunctionIndex InterrogateBuilder:: get_function(CPPInstance *function, string description, CPPStructType *struct_type, CPPScope *scope, int flags, const string &expression) { // Get a unique function signature. Make sure we tell the function // where its native scope is, so we get a fully-scoped signature. if (function->_ident->_native_scope != scope) { function = new CPPInstance(*function); function->_ident = new CPPIdentifier(*function->_ident); function->_ident->_native_scope = scope; } CPPFunctionType *ftype = function->_type->resolve_type(scope, &parser)->as_function_type(); function->_type = ftype; if ((ftype->_flags & CPPFunctionType::F_constructor) && struct_type != (CPPStructType *)NULL && struct_type->is_abstract()) { // This is a constructor for an abstract class; forget it. return 0; } string function_name = TypeManager::get_function_name(function); string function_signature = TypeManager::get_function_signature(function); // First, check to see if it's already there. FunctionsByName::const_iterator tni = _functions_by_name.find(function_name); if (tni != _functions_by_name.end()) { FunctionIndex index = (*tni).second; // It's already here, so update the flags. InterrogateFunction &ifunction = InterrogateDatabase::get_ptr()->update_function(index); ifunction._flags |= flags; // And/or the comment. if (function->_leading_comment != (CPPCommentBlock *)NULL) { string comment = trim_blanks(function->_leading_comment->_comment); if (!ifunction._comment.empty()) { ifunction._comment += "\n\n"; } ifunction._comment += comment; } // Also, make sure this particular signature is defined. ifunction._instances->insert(InterrogateFunction::Instances::value_type(function_signature, function)); return index; } // It isn't here, so we'll have to define it. FunctionIndex index = InterrogateDatabase::get_ptr()->get_next_index(); _functions_by_name[function_name] = index; InterrogateFunction *ifunction = new InterrogateFunction; ifunction->_name = function->get_local_name(scope); ifunction->_scoped_name = descope(function->get_local_name(&parser)); ifunction->_instances = new InterrogateFunction::Instances; if (function->_leading_comment != (CPPCommentBlock *)NULL) { ifunction->_comment = trim_blanks(function->_leading_comment->_comment); } ostringstream prototype; function->output(prototype, 0, &parser, false); prototype << ";"; ifunction->_prototype = prototype.str(); if (struct_type != (CPPStructType *)NULL) { // The function is a method. ifunction->_flags |= InterrogateFunction::F_method; ifunction->_class = get_type(struct_type, false); } ifunction->_flags |= flags; ifunction->_instances->insert(InterrogateFunction::Instances::value_type(function_signature, function)); ifunction->_expression = expression; InterrogateDatabase::get_ptr()->add_function(index, ifunction); return index; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::get_atomic_string_type // Access: Private // Description: Returns a TypeIndex for the "atomic string" type, // which is a bogus type that might be used if -string // is passed to interrogate. It means to translate // basic_string and char * to whatever atomic // string type is native to the particular the scripting // language we happen to be generating wrappers for. //////////////////////////////////////////////////////////////////// TypeIndex InterrogateBuilder:: get_atomic_string_type() { // Make up a true name that can't possibly clash with an actual C++ // type name. string true_name = "atomic string"; TypesByName::const_iterator tni = _types_by_name.find(true_name); if (tni != _types_by_name.end()) { return (*tni).second; } // This is the first time the atomic string has been requested; // define it now. TypeIndex index = InterrogateDatabase::get_ptr()->get_next_index(); _types_by_name[true_name] = index; InterrogateType itype; itype._flags |= InterrogateType::F_atomic; itype._atomic_token = AT_string; itype._true_name = true_name; itype._scoped_name = true_name; itype._name = true_name; InterrogateDatabase::get_ptr()->add_type(index, itype); return index; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::get_type // Access: Private // Description: Adds the indicated type to the database, if it is not // already present. In either case, returns the // TypeIndex of the type within the database. //////////////////////////////////////////////////////////////////// TypeIndex InterrogateBuilder:: get_type(CPPType *type, bool global) { if (type->is_template()) { // Can't do anything with a template type. return 0; } TypeIndex index = 0; // First, check to see if it's already there. string true_name = type->get_local_name(&parser); TypesByName::const_iterator tni = _types_by_name.find(true_name); if (tni != _types_by_name.end()) { // It's already here, so update the global flag. index = (*tni).second; if (index == 0) { // This is an invalid type; we don't know anything about it. return 0; } InterrogateType &itype = InterrogateDatabase::get_ptr()->update_type(index); if (global) { itype._flags |= InterrogateType::F_global; } if ((itype._flags & InterrogateType::F_fully_defined) != 0) { return index; } // But wait--it's not fully defined yet! We'll go ahead and // define it now. } bool forced = in_forcetype(true_name); if (index == 0) { // It isn't already there, so we have to define it. index = InterrogateDatabase::get_ptr()->get_next_index(); _types_by_name[true_name] = index; InterrogateType itype; if (global) { itype._flags |= InterrogateType::F_global; } InterrogateDatabase::get_ptr()->add_type(index, itype); } InterrogateType &itype = InterrogateDatabase::get_ptr()->update_type(index); itype._name = get_preferred_name(type); itype._scoped_name = true_name; itype._true_name = true_name; itype._cpptype = type; if (type->_declaration != (CPPTypeDeclaration *)NULL) { // This type has a declaration; does the declaration have a // comment? CPPTypeDeclaration *decl = type->_declaration; if (decl->_leading_comment != (CPPCommentBlock *)NULL) { itype._comment = trim_blanks(decl->_leading_comment->_comment); } } CPPExtensionType *ext_type = type->as_extension_type(); if (ext_type != (CPPExtensionType *)NULL) { // If it's an extension type of some kind, it might be scoped. if (ext_type->_ident != (CPPIdentifier *)NULL) { CPPScope *scope = ext_type->_ident->get_scope(&parser, &parser); while (scope->as_template_scope() != (CPPTemplateScope *)NULL) { assert(scope->get_parent_scope() != scope); scope = scope->get_parent_scope(); assert(scope != (CPPScope *)NULL); } itype._cppscope = scope; if (scope != &parser) { // We're scoped! itype._scoped_name = descope(scope->get_local_name(&parser) + "::" + itype._name); CPPStructType *struct_type = scope->get_struct_type(); if (struct_type != (CPPStructType *)NULL) { itype._flags |= InterrogateType::F_nested; itype._outer_class = get_type(struct_type, false); } } } } if (forced || !in_ignoretype(true_name)) { itype._flags |= InterrogateType::F_fully_defined; if (type->as_simple_type() != (CPPSimpleType *)NULL) { define_atomic_type(itype, type->as_simple_type()); } else if (type->as_pointer_type() != (CPPPointerType *)NULL) { define_wrapped_type(itype, type->as_pointer_type()); } else if (type->as_const_type() != (CPPConstType *)NULL) { define_wrapped_type(itype, type->as_const_type()); } else if (type->as_struct_type() != (CPPStructType *)NULL) { define_struct_type(itype, type->as_struct_type(), index, forced); } else if (type->as_enum_type() != (CPPEnumType *)NULL) { define_enum_type(itype, type->as_enum_type()); } else if (type->as_extension_type() != (CPPExtensionType *)NULL) { define_extension_type(itype, type->as_extension_type()); } else { // nout << "Attempt to define invalid type " << true_name << "\n"; // Remove the type from the database. InterrogateDatabase::get_ptr()->remove_type(index); _types_by_name[true_name] = 0; index = 0; } } return index; } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::define_atomic_type // Access: Private // Description: Builds up a definition for the indicated atomic type. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: define_atomic_type(InterrogateType &itype, CPPSimpleType *cpptype) { itype._flags |= InterrogateType::F_atomic; switch (cpptype->_type) { case CPPSimpleType::T_bool: itype._atomic_token = AT_bool; break; case CPPSimpleType::T_char: itype._atomic_token = AT_char; break; case CPPSimpleType::T_int: if ((cpptype->_flags & CPPSimpleType::F_longlong) != 0) { itype._atomic_token = AT_longlong; } else { itype._atomic_token = AT_int; } break; case CPPSimpleType::T_float: itype._atomic_token = AT_float; break; case CPPSimpleType::T_double: itype._atomic_token = AT_double; break; case CPPSimpleType::T_void: itype._atomic_token = AT_void; break; default: nout << "Invalid CPPSimpleType: " << (int)cpptype->_type << "\n"; itype._atomic_token = AT_not_atomic; } if ((cpptype->_flags & CPPSimpleType::F_longlong) != 0) { itype._flags |= InterrogateType::F_longlong; } else if ((cpptype->_flags & CPPSimpleType::F_long) != 0) { itype._flags |= InterrogateType::F_long; } if ((cpptype->_flags & CPPSimpleType::F_short) != 0) { itype._flags |= InterrogateType::F_short; } if ((cpptype->_flags & CPPSimpleType::F_unsigned) != 0) { itype._flags |= InterrogateType::F_unsigned; } if ((cpptype->_flags & CPPSimpleType::F_signed) != 0) { itype._flags |= InterrogateType::F_signed; } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::define_wrapped_type // Access: Private // Description: Builds up a definition for the indicated wrapped type. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: define_wrapped_type(InterrogateType &itype, CPPPointerType *cpptype) { itype._flags |= (InterrogateType::F_wrapped | InterrogateType::F_pointer); itype._wrapped_type = get_type(cpptype->_pointing_at, false); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::define_wrapped_type // Access: Private // Description: Builds up a definition for the indicated wrapped type. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: define_wrapped_type(InterrogateType &itype, CPPConstType *cpptype) { itype._flags |= (InterrogateType::F_wrapped | InterrogateType::F_const); itype._wrapped_type = get_type(cpptype->_wrapped_around, false); } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::define_struct_type // Access: Private // Description: Builds up a definition for the indicated struct type. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: define_struct_type(InterrogateType &itype, CPPStructType *cpptype, TypeIndex type_index, bool forced) { if (cpptype->get_simple_name().empty()) { // If the type has no name, forget it. We don't export anonymous // types. return; } cpptype = TypeManager::resolve_type(cpptype)->as_struct_type(); assert(cpptype != (CPPStructType *)NULL); bool has_virt_methods = cpptype->check_virtual(); switch (cpptype->_type) { case CPPExtensionType::T_class: itype._flags |= InterrogateType::F_class; break; case CPPExtensionType::T_struct: itype._flags |= InterrogateType::F_struct; break; case CPPExtensionType::T_union: itype._flags |= InterrogateType::F_union; break; default: break; } if (cpptype->_file.is_c_file()) { // This type declaration appears in a .C file. We can only export // types defined in a .h file. return; } if (!forced && (cpptype->_file._source != CPPFile::S_local || in_ignorefile(cpptype->_file._filename_as_referenced))) { // The struct type is defined in some other package or in an // ignorable file, so don't try to output it. // This means we also don't gather any information about its // derivations or determine if an implicit destructor is // necessary. However, this is not important, and it causes // problems if we do (how many implicit destructors do we need, // anyway?). itype._flags &= ~InterrogateType::F_fully_defined; return; } // Make sure the class declaration within its parent scope isn't // private or protected. If it is, we can't export any of its // members. if (TypeManager::involves_unpublished(cpptype)) { itype._flags &= ~InterrogateType::F_fully_defined; itype._flags |= InterrogateType::F_unpublished; return; } if (TypeManager::involves_protected(cpptype)) { itype._flags &= ~InterrogateType::F_fully_defined; return; } // A struct type should always be global. itype._flags |= InterrogateType::F_global; CPPScope *scope = cpptype->_scope; CPPStructType::Derivation::const_iterator bi; for (bi = cpptype->_derivation.begin(); bi != cpptype->_derivation.end(); ++bi) { const CPPStructType::Base &base = (*bi); if (base._vis <= V_public) { CPPType *base_type = TypeManager::resolve_type(base._base, scope); TypeIndex base_index = get_type(base_type, true); if (base_index == 0) { nout << *cpptype << " reports a derivation from an invalid type.\n"; } else { InterrogateType::Derivation d; d._flags = 0; d._base = base_index; d._upcast = 0; d._downcast = 0; // Do we need to synthesize upcast and downcast functions? bool generate_casts = false; if (base._is_virtual) { // We do in the presence of virtual inheritance. generate_casts = true; } else if (bi != cpptype->_derivation.begin()) { // Or if we're not talking about the leftmost fork of multiple // inheritance. generate_casts = true; } else if (cpptype->_derivation.size() != 1 && left_inheritance_requires_upcast) { // Or even if we are the leftmost fork of multiple // inheritance, if the flag is set indicating that this // requires a pointer change. (For many compilers, this does // not require a pointer change.) generate_casts = true; } else if (has_virt_methods && (base_type->as_struct_type() == (CPPStructType *)NULL || !base_type->as_struct_type()->check_virtual())) { // Finally, if this class has virtual methods, but its // parent doesn't, then we have to upcast (because this // class will require space for a virtual function table // pointer, while the parent class won't). generate_casts = true; } if (generate_casts) { d._upcast = get_cast_function(base_type, cpptype, "upcast"); d._flags |= InterrogateType::DF_upcast; if (base._is_virtual) { // If this is a virtual inheritance, we can't write a // downcast. d._flags |= InterrogateType::DF_downcast_impossible; } else { d._downcast = get_cast_function(cpptype, base_type, "downcast"); d._flags |= InterrogateType::DF_downcast; } } itype._derivations.push_back(d); } } } CPPScope::Declarations::const_iterator di; for (di = scope->_declarations.begin(); di != scope->_declarations.end(); ++di) { if ((*di)->get_subtype() == CPPDeclaration::ST_instance) { CPPInstance *inst = (*di)->as_instance(); if (inst->_type->get_subtype() == CPPDeclaration::ST_function) { // Here's a method declaration. define_method(inst, itype, cpptype, scope); } else { // Here's a data member declaration. ElementIndex data_member = scan_element(inst, cpptype, scope); if (data_member != 0) { itype._elements.push_back(data_member); } } } else if ((*di)->get_subtype() == CPPDeclaration::ST_type_declaration) { CPPType *type = (*di)->as_type_declaration()->_type; if ((*di)->_vis <= min_vis) { if (type->as_struct_type() != (CPPStructType *)NULL || type->as_enum_type() != (CPPEnumType *)NULL) { // Here's a nested class or enum definition. type->_vis = (*di)->_vis; CPPExtensionType *nested_type = type->as_extension_type(); assert(nested_type != (CPPExtensionType *)NULL); // Only try to export named types. if (nested_type->_ident != (CPPIdentifier *)NULL) { TypeIndex nested_index = get_type(nested_type, false); itype._nested_types.push_back(nested_index); } } } } } if ((itype._flags & InterrogateType::F_inherited_destructor) != 0) { // If we have inherited our virtual destructor from our base // class, go ahead and assign the same function index. assert(!itype._derivations.empty()); TypeIndex base_type_index = itype._derivations.front()._base; InterrogateType &base_type = InterrogateDatabase::get_ptr()-> update_type(base_type_index); itype._destructor = base_type._destructor; } else if ((itype._flags & (InterrogateType::F_true_destructor | InterrogateType::F_private_destructor | InterrogateType::F_inherited_destructor | InterrogateType::F_implicit_destructor)) == 0) { // If we didn't get a destructor at all, we should make a wrapper // for one anyway. string function_name = "~" + cpptype->get_simple_name(); // Make up a CPPFunctionType. CPPType *void_type = TypeManager::get_void_type(); CPPParameterList *params = new CPPParameterList; CPPFunctionType *ftype = new CPPFunctionType(void_type, params, 0); ftype->_flags |= CPPFunctionType::F_destructor; // Now make up an instance for the destructor. CPPInstance *function = new CPPInstance(ftype, function_name); itype._destructor = get_function(function, "", cpptype, cpptype->get_scope(), 0); itype._flags |= InterrogateType::F_implicit_destructor; } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::update_method_comment // Access: Private // Description: Updates the method definition in the database to // include whatever comment is associated with this // declaration. This is called when we encounted a // method definition outside of the class; the only new // information this might include for us is the method // comment. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: update_method_comment(CPPInstance *function, CPPStructType *struct_type, CPPScope *scope) { if (function->_leading_comment == (CPPCommentBlock *)NULL) { // No comment anyway. Forget it. return; } // Get a function name so we can look this method up. if (function->_ident->_native_scope != scope) { function = new CPPInstance(*function); function->_ident = new CPPIdentifier(*function->_ident); function->_ident->_native_scope = scope; } CPPFunctionType *ftype = function->_type->resolve_type(scope, &parser)->as_function_type(); function->_type = ftype; string function_name = TypeManager::get_function_name(function); // Now look it up. FunctionsByName::const_iterator tni = _functions_by_name.find(function_name); if (tni != _functions_by_name.end()) { FunctionIndex index = (*tni).second; // Here it is! InterrogateFunction &ifunction = InterrogateDatabase::get_ptr()->update_function(index); // Update the comment. string comment = trim_blanks(function->_leading_comment->_comment); if (!ifunction._comment.empty()) { ifunction._comment += "\n\n"; } ifunction._comment += comment; /* if (comment.length() > ifunction._comment.length()) { ifunction._comment = comment; } */ } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::define_method // Access: Private // Description: Adds the indicated member function to the struct type, //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: define_method(CPPFunctionGroup *fgroup, InterrogateType &itype, CPPStructType *struct_type, CPPScope *scope) { CPPFunctionGroup::Instances::const_iterator fi; for (fi = fgroup->_instances.begin(); fi != fgroup->_instances.end(); ++fi) { CPPInstance *function = (*fi); define_method(function, itype, struct_type, scope); } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::define_method // Access: Private // Description: Adds the indicated member function to the struct type, //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: define_method(CPPInstance *function, InterrogateType &itype, CPPStructType *struct_type, CPPScope *scope) { assert(function != (CPPInstance *)NULL); assert(function->_type != (CPPType *)NULL && function->_type->as_function_type() != (CPPFunctionType *)NULL); CPPFunctionType *ftype = function->_type->resolve_type(scope, &parser)->as_function_type(); if (function->is_template()) { // The function is a template function, not a true function. return; } // As a special kludgey extension, we consider a public static // method called "get_class_type()" to be marked published, even if // it is not. This allows us to export all of the TypeHandle system // stuff without having to specifically flag get_class_type() as // published. bool force_publish = false; if (function->get_simple_name() == "get_class_type" && (function->_storage_class && CPPInstance::SC_static) != 0 && function->_vis <= V_public) { force_publish = true; } if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { // A destructor is a special case. If it's public, we export it // (even if it's not published), but if it's protected or private, // we don't exported it, and we flag it so we don't try to // synthesize one later. if (function->_vis > V_public) { itype._flags |= InterrogateType::F_private_destructor; return; } force_publish = true; } if (!force_publish && function->_vis > min_vis) { // The function is not marked to be exported. return; } if (TypeManager::involves_protected(ftype)) { // We can't export the function because it involves parameter // types that are protected or private. return; } if (in_ignoreinvolved(ftype)) { // The function or its parameters involves something that the // user requested we ignore. if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { itype._flags |= InterrogateType::F_private_destructor; } return; } if (in_ignoremember(function->get_simple_name())) { // The user requested us to ignore members of this name. if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { itype._flags |= InterrogateType::F_private_destructor; } return; } if ((function->_storage_class & CPPInstance::SC_inherited_virtual) != 0 && struct_type->_derivation.size() == 1 && struct_type->_derivation[0]._vis <= V_public && !struct_type->_derivation[0]._is_virtual) { // If this function is a virtual function whose first appearance // is in some base class, we don't need to repeat its definition // here, since we're already inheriting it properly. However, we // may need to make an exception in the presence of multiple // inheritance. if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { itype._flags |= InterrogateType::F_inherited_destructor; } return; } FunctionIndex index = get_function(function, "", struct_type, scope, 0); if (index != 0) { if ((ftype->_flags & CPPFunctionType::F_constructor) != 0) { if (find(itype._constructors.begin(), itype._constructors.end(), index) == itype._constructors.end()) { itype._constructors.push_back(index); } } else if ((ftype->_flags & CPPFunctionType::F_destructor) != 0) { itype._flags |= InterrogateType::F_true_destructor; itype._destructor = index; } else if ((ftype->_flags & CPPFunctionType::F_operator_typecast) != 0) { if (find(itype._casts.begin(), itype._casts.end(), index) == itype._casts.end()) { itype._casts.push_back(index); } } else { if (find(itype._methods.begin(), itype._methods.end(), index) == itype._methods.end()) { itype._methods.push_back(index); } } } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::define_enum_type // Access: Private // Description: Builds up a definition for the indicated enum type. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: define_enum_type(InterrogateType &itype, CPPEnumType *cpptype) { itype._flags |= InterrogateType::F_enum; CPPScope *scope = &parser; if (cpptype->_ident != (CPPIdentifier *)NULL) { scope = cpptype->_ident->get_scope(&parser, &parser); } // Make sure the enum declaration within its parent scope isn't // private or protected. If it is, we can't export any of its // members. if (TypeManager::involves_unpublished(cpptype)) { itype._flags &= ~InterrogateType::F_fully_defined; itype._flags |= InterrogateType::F_unpublished; return; } int next_value = 0; CPPEnumType::Elements::const_iterator ei; for (ei = cpptype->_elements.begin(); ei != cpptype->_elements.end(); ++ei) { CPPInstance *element = (*ei); // Tell the enum element where its native scope is, so we can get // a properly scoped name. if (element->_ident->_native_scope != scope) { element = new CPPInstance(*element); element->_ident = new CPPIdentifier(*element->_ident); element->_ident->_native_scope = scope; } InterrogateType::EnumValue evalue; evalue._name = element->get_simple_name(); evalue._scoped_name = descope(element->get_local_name(&parser)); if (element->_initializer != (CPPExpression *)NULL) { CPPExpression::Result result = element->_initializer->evaluate(); next_value = result.as_integer(); } evalue._value = next_value; itype._enum_values.push_back(evalue); next_value++; } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::define_extension_type // Access: Private // Description: Builds up a definition for the indicated extension type. //////////////////////////////////////////////////////////////////// void InterrogateBuilder:: define_extension_type(InterrogateType &itype, CPPExtensionType *cpptype) { // An "extension type" as returned by CPPParser is really a forward // reference to an undefined struct or class type. itype._flags &= ~InterrogateType::F_fully_defined; // But we can at least indicate which of the various extension types // it is. switch (cpptype->_type) { case CPPExtensionType::T_enum: itype._flags |= InterrogateType::F_enum; break; case CPPExtensionType::T_class: itype._flags |= InterrogateType::F_class; break; case CPPExtensionType::T_struct: itype._flags |= InterrogateType::F_struct; break; case CPPExtensionType::T_union: itype._flags |= InterrogateType::F_union; break; } } //////////////////////////////////////////////////////////////////// // Function: InterrogateBuilder::trim_blanks // Access: Private, Static // Description: //////////////////////////////////////////////////////////////////// string InterrogateBuilder:: trim_blanks(const string &str) { size_t start = 0; while (start < str.length() && isspace(str[start])) { start++; } size_t end = str.length(); while (end > start && isspace(str[end - 1])) { end--; } return str.substr(start, end - start); }