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 <ss@pnotepad.org>, 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>
FunctionPointer(void * function)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:
ExternalLexerModule(int language_,LexerFunction fnLexer_,const char * languageName_=nullptr,LexerFunction fnFolder_=nullptr)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 
SetExternal(GetLexerFactoryFunction fFactory,int index)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 
LexerLibrary(const char * moduleName_)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 
~LexerLibrary()161 LexerLibrary::~LexerLibrary() {
162 }
163 
164 //------------------------------------------
165 //
166 // LexerManager
167 //
168 //------------------------------------------
169 
170 /// Return the single LexerManager instance...
GetInstance()171 LexerManager *LexerManager::GetInstance() {
172 	if (!theInstance)
173 		theInstance.reset(new LexerManager);
174 	return theInstance.get();
175 }
176 
177 /// Delete any LexerManager instance...
DeleteInstance()178 void LexerManager::DeleteInstance() noexcept {
179 	theInstance.reset();
180 }
181 
182 /// protected constructor - this is a singleton...
LexerManager()183 LexerManager::LexerManager() {
184 }
185 
~LexerManager()186 LexerManager::~LexerManager() {
187 	Clear();
188 }
189 
Load(const char * path)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 
Clear()198 void LexerManager::Clear() noexcept {
199 	libraries.clear();
200 }
201 
202 //------------------------------------------
203 //
204 // LMMinder	-- trigger to clean up at exit.
205 //
206 //------------------------------------------
207 
~LMMinder()208 LMMinder::~LMMinder() {
209 	LexerManager::DeleteInstance();
210 }
211 
212 LMMinder minder;
213 
214 }
215 
216 namespace Scintilla {
217 
ExternalLexerLoad(const char * path)218 void ExternalLexerLoad(const char *path) {
219 	LexerManager::GetInstance()->Load(path);
220 }
221 
222 }
223