#include "scriptbuilder.h" #include #include using namespace std; #include #if defined(_MSC_VER) && !defined(_WIN32_WCE) && !defined(__S3E__) #include #endif #ifdef _WIN32_WCE #include // For GetModuleFileName() #endif #if defined(__S3E__) || defined(__APPLE__) || defined(__GNUC__) #include // For getcwd() #endif BEGIN_AS_NAMESPACE // Helper functions static string GetCurrentDir(); static string GetAbsolutePath(const string &path); CScriptBuilder::CScriptBuilder() { engine = 0; module = 0; includeCallback = 0; includeParam = 0; pragmaCallback = 0; pragmaParam = 0; } void CScriptBuilder::SetIncludeCallback(INCLUDECALLBACK_t callback, void *userParam) { includeCallback = callback; includeParam = userParam; } void CScriptBuilder::SetPragmaCallback(PRAGMACALLBACK_t callback, void *userParam) { pragmaCallback = callback; pragmaParam = userParam; } int CScriptBuilder::StartNewModule(asIScriptEngine *inEngine, const char *moduleName) { if(inEngine == 0 ) return -1; engine = inEngine; module = inEngine->GetModule(moduleName, asGM_ALWAYS_CREATE); if( module == 0 ) return -1; ClearAll(); return 0; } asIScriptEngine *CScriptBuilder::GetEngine() { return engine; } asIScriptModule *CScriptBuilder::GetModule() { return module; } unsigned int CScriptBuilder::GetSectionCount() const { return (unsigned int)(includedScripts.size()); } string CScriptBuilder::GetSectionName(unsigned int idx) const { if( idx >= includedScripts.size() ) return ""; #ifdef _WIN32 set::const_iterator it = includedScripts.begin(); #else set::const_iterator it = includedScripts.begin(); #endif while( idx-- > 0 ) it++; return *it; } // Returns 1 if the section was included // Returns 0 if the section was not included because it had already been included before // Returns <0 if there was an error int CScriptBuilder::AddSectionFromFile(const char *filename) { // The file name stored in the set should be the fully resolved name because // it is possible to name the same file in multiple ways using relative paths. string fullpath = GetAbsolutePath(filename); if( IncludeIfNotAlreadyIncluded(fullpath.c_str()) ) { int r = LoadScriptSection(fullpath.c_str()); if( r < 0 ) return r; else return 1; } return 0; } // Returns 1 if the section was included // Returns 0 if the section was not included because it had already been included before // Returns <0 if there was an error int CScriptBuilder::AddSectionFromMemory(const char *sectionName, const char *scriptCode, unsigned int scriptLength, int lineOffset) { if( IncludeIfNotAlreadyIncluded(sectionName) ) { int r = ProcessScriptSection(scriptCode, scriptLength, sectionName, lineOffset); if( r < 0 ) return r; else return 1; } return 0; } int CScriptBuilder::BuildModule() { return Build(); } void CScriptBuilder::DefineWord(const char *word) { string sword = word; if( definedWords.find(sword) == definedWords.end() ) { definedWords.insert(sword); } } void CScriptBuilder::ClearAll() { includedScripts.clear(); #if AS_PROCESS_METADATA == 1 currentClass = ""; currentNamespace = ""; foundDeclarations.clear(); typeMetadataMap.clear(); funcMetadataMap.clear(); varMetadataMap.clear(); #endif } bool CScriptBuilder::IncludeIfNotAlreadyIncluded(const char *filename) { string scriptFile = filename; if( includedScripts.find(scriptFile) != includedScripts.end() ) { // Already included return false; } // Add the file to the set of included sections includedScripts.insert(scriptFile); return true; } int CScriptBuilder::LoadScriptSection(const char *filename) { // Open the script file string scriptFile = filename; #if _MSC_VER >= 1500 && !defined(__S3E__) FILE *f = 0; fopen_s(&f, scriptFile.c_str(), "rb"); #else FILE *f = fopen(scriptFile.c_str(), "rb"); #endif if( f == 0 ) { // Write a message to the engine's message callback string msg = "Failed to open script file '" + GetAbsolutePath(scriptFile) + "'"; engine->WriteMessage(filename, 0, 0, asMSGTYPE_ERROR, msg.c_str()); // TODO: Write the file where this one was included from return -1; } // Determine size of the file fseek(f, 0, SEEK_END); int len = ftell(f); fseek(f, 0, SEEK_SET); // On Win32 it is possible to do the following instead // int len = _filelength(_fileno(f)); // Read the entire file string code; size_t c = 0; if( len > 0 ) { code.resize(len); c = fread(&code[0], len, 1, f); } fclose(f); if( c == 0 && len > 0 ) { // Write a message to the engine's message callback string msg = "Failed to load script file '" + GetAbsolutePath(scriptFile) + "'"; engine->WriteMessage(filename, 0, 0, asMSGTYPE_ERROR, msg.c_str()); return -1; } // Process the script section even if it is zero length so that the name is registered return ProcessScriptSection(code.c_str(), (unsigned int)(code.length()), filename, 0); } int CScriptBuilder::ProcessScriptSection(const char *script, unsigned int length, const char *sectionname, int lineOffset) { vector includes; // Perform a superficial parsing of the script first to store the metadata if( length ) modifiedScript.assign(script, length); else modifiedScript = script; // First perform the checks for #if directives to exclude code that shouldn't be compiled unsigned int pos = 0; int nested = 0; while( pos < modifiedScript.size() ) { asUINT len = 0; asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); if( t == asTC_UNKNOWN && modifiedScript[pos] == '#' && (pos + 1 < modifiedScript.size()) ) { int start = pos++; // Is this an #if directive? t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); string token; token.assign(&modifiedScript[pos], len); pos += len; if( token == "if" ) { t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); if( t == asTC_WHITESPACE ) { pos += len; t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); } if( t == asTC_IDENTIFIER ) { string word; word.assign(&modifiedScript[pos], len); // Overwrite the #if directive with space characters to avoid compiler error pos += len; OverwriteCode(start, pos-start); // Has this identifier been defined by the application or not? if( definedWords.find(word) == definedWords.end() ) { // Exclude all the code until and including the #endif pos = ExcludeCode(pos); } else { nested++; } } } else if( token == "endif" ) { // Only remove the #endif if there was a matching #if if( nested > 0 ) { OverwriteCode(start, pos-start); nested--; } } } else pos += len; } #if AS_PROCESS_METADATA == 1 // Preallocate memory string name, declaration; vector metadata; declaration.reserve(100); #endif // Then check for meta data and #include directives pos = 0; while( pos < modifiedScript.size() ) { asUINT len = 0; asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); if( t == asTC_COMMENT || t == asTC_WHITESPACE ) { pos += len; continue; } #if AS_PROCESS_METADATA == 1 // Check if class if( currentClass == "" && modifiedScript.substr(pos,len) == "class" ) { // Get the identifier after "class" do { pos += len; if( pos >= modifiedScript.size() ) { t = asTC_UNKNOWN; break; } t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); } while(t == asTC_COMMENT || t == asTC_WHITESPACE); if( t == asTC_IDENTIFIER ) { currentClass = modifiedScript.substr(pos,len); // Search until first { or ; is encountered while( pos < modifiedScript.length() ) { engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); // If start of class section encountered stop if( modifiedScript[pos] == '{' ) { pos += len; break; } else if (modifiedScript[pos] == ';') { // The class declaration has ended and there are no children currentClass = ""; pos += len; break; } // Check next symbol pos += len; } } continue; } // Check if end of class if( currentClass != "" && modifiedScript[pos] == '}' ) { currentClass = ""; pos += len; continue; } // Check if namespace if( modifiedScript.substr(pos,len) == "namespace" ) { // Get the identifier after "namespace" do { pos += len; t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); } while(t == asTC_COMMENT || t == asTC_WHITESPACE); if( currentNamespace != "" ) currentNamespace += "::"; currentNamespace += modifiedScript.substr(pos,len); // Search until first { is encountered while( pos < modifiedScript.length() ) { engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); // If start of namespace section encountered stop if( modifiedScript[pos] == '{' ) { pos += len; break; } // Check next symbol pos += len; } continue; } // Check if end of namespace if( currentNamespace != "" && modifiedScript[pos] == '}' ) { size_t found = currentNamespace.rfind( "::" ); if( found != string::npos ) { currentNamespace.erase( found ); } else { currentNamespace = ""; } pos += len; continue; } // Is this the start of metadata? if( modifiedScript[pos] == '[' ) { // Get the metadata string pos = ExtractMetadata(pos, metadata); // Determine what this metadata is for int type; ExtractDeclaration(pos, name, declaration, type); // Store away the declaration in a map for lookup after the build has completed if( type > 0 ) { SMetadataDecl decl(metadata, name, declaration, type, currentClass, currentNamespace); foundDeclarations.push_back(decl); } } else #endif // Is this a preprocessor directive? if( modifiedScript[pos] == '#' && (pos + 1 < modifiedScript.size()) ) { int start = pos++; t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); if( t == asTC_IDENTIFIER ) { string token; token.assign(&modifiedScript[pos], len); if( token == "include" ) { pos += len; t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); if( t == asTC_WHITESPACE ) { pos += len; t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); } if( t == asTC_VALUE && len > 2 && (modifiedScript[pos] == '"' || modifiedScript[pos] == '\'') ) { // Get the include file string includefile; includefile.assign(&modifiedScript[pos+1], len-2); pos += len; // Store it for later processing includes.push_back(includefile); // Overwrite the include directive with space characters to avoid compiler error OverwriteCode(start, pos-start); } } else if (token == "pragma") { // Read until the end of the line pos += len; for (; pos < modifiedScript.size() && modifiedScript[pos] != '\n'; pos++); // Call the pragma callback string pragmaText(&modifiedScript[start + 7], pos - start - 7); int r = pragmaCallback ? pragmaCallback(pragmaText, *this, pragmaParam) : -1; if (r < 0) { // TODO: Report the correct line number engine->WriteMessage(sectionname, 0, 0, asMSGTYPE_ERROR, "Invalid #pragma directive"); return r; } // Overwrite the pragma directive with space characters to avoid compiler error OverwriteCode(start, pos - start); } } } // Don't search for metadata/includes within statement blocks or between tokens in statements else { pos = SkipStatement(pos); } } // Build the actual script engine->SetEngineProperty(asEP_COPY_SCRIPT_SECTIONS, true); module->AddScriptSection(sectionname, modifiedScript.c_str(), modifiedScript.size(), lineOffset); if( includes.size() > 0 ) { // If the callback has been set, then call it for each included file if( includeCallback ) { for( int n = 0; n < (int)includes.size(); n++ ) { int r = includeCallback(includes[n].c_str(), sectionname, this, includeParam); if( r < 0 ) return r; } } else { // By default we try to load the included file from the relative directory of the current file // Determine the path of the current script so that we can resolve relative paths for includes string path = sectionname; size_t posOfSlash = path.find_last_of("/\\"); if( posOfSlash != string::npos ) path.resize(posOfSlash+1); else path = ""; // Load the included scripts for( int n = 0; n < (int)includes.size(); n++ ) { // If the include is a relative path, then prepend the path of the originating script if( includes[n].find_first_of("/\\") != 0 && includes[n].find_first_of(":") == string::npos ) { includes[n] = path + includes[n]; } // Include the script section int r = AddSectionFromFile(includes[n].c_str()); if( r < 0 ) return r; } } } return 0; } int CScriptBuilder::Build() { int r = module->Build(); if( r < 0 ) return r; #if AS_PROCESS_METADATA == 1 // After the script has been built, the metadata strings should be // stored for later lookup by function id, type id, and variable index for( int n = 0; n < (int)foundDeclarations.size(); n++ ) { SMetadataDecl *decl = &foundDeclarations[n]; module->SetDefaultNamespace(decl->nameSpace.c_str()); if( decl->type == MDT_TYPE ) { // Find the type id int typeId = module->GetTypeIdByDecl(decl->declaration.c_str()); assert( typeId >= 0 ); if( typeId >= 0 ) typeMetadataMap.insert(map >::value_type(typeId, decl->metadata)); } else if( decl->type == MDT_FUNC ) { if( decl->parentClass == "" ) { // Find the function id asIScriptFunction *func = module->GetFunctionByDecl(decl->declaration.c_str()); assert( func ); if( func ) funcMetadataMap.insert(map >::value_type(func->GetId(), decl->metadata)); } else { // Find the method id int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str()); assert( typeId > 0 ); map::iterator it = classMetadataMap.find(typeId); if( it == classMetadataMap.end() ) { classMetadataMap.insert(map::value_type(typeId, SClassMetadata(decl->parentClass))); it = classMetadataMap.find(typeId); } asITypeInfo *type = engine->GetTypeInfoById(typeId); asIScriptFunction *func = type->GetMethodByDecl(decl->declaration.c_str()); assert( func ); if( func ) it->second.funcMetadataMap.insert(map >::value_type(func->GetId(), decl->metadata)); } } else if( decl->type == MDT_VIRTPROP ) { if( decl->parentClass == "" ) { // Find the global virtual property accessors asIScriptFunction *func = module->GetFunctionByName(("get_" + decl->declaration).c_str()); if( func ) funcMetadataMap.insert(map >::value_type(func->GetId(), decl->metadata)); func = module->GetFunctionByName(("set_" + decl->declaration).c_str()); if( func ) funcMetadataMap.insert(map >::value_type(func->GetId(), decl->metadata)); } else { // Find the method virtual property accessors int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str()); assert( typeId > 0 ); map::iterator it = classMetadataMap.find(typeId); if( it == classMetadataMap.end() ) { classMetadataMap.insert(map::value_type(typeId, SClassMetadata(decl->parentClass))); it = classMetadataMap.find(typeId); } asITypeInfo *type = engine->GetTypeInfoById(typeId); asIScriptFunction *func = type->GetMethodByName(("get_" + decl->declaration).c_str()); if( func ) it->second.funcMetadataMap.insert(map >::value_type(func->GetId(), decl->metadata)); func = type->GetMethodByName(("set_" + decl->declaration).c_str()); if( func ) it->second.funcMetadataMap.insert(map >::value_type(func->GetId(), decl->metadata)); } } else if( decl->type == MDT_VAR ) { if( decl->parentClass == "" ) { // Find the global variable index int varIdx = module->GetGlobalVarIndexByName(decl->declaration.c_str()); assert( varIdx >= 0 ); if( varIdx >= 0 ) varMetadataMap.insert(map >::value_type(varIdx, decl->metadata)); } else { int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str()); assert( typeId > 0 ); // Add the classes if needed map::iterator it = classMetadataMap.find(typeId); if( it == classMetadataMap.end() ) { classMetadataMap.insert(map::value_type(typeId, SClassMetadata(decl->parentClass))); it = classMetadataMap.find(typeId); } // Add the variable to class asITypeInfo *objectType = engine->GetTypeInfoById(typeId); int idx = -1; // Search through all properties to get proper declaration for( asUINT i = 0; i < (asUINT)objectType->GetPropertyCount(); ++i ) { const char *name; objectType->GetProperty(i, &name); if( decl->declaration == name ) { idx = i; break; } } // If found, add it assert( idx >= 0 ); if( idx >= 0 ) it->second.varMetadataMap.insert(map >::value_type(idx, decl->metadata)); } } else if (decl->type == MDT_FUNC_OR_VAR) { if (decl->parentClass == "") { // Find the global variable index int varIdx = module->GetGlobalVarIndexByName(decl->name.c_str()); if (varIdx >= 0) varMetadataMap.insert(map >::value_type(varIdx, decl->metadata)); else { asIScriptFunction *func = module->GetFunctionByDecl(decl->declaration.c_str()); assert(func); if (func) funcMetadataMap.insert(map >::value_type(func->GetId(), decl->metadata)); } } else { int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str()); assert(typeId > 0); // Add the classes if needed map::iterator it = classMetadataMap.find(typeId); if (it == classMetadataMap.end()) { classMetadataMap.insert(map::value_type(typeId, SClassMetadata(decl->parentClass))); it = classMetadataMap.find(typeId); } // Add the variable to class asITypeInfo *objectType = engine->GetTypeInfoById(typeId); int idx = -1; // Search through all properties to get proper declaration for (asUINT i = 0; i < (asUINT)objectType->GetPropertyCount(); ++i) { const char *name; objectType->GetProperty(i, &name); if (decl->name == name) { idx = i; break; } } // If found, add it if (idx >= 0) it->second.varMetadataMap.insert(map >::value_type(idx, decl->metadata)); else { // Look for the matching method instead asITypeInfo *type = engine->GetTypeInfoById(typeId); asIScriptFunction *func = type->GetMethodByDecl(decl->declaration.c_str()); assert(func); if (func) it->second.funcMetadataMap.insert(map >::value_type(func->GetId(), decl->metadata)); } } } } module->SetDefaultNamespace(""); #endif return 0; } int CScriptBuilder::SkipStatement(int pos) { asUINT len = 0; // Skip until ; or { whichever comes first while( pos < (int)modifiedScript.length() && modifiedScript[pos] != ';' && modifiedScript[pos] != '{' ) { engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); pos += len; } // Skip entire statement block if( pos < (int)modifiedScript.length() && modifiedScript[pos] == '{' ) { pos += 1; // Find the end of the statement block int level = 1; while( level > 0 && pos < (int)modifiedScript.size() ) { asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); if( t == asTC_KEYWORD ) { if( modifiedScript[pos] == '{' ) level++; else if( modifiedScript[pos] == '}' ) level--; } pos += len; } } else pos += 1; return pos; } // Overwrite all code with blanks until the matching #endif int CScriptBuilder::ExcludeCode(int pos) { asUINT len = 0; int nested = 0; while( pos < (int)modifiedScript.size() ) { engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); if( modifiedScript[pos] == '#' ) { modifiedScript[pos] = ' '; pos++; // Is it an #if or #endif directive? engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); string token; token.assign(&modifiedScript[pos], len); OverwriteCode(pos, len); if( token == "if" ) { nested++; } else if( token == "endif" ) { if( nested-- == 0 ) { pos += len; break; } } } else if( modifiedScript[pos] != '\n' ) { OverwriteCode(pos, len); } pos += len; } return pos; } // Overwrite all characters except line breaks with blanks void CScriptBuilder::OverwriteCode(int start, int len) { char *code = &modifiedScript[start]; for( int n = 0; n < len; n++ ) { if( *code != '\n' ) *code = ' '; code++; } } #if AS_PROCESS_METADATA == 1 int CScriptBuilder::ExtractMetadata(int pos, vector &metadata) { metadata.clear(); // Extract all metadata. They can be separated by whitespace and comments for (;;) { string metadataString = ""; // Overwrite the metadata with space characters to allow compilation modifiedScript[pos] = ' '; // Skip opening brackets pos += 1; int level = 1; asUINT len = 0; while (level > 0 && pos < (int)modifiedScript.size()) { asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); if (t == asTC_KEYWORD) { if (modifiedScript[pos] == '[') level++; else if (modifiedScript[pos] == ']') level--; } // Copy the metadata to our buffer if (level > 0) metadataString.append(&modifiedScript[pos], len); // Overwrite the metadata with space characters to allow compilation if (t != asTC_WHITESPACE) OverwriteCode(pos, len); pos += len; } metadata.push_back(metadataString); // Check for more metadata. Possibly separated by comments asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); while (t == asTC_COMMENT || t == asTC_WHITESPACE) { pos += len; t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); } if (modifiedScript[pos] != '[') break; } return pos; } int CScriptBuilder::ExtractDeclaration(int pos, string &name, string &declaration, int &type) { declaration = ""; type = 0; int start = pos; std::string token; asUINT len = 0; asETokenClass t = asTC_WHITESPACE; // Skip white spaces, comments, and leading decorators do { pos += len; t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); token.assign(&modifiedScript[pos], len); } while ( t == asTC_WHITESPACE || t == asTC_COMMENT || token == "private" || token == "protected" || token == "shared" || token == "external" || token == "final" || token == "abstract" ); // We're expecting, either a class, interface, function, or variable declaration if( t == asTC_KEYWORD || t == asTC_IDENTIFIER ) { token.assign(&modifiedScript[pos], len); if( token == "interface" || token == "class" || token == "enum" ) { // Skip white spaces and comments do { pos += len; t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); } while ( t == asTC_WHITESPACE || t == asTC_COMMENT ); if( t == asTC_IDENTIFIER ) { type = MDT_TYPE; declaration.assign(&modifiedScript[pos], len); pos += len; return pos; } } else { // For function declarations, store everything up to the start of the // statement block, except for succeeding decorators (final, override, etc) // For variable declaration store just the name as there can only be one // We'll only know if the declaration is a variable or function declaration // when we see the statement block, or absense of a statement block. bool hasParenthesis = false; int nestedParenthesis = 0; declaration.append(&modifiedScript[pos], len); pos += len; for(; pos < (int)modifiedScript.size();) { t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len); token.assign(&modifiedScript[pos], len); if (t == asTC_KEYWORD) { if (token == "{" && nestedParenthesis == 0) { if (hasParenthesis) { // We've found the end of a function signature type = MDT_FUNC; } else { // We've found a virtual property. Just keep the name declaration = name; type = MDT_VIRTPROP; } return pos; } if ((token == "=" && !hasParenthesis) || token == ";") { if (hasParenthesis) { // The declaration is ambigous. It can be a variable with initialization, or a function prototype type = MDT_FUNC_OR_VAR; } else { // Substitute the declaration with just the name declaration = name; type = MDT_VAR; } return pos; } else if (token == "(") { nestedParenthesis++; // This is the first parenthesis we encounter. If the parenthesis isn't followed // by a statement block, then this is a variable declaration, in which case we // should only store the type and name of the variable, not the initialization parameters. hasParenthesis = true; } else if (token == ")") { nestedParenthesis--; } } else if( t == asTC_IDENTIFIER ) { name = token; } // Skip trailing decorators if( !hasParenthesis || nestedParenthesis > 0 || t != asTC_IDENTIFIER || (token != "final" && token != "override") ) declaration += token; pos += len; } } } return start; } vector CScriptBuilder::GetMetadataForType(int typeId) { map >::iterator it = typeMetadataMap.find(typeId); if( it != typeMetadataMap.end() ) return it->second; return vector(); } vector CScriptBuilder::GetMetadataForFunc(asIScriptFunction *func) { if( func ) { map >::iterator it = funcMetadataMap.find(func->GetId()); if( it != funcMetadataMap.end() ) return it->second; } return vector(); } vector CScriptBuilder::GetMetadataForVar(int varIdx) { map >::iterator it = varMetadataMap.find(varIdx); if( it != varMetadataMap.end() ) return it->second; return vector(); } vector CScriptBuilder::GetMetadataForTypeProperty(int typeId, int varIdx) { map::iterator typeIt = classMetadataMap.find(typeId); if(typeIt == classMetadataMap.end()) return vector(); map >::iterator propIt = typeIt->second.varMetadataMap.find(varIdx); if(propIt == typeIt->second.varMetadataMap.end()) return vector(); return propIt->second; } vector CScriptBuilder::GetMetadataForTypeMethod(int typeId, asIScriptFunction *method) { if( method ) { map::iterator typeIt = classMetadataMap.find(typeId); if (typeIt == classMetadataMap.end()) return vector(); map >::iterator methodIt = typeIt->second.funcMetadataMap.find(method->GetId()); if(methodIt == typeIt->second.funcMetadataMap.end()) return vector(); return methodIt->second; } return vector(); } #endif string GetAbsolutePath(const string &file) { string str = file; // If this is a relative path, complement it with the current path if( !((str.length() > 0 && (str[0] == '/' || str[0] == '\\')) || str.find(":") != string::npos) ) { str = GetCurrentDir() + "/" + str; } // Replace backslashes for forward slashes size_t pos = 0; while( (pos = str.find("\\", pos)) != string::npos ) str[pos] = '/'; // Replace /./ with / pos = 0; while( (pos = str.find("/./", pos)) != string::npos ) str.erase(pos+1, 2); // For each /../ remove the parent dir and the /../ pos = 0; while( (pos = str.find("/../")) != string::npos ) { size_t pos2 = str.rfind("/", pos-1); if( pos2 != string::npos ) str.erase(pos2, pos+3-pos2); else { // The path is invalid break; } } return str; } string GetCurrentDir() { char buffer[1024]; #if defined(_MSC_VER) || defined(_WIN32) #ifdef _WIN32_WCE static TCHAR apppath[MAX_PATH] = TEXT(""); if (!apppath[0]) { GetModuleFileName(NULL, apppath, MAX_PATH); int appLen = _tcslen(apppath); // Look for the last backslash in the path, which would be the end // of the path itself and the start of the filename. We only want // the path part of the exe's full-path filename // Safety is that we make sure not to walk off the front of the // array (in case the path is nothing more than a filename) while (appLen > 1) { if (apppath[appLen-1] == TEXT('\\')) break; appLen--; } // Terminate the string after the trailing backslash apppath[appLen] = TEXT('\0'); } #ifdef _UNICODE wcstombs(buffer, apppath, min(1024, wcslen(apppath)*sizeof(wchar_t))); #else memcpy(buffer, apppath, min(1024, strlen(apppath))); #endif return buffer; #elif defined(__S3E__) // Marmalade uses its own portable C library return getcwd(buffer, (int)1024); #elif _XBOX_VER >= 200 // XBox 360 doesn't support the getcwd function, just use the root folder return "game:/"; #elif defined(_M_ARM) // TODO: How to determine current working dir on Windows Phone? return ""; #else return _getcwd(buffer, (int)1024); #endif // _MSC_VER #elif defined(__APPLE__) || defined(__linux__) return getcwd(buffer, 1024); #else return ""; #endif } END_AS_NAMESPACE