1 // Scintilla source code edit control 2 /** @file ExternalLexer.cxx 3 ** Support external lexers in DLLs or shared libraries. 4 **/ 5 // Copyright 2001 Simon Steele <[email protected]>, portions copyright Neil Hodgson. 6 // The License.txt file describes the conditions under which this software may be distributed. 7 8 #include <cstdlib> 9 #include <cassert> 10 #include <cstring> 11 12 #include <stdexcept> 13 #include <string> 14 #include <string_view> 15 #include <vector> 16 #include <memory> 17 18 #include "Platform.h" 19 20 #include "ILexer.h" 21 #include "Scintilla.h" 22 #include "SciLexer.h" 23 24 #include "LexerModule.h" 25 #include "Catalogue.h" 26 #include "ExternalLexer.h" 27 28 using namespace Scintilla; 29 30 #if PLAT_WIN 31 #define EXT_LEXER_DECL __stdcall 32 #else 33 #define EXT_LEXER_DECL 34 #endif 35 36 namespace { 37 38 int nextLanguage = SCLEX_AUTOMATIC + 1; 39 40 typedef int (EXT_LEXER_DECL *GetLexerCountFn)(); 41 typedef void (EXT_LEXER_DECL *GetLexerNameFn)(unsigned int Index, char *name, int buflength); 42 typedef LexerFactoryFunction(EXT_LEXER_DECL *GetLexerFactoryFunction)(unsigned int Index); 43 44 /// Generic function to convert from a void* to a function pointer. 45 /// This avoids undefined and conditionally defined behaviour. 46 template<typename T> 47 T FunctionPointer(void *function) noexcept { 48 static_assert(sizeof(T) == sizeof(function)); 49 T fp; 50 memcpy(&fp, &function, sizeof(T)); 51 return fp; 52 } 53 54 /// Sub-class of LexerModule to use an external lexer. 55 class ExternalLexerModule : public LexerModule { 56 protected: 57 GetLexerFactoryFunction fneFactory; 58 std::string name; 59 public: 60 ExternalLexerModule(int language_, LexerFunction fnLexer_, 61 const char *languageName_=nullptr, LexerFunction fnFolder_=nullptr) : 62 LexerModule(language_, fnLexer_, nullptr, fnFolder_), 63 fneFactory(nullptr), name(languageName_){ 64 languageName = name.c_str(); 65 } 66 void SetExternal(GetLexerFactoryFunction fFactory, int index) noexcept; 67 }; 68 69 /// LexerLibrary exists for every External Lexer DLL, contains ExternalLexerModules. 70 class LexerLibrary { 71 std::unique_ptr<DynamicLibrary> lib; 72 std::vector<std::unique_ptr<ExternalLexerModule>> modules; 73 public: 74 explicit LexerLibrary(const char *moduleName_); 75 ~LexerLibrary(); 76 77 std::string moduleName; 78 }; 79 80 /// LexerManager manages external lexers, contains LexerLibrarys. 81 class LexerManager { 82 public: 83 ~LexerManager(); 84 85 static LexerManager *GetInstance(); 86 static void DeleteInstance() noexcept; 87 88 void Load(const char *path); 89 void Clear() noexcept; 90 91 private: 92 LexerManager(); 93 static std::unique_ptr<LexerManager> theInstance; 94 std::vector<std::unique_ptr<LexerLibrary>> libraries; 95 }; 96 97 class LMMinder { 98 public: 99 ~LMMinder(); 100 }; 101 102 std::unique_ptr<LexerManager> LexerManager::theInstance; 103 104 //------------------------------------------ 105 // 106 // ExternalLexerModule 107 // 108 //------------------------------------------ 109 110 void ExternalLexerModule::SetExternal(GetLexerFactoryFunction fFactory, int index) noexcept { 111 fneFactory = fFactory; 112 fnFactory = fFactory(index); 113 } 114 115 //------------------------------------------ 116 // 117 // LexerLibrary 118 // 119 //------------------------------------------ 120 121 LexerLibrary::LexerLibrary(const char *moduleName_) { 122 // Load the DLL 123 lib.reset(DynamicLibrary::Load(moduleName_)); 124 if (lib->IsValid()) { 125 moduleName = moduleName_; 126 GetLexerCountFn GetLexerCount = FunctionPointer<GetLexerCountFn>(lib->FindFunction("GetLexerCount")); 127 128 if (GetLexerCount) { 129 // Find functions in the DLL 130 GetLexerNameFn GetLexerName = FunctionPointer<GetLexerNameFn>(lib->FindFunction("GetLexerName")); 131 GetLexerFactoryFunction fnFactory = FunctionPointer<GetLexerFactoryFunction>(lib->FindFunction("GetLexerFactory")); 132 133 if (!GetLexerName || !fnFactory) { 134 return; 135 } 136 137 const int nl = GetLexerCount(); 138 139 for (int i = 0; i < nl; i++) { 140 // Assign a buffer for the lexer name. 141 char lexname[100] = ""; 142 GetLexerName(i, lexname, sizeof(lexname)); 143 ExternalLexerModule *lex = new ExternalLexerModule(nextLanguage, nullptr, lexname, nullptr); 144 nextLanguage++; 145 146 // This is storing a second reference to lex in the Catalogue as well as in modules. 147 // TODO: Should use std::shared_ptr or similar to ensure allocation safety. 148 Catalogue::AddLexerModule(lex); 149 150 // Remember ExternalLexerModule so we don't leak it 151 modules.push_back(std::unique_ptr<ExternalLexerModule>(lex)); 152 153 // The external lexer needs to know how to call into its DLL to 154 // do its lexing and folding, we tell it here. 155 lex->SetExternal(fnFactory, i); 156 } 157 } 158 } 159 } 160 161 LexerLibrary::~LexerLibrary() { 162 } 163 164 //------------------------------------------ 165 // 166 // LexerManager 167 // 168 //------------------------------------------ 169 170 /// Return the single LexerManager instance... 171 LexerManager *LexerManager::GetInstance() { 172 if (!theInstance) 173 theInstance.reset(new LexerManager); 174 return theInstance.get(); 175 } 176 177 /// Delete any LexerManager instance... 178 void LexerManager::DeleteInstance() noexcept { 179 theInstance.reset(); 180 } 181 182 /// protected constructor - this is a singleton... 183 LexerManager::LexerManager() { 184 } 185 186 LexerManager::~LexerManager() { 187 Clear(); 188 } 189 190 void LexerManager::Load(const char *path) { 191 for (const std::unique_ptr<LexerLibrary> &ll : libraries) { 192 if (ll->moduleName == path) 193 return; 194 } 195 libraries.push_back(std::make_unique<LexerLibrary>(path)); 196 } 197 198 void LexerManager::Clear() noexcept { 199 libraries.clear(); 200 } 201 202 //------------------------------------------ 203 // 204 // LMMinder -- trigger to clean up at exit. 205 // 206 //------------------------------------------ 207 208 LMMinder::~LMMinder() { 209 LexerManager::DeleteInstance(); 210 } 211 212 LMMinder minder; 213 214 } 215 216 namespace Scintilla { 217 218 void ExternalLexerLoad(const char *path) { 219 LexerManager::GetInstance()->Load(path); 220 } 221 222 } 223