1 | //===-- Language.cpp ------------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include <functional> |
10 | #include <map> |
11 | #include <mutex> |
12 | |
13 | #include "lldb/Target/Language.h" |
14 | |
15 | #include "lldb/Core/PluginManager.h" |
16 | #include "lldb/Symbol/SymbolFile.h" |
17 | #include "lldb/Symbol/TypeList.h" |
18 | #include "lldb/Target/Target.h" |
19 | #include "lldb/Utility/Stream.h" |
20 | |
21 | #include "llvm/Support/Threading.h" |
22 | |
23 | using namespace lldb; |
24 | using namespace lldb_private; |
25 | using namespace lldb_private::formatters; |
26 | |
27 | typedef std::unique_ptr<Language> LanguageUP; |
28 | typedef std::map<lldb::LanguageType, LanguageUP> LanguagesMap; |
29 | |
30 | static LanguagesMap &GetLanguagesMap() { |
31 | static LanguagesMap *g_map = nullptr; |
32 | static llvm::once_flag g_initialize; |
33 | |
34 | llvm::call_once(flag&: g_initialize, F: [] { |
35 | g_map = new LanguagesMap(); // NOTE: INTENTIONAL LEAK due to global |
36 | // destructor chain |
37 | }); |
38 | |
39 | return *g_map; |
40 | } |
41 | static std::mutex &GetLanguagesMutex() { |
42 | static std::mutex *g_mutex = nullptr; |
43 | static llvm::once_flag g_initialize; |
44 | |
45 | llvm::call_once(flag&: g_initialize, F: [] { |
46 | g_mutex = new std::mutex(); // NOTE: INTENTIONAL LEAK due to global |
47 | // destructor chain |
48 | }); |
49 | |
50 | return *g_mutex; |
51 | } |
52 | |
53 | Language *Language::FindPlugin(lldb::LanguageType language) { |
54 | std::lock_guard<std::mutex> guard(GetLanguagesMutex()); |
55 | LanguagesMap &map(GetLanguagesMap()); |
56 | auto iter = map.find(x: language), end = map.end(); |
57 | if (iter != end) |
58 | return iter->second.get(); |
59 | |
60 | Language *language_ptr = nullptr; |
61 | LanguageCreateInstance create_callback; |
62 | |
63 | for (uint32_t idx = 0; |
64 | (create_callback = |
65 | PluginManager::GetLanguageCreateCallbackAtIndex(idx)) != nullptr; |
66 | ++idx) { |
67 | language_ptr = create_callback(language); |
68 | |
69 | if (language_ptr) { |
70 | map[language] = std::unique_ptr<Language>(language_ptr); |
71 | return language_ptr; |
72 | } |
73 | } |
74 | |
75 | return nullptr; |
76 | } |
77 | |
78 | Language *Language::FindPlugin(llvm::StringRef file_path) { |
79 | Language *result = nullptr; |
80 | ForEach(callback: [&result, file_path](Language *language) { |
81 | if (language->IsSourceFile(file_path)) { |
82 | result = language; |
83 | return false; |
84 | } |
85 | return true; |
86 | }); |
87 | return result; |
88 | } |
89 | |
90 | Language *Language::FindPlugin(LanguageType language, |
91 | llvm::StringRef file_path) { |
92 | Language *result = FindPlugin(language); |
93 | // Finding a language by file path is slower, we so we use this as the |
94 | // fallback. |
95 | if (!result) |
96 | result = FindPlugin(file_path); |
97 | return result; |
98 | } |
99 | |
100 | void Language::ForEach(std::function<bool(Language *)> callback) { |
101 | // If we want to iterate over all languages, we first have to complete the |
102 | // LanguagesMap. |
103 | static llvm::once_flag g_initialize; |
104 | llvm::call_once(flag&: g_initialize, F: [] { |
105 | for (unsigned lang = eLanguageTypeUnknown; lang < eNumLanguageTypes; |
106 | ++lang) { |
107 | FindPlugin(language: static_cast<lldb::LanguageType>(lang)); |
108 | } |
109 | }); |
110 | |
111 | // callback may call a method in Language that attempts to acquire the same |
112 | // lock (such as Language::ForEach or Language::FindPlugin). To avoid a |
113 | // deadlock, we do not use callback while holding the lock. |
114 | std::vector<Language *> loaded_plugins; |
115 | { |
116 | std::lock_guard<std::mutex> guard(GetLanguagesMutex()); |
117 | LanguagesMap &map(GetLanguagesMap()); |
118 | for (const auto &entry : map) { |
119 | if (entry.second) |
120 | loaded_plugins.push_back(x: entry.second.get()); |
121 | } |
122 | } |
123 | |
124 | for (auto *lang : loaded_plugins) { |
125 | if (!callback(lang)) |
126 | break; |
127 | } |
128 | } |
129 | |
130 | bool Language::IsTopLevelFunction(Function &function) { return false; } |
131 | |
132 | lldb::TypeCategoryImplSP Language::GetFormatters() { return nullptr; } |
133 | |
134 | HardcodedFormatters::HardcodedFormatFinder Language::GetHardcodedFormats() { |
135 | return {}; |
136 | } |
137 | |
138 | HardcodedFormatters::HardcodedSummaryFinder Language::GetHardcodedSummaries() { |
139 | return {}; |
140 | } |
141 | |
142 | HardcodedFormatters::HardcodedSyntheticFinder |
143 | Language::GetHardcodedSynthetics() { |
144 | return {}; |
145 | } |
146 | |
147 | std::vector<FormattersMatchCandidate> |
148 | Language::GetPossibleFormattersMatches(ValueObject &valobj, |
149 | lldb::DynamicValueType use_dynamic) { |
150 | return {}; |
151 | } |
152 | |
153 | struct language_name_pair { |
154 | const char *name; |
155 | LanguageType type; |
156 | }; |
157 | |
158 | struct language_name_pair language_names[] = { |
159 | // To allow GetNameForLanguageType to be a simple array lookup, the first |
160 | // part of this array must follow enum LanguageType exactly. |
161 | {.name: "unknown" , .type: eLanguageTypeUnknown}, |
162 | {.name: "c89" , .type: eLanguageTypeC89}, |
163 | {.name: "c" , .type: eLanguageTypeC}, |
164 | {.name: "ada83" , .type: eLanguageTypeAda83}, |
165 | {.name: "c++" , .type: eLanguageTypeC_plus_plus}, |
166 | {.name: "cobol74" , .type: eLanguageTypeCobol74}, |
167 | {.name: "cobol85" , .type: eLanguageTypeCobol85}, |
168 | {.name: "fortran77" , .type: eLanguageTypeFortran77}, |
169 | {.name: "fortran90" , .type: eLanguageTypeFortran90}, |
170 | {.name: "pascal83" , .type: eLanguageTypePascal83}, |
171 | {.name: "modula2" , .type: eLanguageTypeModula2}, |
172 | {.name: "java" , .type: eLanguageTypeJava}, |
173 | {.name: "c99" , .type: eLanguageTypeC99}, |
174 | {.name: "ada95" , .type: eLanguageTypeAda95}, |
175 | {.name: "fortran95" , .type: eLanguageTypeFortran95}, |
176 | {.name: "pli" , .type: eLanguageTypePLI}, |
177 | {.name: "objective-c" , .type: eLanguageTypeObjC}, |
178 | {.name: "objective-c++" , .type: eLanguageTypeObjC_plus_plus}, |
179 | {.name: "upc" , .type: eLanguageTypeUPC}, |
180 | {.name: "d" , .type: eLanguageTypeD}, |
181 | {.name: "python" , .type: eLanguageTypePython}, |
182 | {.name: "opencl" , .type: eLanguageTypeOpenCL}, |
183 | {.name: "go" , .type: eLanguageTypeGo}, |
184 | {.name: "modula3" , .type: eLanguageTypeModula3}, |
185 | {.name: "haskell" , .type: eLanguageTypeHaskell}, |
186 | {.name: "c++03" , .type: eLanguageTypeC_plus_plus_03}, |
187 | {.name: "c++11" , .type: eLanguageTypeC_plus_plus_11}, |
188 | {.name: "ocaml" , .type: eLanguageTypeOCaml}, |
189 | {.name: "rust" , .type: eLanguageTypeRust}, |
190 | {.name: "c11" , .type: eLanguageTypeC11}, |
191 | {.name: "swift" , .type: eLanguageTypeSwift}, |
192 | {.name: "julia" , .type: eLanguageTypeJulia}, |
193 | {.name: "dylan" , .type: eLanguageTypeDylan}, |
194 | {.name: "c++14" , .type: eLanguageTypeC_plus_plus_14}, |
195 | {.name: "fortran03" , .type: eLanguageTypeFortran03}, |
196 | {.name: "fortran08" , .type: eLanguageTypeFortran08}, |
197 | {.name: "renderscript" , .type: eLanguageTypeRenderScript}, |
198 | {.name: "bliss" , .type: eLanguageTypeBLISS}, |
199 | {.name: "kotlin" , .type: eLanguageTypeKotlin}, |
200 | {.name: "zig" , .type: eLanguageTypeZig}, |
201 | {.name: "crystal" , .type: eLanguageTypeCrystal}, |
202 | {.name: "<invalid language>" , |
203 | .type: static_cast<LanguageType>( |
204 | 0x0029)}, // Not yet taken by any language in the DWARF spec |
205 | // and thus has no entry in LanguageType |
206 | {.name: "c++17" , .type: eLanguageTypeC_plus_plus_17}, |
207 | {.name: "c++20" , .type: eLanguageTypeC_plus_plus_20}, |
208 | {.name: "c17" , .type: eLanguageTypeC17}, |
209 | {.name: "fortran18" , .type: eLanguageTypeFortran18}, |
210 | {.name: "ada2005" , .type: eLanguageTypeAda2005}, |
211 | {.name: "ada2012" , .type: eLanguageTypeAda2012}, |
212 | {.name: "HIP" , .type: eLanguageTypeHIP}, |
213 | {.name: "assembly" , .type: eLanguageTypeAssembly}, |
214 | {.name: "c-sharp" , .type: eLanguageTypeC_sharp}, |
215 | {.name: "mojo" , .type: eLanguageTypeMojo}, |
216 | // Vendor Extensions |
217 | {.name: "assembler" , .type: eLanguageTypeMipsAssembler}, |
218 | // Now synonyms, in arbitrary order |
219 | {.name: "objc" , .type: eLanguageTypeObjC}, |
220 | {.name: "objc++" , .type: eLanguageTypeObjC_plus_plus}, |
221 | {.name: "pascal" , .type: eLanguageTypePascal83}}; |
222 | |
223 | static uint32_t num_languages = |
224 | sizeof(language_names) / sizeof(struct language_name_pair); |
225 | |
226 | LanguageType Language::GetLanguageTypeFromString(llvm::StringRef string) { |
227 | for (const auto &L : language_names) { |
228 | if (string.equals_insensitive(RHS: L.name)) |
229 | return static_cast<LanguageType>(L.type); |
230 | } |
231 | |
232 | return eLanguageTypeUnknown; |
233 | } |
234 | |
235 | const char *Language::GetNameForLanguageType(LanguageType language) { |
236 | if (language < num_languages) |
237 | return language_names[language].name; |
238 | else |
239 | return language_names[eLanguageTypeUnknown].name; |
240 | } |
241 | |
242 | void Language::PrintSupportedLanguagesForExpressions(Stream &s, |
243 | llvm::StringRef prefix, |
244 | llvm::StringRef suffix) { |
245 | auto supported = Language::GetLanguagesSupportingTypeSystemsForExpressions(); |
246 | for (size_t idx = 0; idx < num_languages; ++idx) { |
247 | auto const &lang = language_names[idx]; |
248 | if (supported[lang.type]) |
249 | s << prefix << lang.name << suffix; |
250 | } |
251 | } |
252 | |
253 | void Language::PrintAllLanguages(Stream &s, const char *prefix, |
254 | const char *suffix) { |
255 | for (uint32_t i = 1; i < num_languages; i++) { |
256 | s.Printf(format: "%s%s%s" , prefix, language_names[i].name, suffix); |
257 | } |
258 | } |
259 | |
260 | void Language::ForAllLanguages( |
261 | std::function<bool(lldb::LanguageType)> callback) { |
262 | for (uint32_t i = 1; i < num_languages; i++) { |
263 | if (!callback(language_names[i].type)) |
264 | break; |
265 | } |
266 | } |
267 | |
268 | bool Language::LanguageIsCPlusPlus(LanguageType language) { |
269 | switch (language) { |
270 | case eLanguageTypeC_plus_plus: |
271 | case eLanguageTypeC_plus_plus_03: |
272 | case eLanguageTypeC_plus_plus_11: |
273 | case eLanguageTypeC_plus_plus_14: |
274 | case eLanguageTypeC_plus_plus_17: |
275 | case eLanguageTypeC_plus_plus_20: |
276 | case eLanguageTypeObjC_plus_plus: |
277 | return true; |
278 | default: |
279 | return false; |
280 | } |
281 | } |
282 | |
283 | bool Language::LanguageIsObjC(LanguageType language) { |
284 | switch (language) { |
285 | case eLanguageTypeObjC: |
286 | case eLanguageTypeObjC_plus_plus: |
287 | return true; |
288 | default: |
289 | return false; |
290 | } |
291 | } |
292 | |
293 | bool Language::LanguageIsC(LanguageType language) { |
294 | switch (language) { |
295 | case eLanguageTypeC: |
296 | case eLanguageTypeC89: |
297 | case eLanguageTypeC99: |
298 | case eLanguageTypeC11: |
299 | return true; |
300 | default: |
301 | return false; |
302 | } |
303 | } |
304 | |
305 | bool Language::LanguageIsCFamily(LanguageType language) { |
306 | switch (language) { |
307 | case eLanguageTypeC: |
308 | case eLanguageTypeC89: |
309 | case eLanguageTypeC99: |
310 | case eLanguageTypeC11: |
311 | case eLanguageTypeC_plus_plus: |
312 | case eLanguageTypeC_plus_plus_03: |
313 | case eLanguageTypeC_plus_plus_11: |
314 | case eLanguageTypeC_plus_plus_14: |
315 | case eLanguageTypeC_plus_plus_17: |
316 | case eLanguageTypeC_plus_plus_20: |
317 | case eLanguageTypeObjC_plus_plus: |
318 | case eLanguageTypeObjC: |
319 | return true; |
320 | default: |
321 | return false; |
322 | } |
323 | } |
324 | |
325 | bool Language::LanguageIsPascal(LanguageType language) { |
326 | switch (language) { |
327 | case eLanguageTypePascal83: |
328 | return true; |
329 | default: |
330 | return false; |
331 | } |
332 | } |
333 | |
334 | LanguageType Language::GetPrimaryLanguage(LanguageType language) { |
335 | switch (language) { |
336 | case eLanguageTypeC_plus_plus: |
337 | case eLanguageTypeC_plus_plus_03: |
338 | case eLanguageTypeC_plus_plus_11: |
339 | case eLanguageTypeC_plus_plus_14: |
340 | case eLanguageTypeC_plus_plus_17: |
341 | case eLanguageTypeC_plus_plus_20: |
342 | return eLanguageTypeC_plus_plus; |
343 | case eLanguageTypeC: |
344 | case eLanguageTypeC89: |
345 | case eLanguageTypeC99: |
346 | case eLanguageTypeC11: |
347 | return eLanguageTypeC; |
348 | case eLanguageTypeObjC: |
349 | case eLanguageTypeObjC_plus_plus: |
350 | return eLanguageTypeObjC; |
351 | case eLanguageTypePascal83: |
352 | case eLanguageTypeCobol74: |
353 | case eLanguageTypeCobol85: |
354 | case eLanguageTypeFortran77: |
355 | case eLanguageTypeFortran90: |
356 | case eLanguageTypeFortran95: |
357 | case eLanguageTypeFortran03: |
358 | case eLanguageTypeFortran08: |
359 | case eLanguageTypeAda83: |
360 | case eLanguageTypeAda95: |
361 | case eLanguageTypeModula2: |
362 | case eLanguageTypeJava: |
363 | case eLanguageTypePLI: |
364 | case eLanguageTypeUPC: |
365 | case eLanguageTypeD: |
366 | case eLanguageTypePython: |
367 | case eLanguageTypeOpenCL: |
368 | case eLanguageTypeGo: |
369 | case eLanguageTypeModula3: |
370 | case eLanguageTypeHaskell: |
371 | case eLanguageTypeOCaml: |
372 | case eLanguageTypeRust: |
373 | case eLanguageTypeSwift: |
374 | case eLanguageTypeJulia: |
375 | case eLanguageTypeDylan: |
376 | case eLanguageTypeMipsAssembler: |
377 | case eLanguageTypeMojo: |
378 | case eLanguageTypeUnknown: |
379 | default: |
380 | return language; |
381 | } |
382 | } |
383 | |
384 | std::set<lldb::LanguageType> Language::GetSupportedLanguages() { |
385 | std::set<lldb::LanguageType> supported_languages; |
386 | ForEach(callback: [&](Language *lang) { |
387 | supported_languages.emplace(args: lang->GetLanguageType()); |
388 | return true; |
389 | }); |
390 | return supported_languages; |
391 | } |
392 | |
393 | LanguageSet Language::GetLanguagesSupportingTypeSystems() { |
394 | return PluginManager::GetAllTypeSystemSupportedLanguagesForTypes(); |
395 | } |
396 | |
397 | LanguageSet Language::GetLanguagesSupportingTypeSystemsForExpressions() { |
398 | return PluginManager::GetAllTypeSystemSupportedLanguagesForExpressions(); |
399 | } |
400 | |
401 | LanguageSet Language::GetLanguagesSupportingREPLs() { |
402 | return PluginManager::GetREPLAllTypeSystemSupportedLanguages(); |
403 | } |
404 | |
405 | std::unique_ptr<Language::TypeScavenger> Language::GetTypeScavenger() { |
406 | return nullptr; |
407 | } |
408 | |
409 | const char *Language::GetLanguageSpecificTypeLookupHelp() { return nullptr; } |
410 | |
411 | size_t Language::TypeScavenger::Find(ExecutionContextScope *exe_scope, |
412 | const char *key, ResultSet &results, |
413 | bool append) { |
414 | if (!exe_scope || !exe_scope->CalculateTarget().get()) |
415 | return false; |
416 | |
417 | if (!key || !key[0]) |
418 | return false; |
419 | |
420 | if (!append) |
421 | results.clear(); |
422 | |
423 | size_t old_size = results.size(); |
424 | |
425 | if (this->Find_Impl(exe_scope, key, results)) |
426 | return results.size() - old_size; |
427 | return 0; |
428 | } |
429 | |
430 | bool Language::ImageListTypeScavenger::Find_Impl( |
431 | ExecutionContextScope *exe_scope, const char *key, ResultSet &results) { |
432 | bool result = false; |
433 | |
434 | Target *target = exe_scope->CalculateTarget().get(); |
435 | if (target) { |
436 | const auto &images(target->GetImages()); |
437 | TypeQuery query(key); |
438 | TypeResults type_results; |
439 | images.FindTypes(search_first: nullptr, query, results&: type_results); |
440 | for (const auto &match : type_results.GetTypeMap().Types()) { |
441 | if (match) { |
442 | CompilerType compiler_type(match->GetFullCompilerType()); |
443 | compiler_type = AdjustForInclusion(candidate&: compiler_type); |
444 | if (!compiler_type) |
445 | continue; |
446 | std::unique_ptr<Language::TypeScavenger::Result> scavengeresult( |
447 | new Result(compiler_type)); |
448 | results.insert(x: std::move(scavengeresult)); |
449 | result = true; |
450 | } |
451 | } |
452 | } |
453 | |
454 | return result; |
455 | } |
456 | |
457 | std::pair<llvm::StringRef, llvm::StringRef> |
458 | Language::GetFormatterPrefixSuffix(llvm::StringRef type_hint) { |
459 | return std::pair<llvm::StringRef, llvm::StringRef>(); |
460 | } |
461 | |
462 | bool Language::DemangledNameContainsPath(llvm::StringRef path, |
463 | ConstString demangled) const { |
464 | // The base implementation does a simple contains comparision: |
465 | if (path.empty()) |
466 | return false; |
467 | return demangled.GetStringRef().contains(Other: path); |
468 | } |
469 | |
470 | DumpValueObjectOptions::DeclPrintingHelper Language::GetDeclPrintingHelper() { |
471 | return nullptr; |
472 | } |
473 | |
474 | LazyBool Language::IsLogicalTrue(ValueObject &valobj, Status &error) { |
475 | return eLazyBoolCalculate; |
476 | } |
477 | |
478 | bool Language::IsNilReference(ValueObject &valobj) { return false; } |
479 | |
480 | bool Language::IsUninitializedReference(ValueObject &valobj) { return false; } |
481 | |
482 | bool Language::GetFunctionDisplayName(const SymbolContext *sc, |
483 | const ExecutionContext *exe_ctx, |
484 | FunctionNameRepresentation representation, |
485 | Stream &s) { |
486 | return false; |
487 | } |
488 | |
489 | void Language::GetExceptionResolverDescription(bool catch_on, bool throw_on, |
490 | Stream &s) { |
491 | GetDefaultExceptionResolverDescription(catch_on, throw_on, s); |
492 | } |
493 | |
494 | void Language::GetDefaultExceptionResolverDescription(bool catch_on, |
495 | bool throw_on, |
496 | Stream &s) { |
497 | s.Printf(format: "Exception breakpoint (catch: %s throw: %s)" , |
498 | catch_on ? "on" : "off" , throw_on ? "on" : "off" ); |
499 | } |
500 | // Constructor |
501 | Language::Language() = default; |
502 | |
503 | // Destructor |
504 | Language::~Language() = default; |
505 | |