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