492 lines · cpp
1//===-- OptionValueProperties.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Interpreter/OptionValueProperties.h"10 11#include "lldb/Utility/Flags.h"12 13#include "lldb/Core/UserSettingsController.h"14#include "lldb/Interpreter/OptionValues.h"15#include "lldb/Interpreter/Property.h"16#include "lldb/Utility/Args.h"17#include "lldb/Utility/Stream.h"18#include "lldb/Utility/StringList.h"19 20using namespace lldb;21using namespace lldb_private;22 23OptionValueProperties::OptionValueProperties(llvm::StringRef name)24 : m_name(name.str()) {}25 26void OptionValueProperties::Initialize(const PropertyDefinitions &defs) {27 for (const auto &definition : defs) {28 Property property(definition);29 assert(property.IsValid());30 m_name_to_index.insert({property.GetName(), m_properties.size()});31 property.GetValue()->SetParent(shared_from_this());32 m_properties.push_back(property);33 }34}35 36void OptionValueProperties::SetValueChangedCallback(37 size_t property_idx, std::function<void()> callback) {38 Property *property = ProtectedGetPropertyAtIndex(property_idx);39 if (property)40 property->SetValueChangedCallback(std::move(callback));41}42 43void OptionValueProperties::AppendProperty(llvm::StringRef name,44 llvm::StringRef desc, bool is_global,45 const OptionValueSP &value_sp) {46 Property property(name, desc, is_global, value_sp);47 m_name_to_index.insert({name, m_properties.size()});48 m_properties.push_back(property);49 value_sp->SetParent(shared_from_this());50}51 52lldb::OptionValueSP53OptionValueProperties::GetValueForKey(const ExecutionContext *exe_ctx,54 llvm::StringRef key) const {55 auto iter = m_name_to_index.find(key);56 if (iter == m_name_to_index.end())57 return OptionValueSP();58 const size_t idx = iter->second;59 if (idx >= m_properties.size())60 return OptionValueSP();61 return GetPropertyAtIndex(idx, exe_ctx)->GetValue();62}63 64lldb::OptionValueSP65OptionValueProperties::GetSubValue(const ExecutionContext *exe_ctx,66 llvm::StringRef name, Status &error) const {67 lldb::OptionValueSP value_sp;68 if (name.empty())69 return OptionValueSP();70 71 llvm::StringRef sub_name;72 llvm::StringRef key;73 size_t key_len = name.find_first_of(".[{");74 if (key_len != llvm::StringRef::npos) {75 key = name.take_front(key_len);76 sub_name = name.drop_front(key_len);77 } else78 key = name;79 80 value_sp = GetValueForKey(exe_ctx, key);81 if (sub_name.empty() || !value_sp)82 return value_sp;83 84 switch (sub_name[0]) {85 case '.': {86 lldb::OptionValueSP return_val_sp;87 return_val_sp =88 value_sp->GetSubValue(exe_ctx, sub_name.drop_front(), error);89 if (!return_val_sp) {90 if (Properties::IsSettingExperimental(sub_name.drop_front())) {91 const size_t experimental_len =92 Properties::GetExperimentalSettingsName().size();93 if (sub_name[experimental_len + 1] == '.')94 return_val_sp = value_sp->GetSubValue(95 exe_ctx, sub_name.drop_front(experimental_len + 2), error);96 // It isn't an error if an experimental setting is not present.97 if (!return_val_sp)98 error.Clear();99 }100 }101 return return_val_sp;102 }103 case '[':104 // Array or dictionary access for subvalues like: "[12]" -- access105 // 12th array element "['hello']" -- dictionary access of key named hello106 return value_sp->GetSubValue(exe_ctx, sub_name, error);107 108 default:109 value_sp.reset();110 break;111 }112 return value_sp;113}114 115Status OptionValueProperties::SetSubValue(const ExecutionContext *exe_ctx,116 VarSetOperationType op,117 llvm::StringRef name,118 llvm::StringRef value) {119 Status error;120 llvm::SmallVector<llvm::StringRef, 8> components;121 name.split(components, '.');122 bool name_contains_experimental = false;123 for (const auto &part : components)124 if (Properties::IsSettingExperimental(part))125 name_contains_experimental = true;126 127 lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, name, error));128 if (value_sp)129 error = value_sp->SetValueFromString(value, op);130 else {131 // Don't set an error if the path contained .experimental. - those are132 // allowed to be missing and should silently fail.133 if (!name_contains_experimental && error.AsCString() == nullptr) {134 error = Status::FromErrorStringWithFormat("invalid value path '%s'",135 name.str().c_str());136 }137 }138 return error;139}140 141size_t OptionValueProperties::GetPropertyIndex(llvm::StringRef name) const {142 auto iter = m_name_to_index.find(name);143 if (iter == m_name_to_index.end())144 return SIZE_MAX;145 return iter->second;146}147 148const Property *149OptionValueProperties::GetProperty(llvm::StringRef name,150 const ExecutionContext *exe_ctx) const {151 auto iter = m_name_to_index.find(name);152 if (iter == m_name_to_index.end())153 return nullptr;154 return GetPropertyAtIndex(iter->second, exe_ctx);155}156 157lldb::OptionValueSP OptionValueProperties::GetPropertyValueAtIndex(158 size_t idx, const ExecutionContext *exe_ctx) const {159 const Property *setting = GetPropertyAtIndex(idx, exe_ctx);160 if (setting)161 return setting->GetValue();162 return OptionValueSP();163}164 165OptionValuePathMappings *166OptionValueProperties::GetPropertyAtIndexAsOptionValuePathMappings(167 size_t idx, const ExecutionContext *exe_ctx) const {168 OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx));169 if (value_sp)170 return value_sp->GetAsPathMappings();171 return nullptr;172}173 174OptionValueFileSpecList *175OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpecList(176 size_t idx, const ExecutionContext *exe_ctx) const {177 OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx));178 if (value_sp)179 return value_sp->GetAsFileSpecList();180 return nullptr;181}182 183bool OptionValueProperties::GetPropertyAtIndexAsArgs(184 size_t idx, Args &args, const ExecutionContext *exe_ctx) const {185 const Property *property = GetPropertyAtIndex(idx, exe_ctx);186 if (!property)187 return false;188 189 OptionValue *value = property->GetValue().get();190 if (!value)191 return false;192 193 const OptionValueArgs *arguments = value->GetAsArgs();194 if (arguments) {195 arguments->GetArgs(args);196 return true;197 }198 199 const OptionValueArray *array = value->GetAsArray();200 if (array) {201 array->GetArgs(args);202 return true;203 }204 205 const OptionValueDictionary *dict = value->GetAsDictionary();206 if (dict) {207 dict->GetArgs(args);208 return true;209 }210 211 return false;212}213 214bool OptionValueProperties::SetPropertyAtIndexFromArgs(215 size_t idx, const Args &args, const ExecutionContext *exe_ctx) {216 const Property *property = GetPropertyAtIndex(idx, exe_ctx);217 if (!property)218 return false;219 220 OptionValue *value = property->GetValue().get();221 if (!value)222 return false;223 224 OptionValueArgs *arguments = value->GetAsArgs();225 if (arguments)226 return arguments->SetArgs(args, eVarSetOperationAssign).Success();227 228 OptionValueArray *array = value->GetAsArray();229 if (array)230 return array->SetArgs(args, eVarSetOperationAssign).Success();231 232 OptionValueDictionary *dict = value->GetAsDictionary();233 if (dict)234 return dict->SetArgs(args, eVarSetOperationAssign).Success();235 236 return false;237}238 239OptionValueDictionary *240OptionValueProperties::GetPropertyAtIndexAsOptionValueDictionary(241 size_t idx, const ExecutionContext *exe_ctx) const {242 const Property *property = GetPropertyAtIndex(idx, exe_ctx);243 if (property)244 return property->GetValue()->GetAsDictionary();245 return nullptr;246}247 248OptionValueFileSpec *249OptionValueProperties::GetPropertyAtIndexAsOptionValueFileSpec(250 size_t idx, const ExecutionContext *exe_ctx) const {251 const Property *property = GetPropertyAtIndex(idx, exe_ctx);252 if (property) {253 OptionValue *value = property->GetValue().get();254 if (value)255 return value->GetAsFileSpec();256 }257 return nullptr;258}259 260OptionValueSInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueSInt64(261 size_t idx, const ExecutionContext *exe_ctx) const {262 const Property *property = GetPropertyAtIndex(idx, exe_ctx);263 if (property) {264 OptionValue *value = property->GetValue().get();265 if (value)266 return value->GetAsSInt64();267 }268 return nullptr;269}270 271OptionValueUInt64 *OptionValueProperties::GetPropertyAtIndexAsOptionValueUInt64(272 size_t idx, const ExecutionContext *exe_ctx) const {273 const Property *property = GetPropertyAtIndex(idx, exe_ctx);274 if (property) {275 OptionValue *value = property->GetValue().get();276 if (value)277 return value->GetAsUInt64();278 }279 return nullptr;280}281 282OptionValueString *OptionValueProperties::GetPropertyAtIndexAsOptionValueString(283 size_t idx, const ExecutionContext *exe_ctx) const {284 OptionValueSP value_sp(GetPropertyValueAtIndex(idx, exe_ctx));285 if (value_sp)286 return value_sp->GetAsString();287 return nullptr;288}289 290void OptionValueProperties::Clear() {291 const size_t num_properties = m_properties.size();292 for (size_t i = 0; i < num_properties; ++i)293 m_properties[i].GetValue()->Clear();294}295 296Status OptionValueProperties::SetValueFromString(llvm::StringRef value,297 VarSetOperationType op) {298 Status error;299 300 // Args args(value_cstr);301 // const size_t argc = args.GetArgumentCount();302 switch (op) {303 case eVarSetOperationClear:304 Clear();305 break;306 307 case eVarSetOperationReplace:308 case eVarSetOperationAssign:309 case eVarSetOperationRemove:310 case eVarSetOperationInsertBefore:311 case eVarSetOperationInsertAfter:312 case eVarSetOperationAppend:313 case eVarSetOperationInvalid:314 error = OptionValue::SetValueFromString(value, op);315 break;316 }317 318 return error;319}320 321void OptionValueProperties::DumpValue(const ExecutionContext *exe_ctx,322 Stream &strm, uint32_t dump_mask) {323 const size_t num_properties = m_properties.size();324 for (size_t i = 0; i < num_properties; ++i) {325 const Property *property = GetPropertyAtIndex(i, exe_ctx);326 if (property) {327 OptionValue *option_value = property->GetValue().get();328 assert(option_value);329 const bool transparent_value = option_value->ValueIsTransparent();330 property->Dump(exe_ctx, strm, dump_mask);331 if (!transparent_value)332 strm.EOL();333 }334 }335}336 337llvm::json::Value338OptionValueProperties::ToJSON(const ExecutionContext *exe_ctx) const {339 llvm::json::Object json_properties;340 const size_t num_properties = m_properties.size();341 for (size_t i = 0; i < num_properties; ++i) {342 const Property *property = GetPropertyAtIndex(i, exe_ctx);343 if (property) {344 OptionValue *option_value = property->GetValue().get();345 assert(option_value);346 json_properties.try_emplace(property->GetName(),347 option_value->ToJSON(exe_ctx));348 }349 }350 return json_properties;351}352 353Status OptionValueProperties::DumpPropertyValue(const ExecutionContext *exe_ctx,354 Stream &strm,355 llvm::StringRef property_path,356 uint32_t dump_mask,357 bool is_json) {358 Status error;359 lldb::OptionValueSP value_sp(GetSubValue(exe_ctx, property_path, error));360 if (value_sp) {361 if (!value_sp->ValueIsTransparent()) {362 if (dump_mask & eDumpOptionName)363 strm.PutCString(property_path);364 if (dump_mask & ~eDumpOptionName)365 strm.PutChar(' ');366 }367 if (is_json) {368 strm.Printf(369 "%s",370 llvm::formatv("{0:2}", value_sp->ToJSON(exe_ctx)).str().c_str());371 } else372 value_sp->DumpValue(exe_ctx, strm, dump_mask);373 }374 return error;375}376 377OptionValuePropertiesSP378OptionValueProperties::CreateLocalCopy(const Properties &global_properties) {379 auto global_props_sp = global_properties.GetValueProperties();380 lldbassert(global_props_sp);381 382 auto copy_sp = global_props_sp->DeepCopy(global_props_sp->GetParent());383 return std::static_pointer_cast<OptionValueProperties>(copy_sp);384}385 386OptionValueSP387OptionValueProperties::DeepCopy(const OptionValueSP &new_parent) const {388 auto copy_sp = OptionValue::DeepCopy(new_parent);389 // copy_sp->GetAsProperties cannot be used here as it doesn't work for derived390 // types that override GetType returning a different value.391 auto *props_value_ptr = static_cast<OptionValueProperties *>(copy_sp.get());392 lldbassert(props_value_ptr);393 394 for (auto &property : props_value_ptr->m_properties) {395 // Duplicate any values that are not global when constructing properties396 // from a global copy.397 if (!property.IsGlobal()) {398 auto value_sp = property.GetValue()->DeepCopy(copy_sp);399 property.SetOptionValue(value_sp);400 }401 }402 return copy_sp;403}404 405const Property *406OptionValueProperties::GetPropertyAtPath(const ExecutionContext *exe_ctx,407 llvm::StringRef name) const {408 if (name.empty())409 return nullptr;410 411 const Property *property = nullptr;412 llvm::StringRef sub_name;413 llvm::StringRef key;414 size_t key_len = name.find_first_of(".[{");415 416 if (key_len != llvm::StringRef::npos) {417 key = name.take_front(key_len);418 sub_name = name.drop_front(key_len);419 } else420 key = name;421 422 property = GetProperty(key, exe_ctx);423 if (sub_name.empty() || !property)424 return property;425 426 if (sub_name[0] == '.') {427 OptionValueProperties *sub_properties =428 property->GetValue()->GetAsProperties();429 if (sub_properties)430 return sub_properties->GetPropertyAtPath(exe_ctx, sub_name.drop_front());431 }432 return nullptr;433}434 435void OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,436 Stream &strm) const {437 size_t max_name_len = 0;438 const size_t num_properties = m_properties.size();439 for (size_t i = 0; i < num_properties; ++i) {440 const Property *property = ProtectedGetPropertyAtIndex(i);441 if (property)442 max_name_len = std::max<size_t>(property->GetName().size(), max_name_len);443 }444 for (size_t i = 0; i < num_properties; ++i) {445 const Property *property = ProtectedGetPropertyAtIndex(i);446 if (property)447 property->DumpDescription(interpreter, strm, max_name_len, false);448 }449}450 451void OptionValueProperties::Apropos(452 llvm::StringRef keyword,453 std::vector<const Property *> &matching_properties) const {454 const size_t num_properties = m_properties.size();455 StreamString strm;456 for (size_t i = 0; i < num_properties; ++i) {457 const Property *property = ProtectedGetPropertyAtIndex(i);458 if (property) {459 const OptionValueProperties *properties =460 property->GetValue()->GetAsProperties();461 if (properties) {462 properties->Apropos(keyword, matching_properties);463 } else {464 bool match = false;465 llvm::StringRef name = property->GetName();466 if (name.contains_insensitive(keyword))467 match = true;468 else {469 llvm::StringRef desc = property->GetDescription();470 if (desc.contains_insensitive(keyword))471 match = true;472 }473 if (match) {474 matching_properties.push_back(property);475 }476 }477 }478 }479}480 481lldb::OptionValuePropertiesSP482OptionValueProperties::GetSubProperty(const ExecutionContext *exe_ctx,483 llvm::StringRef name) {484 lldb::OptionValueSP option_value_sp(GetValueForKey(exe_ctx, name));485 if (option_value_sp) {486 OptionValueProperties *ov_properties = option_value_sp->GetAsProperties();487 if (ov_properties)488 return ov_properties->shared_from_this();489 }490 return lldb::OptionValuePropertiesSP();491}492