================ @@ -0,0 +1,313 @@ +""" +This module implements a couple of utility classes to make writing +lldb parsed commands more Pythonic. +The way to use it is to make a class for your command that inherits from ParsedCommandBase. +That will make an LLDBOVParser which you will use for your +option definition, and to fetch option values for the current invocation +of your command. Access to the OV parser is through: + +ParsedCommandBase.get_parser() + +Next, implement setup_command_definition() in your new command class, and call: + + self.get_parser().add_option() + +to add all your options. The order doesn't matter for options, lldb will sort them +alphabetically for you when it prints help. + +Similarly you can define the arguments with: + + self.get_parser().add_argument() + +At present, lldb doesn't do as much work as it should verifying arguments, it +only checks that commands that take no arguments don't get passed arguments. + +Then implement the execute function for your command as: + + def __call__(self, debugger, args_list, exe_ctx, result): + +The arguments will be a list of strings. + +You can access the option values using the 'dest' string you passed in when defining the option. + +If you need to know whether a given option was set by the user or not, you can +use the was_set API. + +There are example commands in the lldb testsuite at: + +llvm-project/lldb/test/API/commands/command/script/add/test_commands.py +""" +import inspect +import lldb +import sys +from abc import abstractmethod + +class LLDBOVParser: + def __init__(self): + # This is a dictionary of dictionaries. The key is the long option + # name, and the value is the rest of the definition. + self.options_dict = {} + self.args_array = [] + + # Some methods to translate common value types. Should return a + # tuple of the value and an error value (True => error) if the + # type can't be converted. + # FIXME: Need a way to push the conversion error string back to lldb. + @staticmethod + def to_bool(in_value): + error = True + value = False + if type(in_value) != str or len(in_value) == 0: + return (value, error) + + low_in = in_value.lower() + if low_in == "y" or low_in == "yes" or low_in == "t" or low_in == "true" or low_in == "1": + value = True + error = False + + if not value and low_in == "n" or low_in == "no" or low_in == "f" or low_in == "false" or low_in == "0": + value = False + error = False + + return (value, error) + + @staticmethod + def to_int(in_value): + #FIXME: Not doing errors yet... + return (int(in_value), False) + + @staticmethod + def to_unsigned(in_value): + # FIXME: find an unsigned converter... + # And handle errors. + return (int(in_value), False) + + translators = { + lldb.eArgTypeBoolean : to_bool, + lldb.eArgTypeBreakpointID : to_unsigned, + lldb.eArgTypeByteSize : to_unsigned, + lldb.eArgTypeCount : to_unsigned, + lldb.eArgTypeFrameIndex : to_unsigned, + lldb.eArgTypeIndex : to_unsigned, + lldb.eArgTypeLineNum : to_unsigned, + lldb.eArgTypeNumLines : to_unsigned, + lldb.eArgTypeNumberPerLine : to_unsigned, + lldb.eArgTypeOffset : to_int, + lldb.eArgTypeThreadIndex : to_unsigned, + lldb.eArgTypeUnsignedInteger : to_unsigned, + lldb.eArgTypeWatchpointID : to_unsigned, + lldb.eArgTypeColumnNum : to_unsigned, + lldb.eArgTypeRecognizerID : to_unsigned, + lldb.eArgTypeTargetID : to_unsigned, + lldb.eArgTypeStopHookID : to_unsigned + } + + @classmethod + def translate_value(cls, value_type, value): + try: + return cls.translators[value_type](value) + except KeyError: + # If we don't have a translator, return the string value. + return (value, False) + + # FIXME: would this be better done on the C++ side? + # The common completers are missing some useful ones. + # For instance there really should be a common Type completer + # And an "lldb command name" completer. + completion_table = { + lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion, + lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion, + lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion, + lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion, + lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion, + lldb.eArgTypeClassName : lldb.eSymbolCompletion, + lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion, + lldb.eArgTypeExpression : lldb.eVariablePathCompletion, + lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion, + lldb.eArgTypeFilename : lldb.eDiskFileCompletion, + lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion, + lldb.eArgTypeFunctionName : lldb.eSymbolCompletion, + lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion, + lldb.eArgTypeLanguage : lldb.eTypeLanguageCompletion, + lldb.eArgTypePath : lldb.eDiskFileCompletion, + lldb.eArgTypePid : lldb.eProcessIDCompletion, + lldb.eArgTypeProcessName : lldb.eProcessNameCompletion, + lldb.eArgTypeRegisterName : lldb.eRegisterCompletion, + lldb.eArgTypeRunArgs : lldb.eDiskFileCompletion, + lldb.eArgTypeShlibName : lldb.eModuleCompletion, + lldb.eArgTypeSourceFile : lldb.eSourceFileCompletion, + lldb.eArgTypeSymbol : lldb.eSymbolCompletion, + lldb.eArgTypeThreadIndex : lldb.eThreadIndexCompletion, + lldb.eArgTypeVarName : lldb.eVariablePathCompletion, + lldb.eArgTypePlatform : lldb.ePlatformPluginCompletion, + lldb.eArgTypeWatchpointID : lldb.eWatchpointIDCompletion, + lldb.eArgTypeWatchpointIDRange : lldb.eWatchpointIDCompletion, + lldb.eArgTypeModuleUUID : lldb.eModuleUUIDCompletion, + lldb.eArgTypeStopHookID : lldb.eStopHookIDCompletion + } + + @classmethod + def determine_completion(cls, arg_type): + try: + return cls.completion_table[arg_type] + except KeyError: + return lldb.eNoCompletion + + def get_option_element(self, long_name): + # Fixme: Is it worth making a long_option dict holding the rest of + # the options dict so this lookup is faster? + return self.options_dict.get(long_name, None) + + def option_parsing_started(self): + # This makes the ivars for all the "dest" values in the array and gives them + # their default values. + for key, elem in self.options_dict.items(): + elem['_value_set'] = False + try: + object.__setattr__(self, elem["dest"], elem["default"]) + except AttributeError: + # It isn't an error not to have a target, you'll just have to set and + # get this option value on your own. ---------------- jimingham wrote:
Right, I think I made the comment a little clearer. I don't want to go too much into this right now. One way to extend these commands for fancier option setting is to override `set_option_value` and handle your special options first, then delegate the others to the base class method. But I think it would be more convenient to add a way to do custom setters. Anyway, I'm not decided on that yet. https://github.com/llvm/llvm-project/pull/70734 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits