diff --git a/README.md b/README.md index 15123a6..db093a2 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Then add the following line to your `~/.lldbinit` file. ```Python # ~/.lldbinit ... -command script import /usr/local/opt/chisel/libexec/fblldb.py +command script import /usr/local/opt/chisel/libexec/fbchisellldb.py ``` Alternatively, download chisel and add the following line to your _~/.lldbinit_ file. @@ -32,7 +32,7 @@ Alternatively, download chisel and add the following line to your _~/.lldbinit_ ```Python # ~/.lldbinit ... -command script import /path/to/fblldb.py +command script import /path/to/fbchisellldb.py ``` @@ -97,7 +97,7 @@ You can add local, custom commands. Here's a contrived example. # Example file with custom commands, located at /magical/commands/example.py import lldb -import fblldbbase as fb +import fbchisellldbbase as fb def lldbcommands(): return [ PrintKeyWindowLevel() ] @@ -115,13 +115,13 @@ class PrintKeyWindowLevel(fb.FBCommand): lldb.debugger.HandleCommand('p (CGFloat)[(id)[(id)[UIApplication sharedApplication] keyWindow] windowLevel]') ``` -Then all that's left is to source the commands in lldbinit. `Chisel` has a python function just for this, _loadCommandsInDirectory_ in the _fblldb.py_ module. +Then all that's left is to source the commands in lldbinit. `Chisel` has a python function just for this, _loadCommandsInDirectory_ in the _fbobjclldb.py_ module. ```Python # ~/.lldbinit ... -command script import /path/to/fblldb.py -script fblldb.loadCommandsInDirectory('/magical/commands/') +command script import /path/to/fbobjclldb.py +script fbobjclldb.loadCommandsInDirectory('/magical/commands/') ``` diff --git a/commands/FBAccessibilityCommands.py b/commands/FBAccessibilityCommands.py index c0b59a0..d32603d 100644 --- a/commands/FBAccessibilityCommands.py +++ b/commands/FBAccessibilityCommands.py @@ -8,8 +8,8 @@ import os import re -import fblldbbase as fb -import fblldbobjecthelpers as objHelpers +import fbchisellldbbase as fb +import fbchisellldbobjecthelpers as objHelpers # This is the key corresponding to accessibility label in diff --git a/commands/FBAutoLayoutCommands.py b/commands/FBAutoLayoutCommands.py index d2727d4..159ca48 100644 --- a/commands/FBAutoLayoutCommands.py +++ b/commands/FBAutoLayoutCommands.py @@ -6,8 +6,8 @@ # LICENSE file in the root directory of this source tree. -import fblldbbase as fb -import fblldbviewhelpers as viewHelpers +import fbchisellldbbase as fb +import fbchisellldbviewhelpers as viewHelpers import lldb diff --git a/commands/FBClassDump.py b/commands/FBClassDump.py index 38b9ebb..8d22345 100644 --- a/commands/FBClassDump.py +++ b/commands/FBClassDump.py @@ -7,8 +7,8 @@ import string -import fblldbbase as fb -import fblldbobjcruntimehelpers as runtimeHelpers +import fbchisellldbbase as fb +import fbchisellldbobjcruntimehelpers as runtimeHelpers def lldbcommands(): diff --git a/commands/FBComponentCommands.py b/commands/FBComponentCommands.py index 5fe3449..12224ab 100644 --- a/commands/FBComponentCommands.py +++ b/commands/FBComponentCommands.py @@ -5,8 +5,8 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -import fblldbbase as fb -import fblldbviewhelpers as viewHelpers +import fbchisellldbbase as fb +import fbchisellldbviewhelpers as viewHelpers def lldbcommands(): diff --git a/commands/FBCopyCommands.py b/commands/FBCopyCommands.py index e36483a..0feac6b 100644 --- a/commands/FBCopyCommands.py +++ b/commands/FBCopyCommands.py @@ -11,8 +11,8 @@ import lldb import errno -import fblldbbase as fb -import fblldbobjecthelpers as objectHelpers +import fbchisellldbbase as fb +import fbchisellldbobjecthelpers as objectHelpers def lldbcommands(): diff --git a/commands/FBDebugCommands.py b/commands/FBDebugCommands.py index 7568d69..70d7781 100644 --- a/commands/FBDebugCommands.py +++ b/commands/FBDebugCommands.py @@ -12,8 +12,8 @@ import re import sys -import fblldbbase as fb -import fblldbobjcruntimehelpers as objc +import fbchisellldbbase as fb +import fbchisellldbobjcruntimehelpers as objc import lldb diff --git a/commands/FBDelay.py b/commands/FBDelay.py index 4eb1642..27a165b 100644 --- a/commands/FBDelay.py +++ b/commands/FBDelay.py @@ -7,7 +7,7 @@ from threading import Timer -import fblldbbase as fb +import fbchisellldbbase as fb import lldb diff --git a/commands/FBDisplayCommands.py b/commands/FBDisplayCommands.py index 2529e68..e1713f9 100755 --- a/commands/FBDisplayCommands.py +++ b/commands/FBDisplayCommands.py @@ -5,10 +5,10 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -import fblldbbase as fb -import fblldbobjcruntimehelpers as runtimeHelpers -import fblldbviewcontrollerhelpers as viewControllerHelpers -import fblldbviewhelpers as viewHelpers +import fbchisellldbbase as fb +import fbchisellldbobjcruntimehelpers as runtimeHelpers +import fbchisellldbviewcontrollerhelpers as viewControllerHelpers +import fbchisellldbviewhelpers as viewHelpers import lldb diff --git a/commands/FBFindCommands.py b/commands/FBFindCommands.py index 5adf098..61f92a8 100644 --- a/commands/FBFindCommands.py +++ b/commands/FBFindCommands.py @@ -8,9 +8,9 @@ import os import re -import fblldbbase as fb -import fblldbobjcruntimehelpers as objc -import fblldbviewcontrollerhelpers as vcHelpers +import fbchisellldbbase as fb +import fbchisellldbobjcruntimehelpers as objc +import fbchisellldbviewcontrollerhelpers as vcHelpers import lldb diff --git a/commands/FBFlickerCommands.py b/commands/FBFlickerCommands.py index 9152e67..f11c221 100644 --- a/commands/FBFlickerCommands.py +++ b/commands/FBFlickerCommands.py @@ -8,9 +8,9 @@ import os import sys -import fblldbbase as fb -import fblldbobjcruntimehelpers as runtimeHelpers -import fblldbviewhelpers as viewHelpers +import fbchisellldbbase as fb +import fbchisellldbobjcruntimehelpers as runtimeHelpers +import fbchisellldbviewhelpers as viewHelpers import lldb diff --git a/commands/FBImportCommands.py b/commands/FBImportCommands.py index a1738a4..3e6b69b 100644 --- a/commands/FBImportCommands.py +++ b/commands/FBImportCommands.py @@ -5,7 +5,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -import fblldbbase as fb +import fbchisellldbbase as fb import lldb @@ -27,4 +27,4 @@ def run(self, arguments, options): .GetSelectedThread() .GetSelectedFrame() ) - fb.importModule(frame, "UIKit") + fb.importModule(frame, "UIKit") \ No newline at end of file diff --git a/commands/FBInvocationCommands.py b/commands/FBInvocationCommands.py index a0e032a..8883594 100644 --- a/commands/FBInvocationCommands.py +++ b/commands/FBInvocationCommands.py @@ -7,7 +7,7 @@ import re -import fblldbbase as fb +import fbchisellldbbase as fb import lldb diff --git a/commands/FBPrintCommands.py b/commands/FBPrintCommands.py index 8c24850..9029a5c 100755 --- a/commands/FBPrintCommands.py +++ b/commands/FBPrintCommands.py @@ -9,10 +9,10 @@ import re import subprocess -import fblldbbase as fb -import fblldbobjcruntimehelpers as runtimeHelpers -import fblldbviewcontrollerhelpers as vcHelpers -import fblldbviewhelpers as viewHelpers +import fbchisellldbbase as fb +import fbchisellldbobjcruntimehelpers as runtimeHelpers +import fbchisellldbviewcontrollerhelpers as vcHelpers +import fbchisellldbviewhelpers as viewHelpers import lldb diff --git a/commands/FBTextInputCommands.py b/commands/FBTextInputCommands.py index 031a6c5..17b7320 100644 --- a/commands/FBTextInputCommands.py +++ b/commands/FBTextInputCommands.py @@ -5,8 +5,8 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -import fblldbbase as fb -import fblldbviewhelpers as viewHelpers +import fbchisellldbbase as fb +import fbchisellldbviewhelpers as viewHelpers ACCESSIBILITY_ID = 0 diff --git a/commands/FBVisualizationCommands.py b/commands/FBVisualizationCommands.py index 565b547..2b773f3 100755 --- a/commands/FBVisualizationCommands.py +++ b/commands/FBVisualizationCommands.py @@ -9,8 +9,8 @@ import os import time -import fblldbbase as fb -import fblldbobjecthelpers as objectHelpers +import fbchisellldbbase as fb +import fbchisellldbobjecthelpers as objectHelpers import lldb diff --git a/commands/FBXCTestCommands.py b/commands/FBXCTestCommands.py index 9e6417b..d0d7c4d 100644 --- a/commands/FBXCTestCommands.py +++ b/commands/FBXCTestCommands.py @@ -7,7 +7,7 @@ import re -import fblldbbase as fb +import fbchisellldbbase as fb import lldb diff --git a/fbchisellldb.py b/fbchisellldb.py new file mode 100755 index 0000000..8a6b805 --- /dev/null +++ b/fbchisellldb.py @@ -0,0 +1,203 @@ +#!/usr/bin/python + +# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +import imp +import os +from optparse import OptionParser + +import lldb + + +def __lldb_init_module(debugger, dict): + filePath = os.path.realpath(__file__) + lldbHelperDir = os.path.dirname(filePath) + + commandsDirectory = os.path.join(lldbHelperDir, "commands") + loadCommandsInDirectory(commandsDirectory) + + +def loadCommandsInDirectory(commandsDirectory): + for file in os.listdir(commandsDirectory): + fileName, fileExtension = os.path.splitext(file) + if fileExtension == ".py": + module = imp.load_source(fileName, os.path.join(commandsDirectory, file)) + + if hasattr(module, "lldbinit"): + module.lldbinit() + + if hasattr(module, "lldbcommands"): + module._loadedFunctions = {} + for command in module.lldbcommands(): + loadCommand( + module, command, commandsDirectory, fileName, fileExtension + ) + + +def loadCommand(module, command, directory, filename, extension): + func = makeRunCommand(command, os.path.join(directory, filename + extension)) + name = command.name() + helpText = ( + command.description().strip().splitlines()[0] + ) # first line of description + + key = filename + "_" + name + + module._loadedFunctions[key] = func + + functionName = "__" + key + + lldb.debugger.HandleCommand( + "script " + + functionName + + " = sys.modules['" + + module.__name__ + + "']._loadedFunctions['" + + key + + "']" + ) + lldb.debugger.HandleCommand( + 'command script add --help "{help}" --function {function} {name}'.format( + help=helpText.replace('"', '\\"'), # escape quotes + function=functionName, + name=name, + ) + ) + + +def makeRunCommand(command, filename): + def runCommand(debugger, input, exe_ctx, result, _): + command.result = result + command.context = exe_ctx + splitInput = command.lex(input) + + # OptionParser will throw in the case where you want just one + # big long argument and no options and you enter something + # that starts with '-' in the argument. e.g.: + # somecommand -[SomeClass someSelector:] + # This solves that problem by prepending a '--' so that + # OptionParser does the right thing. + options = command.options() + if len(options) == 0: + if "--" not in splitInput: + splitInput.insert(0, "--") + + parser = optionParserForCommand(command) + (options, args) = parser.parse_args(splitInput) + + # When there are more args than the command has declared, assume + # the initial args form an expression and combine them into a single arg. + if len(args) > len(command.args()): + overhead = len(args) - len(command.args()) + head = args[: overhead + 1] # Take N+1 and reduce to 1. + args = [" ".join(head)] + args[-overhead:] + + if validateArgsForCommand(args, command): + command.run(args, options) + + runCommand.__doc__ = helpForCommand(command, filename) + return runCommand + + +def validateArgsForCommand(args, command): + if len(args) < len(command.args()): + defaultArgs = [arg.default for arg in command.args()] + defaultArgsToAppend = defaultArgs[len(args) :] + + index = len(args) + for defaultArg in defaultArgsToAppend: + if not defaultArg: + arg = command.args()[index] + print("Whoops! You are missing the <" + arg.argName + "> argument.") + print("\nUsage: " + usageForCommand(command)) + return + index += 1 + + args.extend(defaultArgsToAppend) + return True + + +def optionParserForCommand(command): + parser = OptionParser() + + for argument in command.options(): + if argument.boolean: + parser.add_option( + argument.shortName, + argument.longName, + dest=argument.argName, + help=argument.help, + action=("store_false" if argument.default else "store_true"), + ) + else: + parser.add_option( + argument.shortName, + argument.longName, + dest=argument.argName, + help=argument.help, + default=argument.default, + ) + + return parser + + +def helpForCommand(command, filename): + help = command.description() + + argSyntax = "" + optionSyntax = "" + + if command.args(): + help += "\n\nArguments:" + for arg in command.args(): + help += "\n <" + arg.argName + ">; " + if arg.argType: + help += "Type: " + arg.argType + "; " + help += arg.help + argSyntax += " <" + arg.argName + ">" + + if command.options(): + help += "\n\nOptions:" + for option in command.options(): + + if option.longName and option.shortName: + optionFlag = option.longName + "/" + option.shortName + elif option.longName: + optionFlag = option.longName + else: + optionFlag = option.shortName + + help += "\n " + optionFlag + " " + + if not option.boolean: + help += "<" + option.argName + ">; Type: " + option.argType + + help += "; " + option.help + + optionSyntax += " [{name}{arg}]".format( + name=(option.longName or option.shortName), + arg=("" if option.boolean else ("=" + option.argName)), + ) + + help += "\n\nSyntax: " + command.name() + optionSyntax + argSyntax + + help += "\n\nThis command is implemented as %s in %s." % ( + command.__class__.__name__, + filename, + ) + + return help + + +def usageForCommand(command): + usage = command.name() + for arg in command.args(): + if arg.default: + usage += " [" + arg.argName + "]" + else: + usage += " " + arg.argName + + return usage diff --git a/fblldbbase.py b/fbchisellldbbase.py similarity index 98% rename from fblldbbase.py rename to fbchisellldbbase.py index 56b23d0..d7adfb1 100755 --- a/fblldbbase.py +++ b/fbchisellldbbase.py @@ -205,7 +205,7 @@ def check_expr(expr): # must contain a RETURN marco and it will automatic transform the # Objective-C object to Python object # Example: -# >>> fblldbbase.evaluate('NSString *str = @"hello world"; RETURN(@{@"key": str});') # noqa B950 +# >>> fbchisellldbbase.evaluate('NSString *str = @"hello world"; RETURN(@{@"key": str});') # noqa B950 # {u'key': u'hello world'} def evaluate(expr): if not check_expr(expr): diff --git a/fblldbinputhelpers.py b/fbchisellldbinputhelpers.py similarity index 100% rename from fblldbinputhelpers.py rename to fbchisellldbinputhelpers.py diff --git a/fblldbobjcruntimehelpers.py b/fbchisellldbobjcruntimehelpers.py similarity index 99% rename from fblldbobjcruntimehelpers.py rename to fbchisellldbobjcruntimehelpers.py index c2b4905..ae89a52 100644 --- a/fblldbobjcruntimehelpers.py +++ b/fbchisellldbobjcruntimehelpers.py @@ -7,7 +7,7 @@ import re -import fblldbbase as fb +import fbchisellldbbase as fb import lldb diff --git a/fblldbobjecthelpers.py b/fbchisellldbobjecthelpers.py similarity index 94% rename from fblldbobjecthelpers.py rename to fbchisellldbobjecthelpers.py index e1a61c7..f8486c8 100755 --- a/fblldbobjecthelpers.py +++ b/fbchisellldbobjecthelpers.py @@ -5,7 +5,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -import fblldbbase as fb +import fbchisellldbbase as fb def isKindOfClass(obj, className): diff --git a/fblldbviewcontrollerhelpers.py b/fbchisellldbviewcontrollerhelpers.py similarity index 98% rename from fblldbviewcontrollerhelpers.py rename to fbchisellldbviewcontrollerhelpers.py index 2c38e84..d9a1c96 100755 --- a/fblldbviewcontrollerhelpers.py +++ b/fbchisellldbviewcontrollerhelpers.py @@ -5,8 +5,8 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -import fblldbbase as fb -import fblldbobjcruntimehelpers as runtimeHelpers +import fbchisellldbbase as fb +import fbchisellldbobjcruntimehelpers as runtimeHelpers def presentViewController(viewController): diff --git a/fblldbviewhelpers.py b/fbchisellldbviewhelpers.py similarity index 98% rename from fblldbviewhelpers.py rename to fbchisellldbviewhelpers.py index b143db7..3230c1c 100644 --- a/fblldbviewhelpers.py +++ b/fbchisellldbviewhelpers.py @@ -5,8 +5,8 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -import fblldbbase as fb -import fblldbobjcruntimehelpers as runtimeHelpers +import fbchisellldbbase as fb +import fbchisellldbobjcruntimehelpers as runtimeHelpers def flushCoreAnimationTransaction(): diff --git a/fblldb.py b/fblldb.py deleted file mode 100755 index 8a6b805..0000000 --- a/fblldb.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/python - -# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import imp -import os -from optparse import OptionParser - -import lldb - - -def __lldb_init_module(debugger, dict): - filePath = os.path.realpath(__file__) - lldbHelperDir = os.path.dirname(filePath) - - commandsDirectory = os.path.join(lldbHelperDir, "commands") - loadCommandsInDirectory(commandsDirectory) - - -def loadCommandsInDirectory(commandsDirectory): - for file in os.listdir(commandsDirectory): - fileName, fileExtension = os.path.splitext(file) - if fileExtension == ".py": - module = imp.load_source(fileName, os.path.join(commandsDirectory, file)) - - if hasattr(module, "lldbinit"): - module.lldbinit() - - if hasattr(module, "lldbcommands"): - module._loadedFunctions = {} - for command in module.lldbcommands(): - loadCommand( - module, command, commandsDirectory, fileName, fileExtension - ) - - -def loadCommand(module, command, directory, filename, extension): - func = makeRunCommand(command, os.path.join(directory, filename + extension)) - name = command.name() - helpText = ( - command.description().strip().splitlines()[0] - ) # first line of description - - key = filename + "_" + name - - module._loadedFunctions[key] = func - - functionName = "__" + key - - lldb.debugger.HandleCommand( - "script " - + functionName - + " = sys.modules['" - + module.__name__ - + "']._loadedFunctions['" - + key - + "']" - ) - lldb.debugger.HandleCommand( - 'command script add --help "{help}" --function {function} {name}'.format( - help=helpText.replace('"', '\\"'), # escape quotes - function=functionName, - name=name, - ) - ) - - -def makeRunCommand(command, filename): - def runCommand(debugger, input, exe_ctx, result, _): - command.result = result - command.context = exe_ctx - splitInput = command.lex(input) - - # OptionParser will throw in the case where you want just one - # big long argument and no options and you enter something - # that starts with '-' in the argument. e.g.: - # somecommand -[SomeClass someSelector:] - # This solves that problem by prepending a '--' so that - # OptionParser does the right thing. - options = command.options() - if len(options) == 0: - if "--" not in splitInput: - splitInput.insert(0, "--") - - parser = optionParserForCommand(command) - (options, args) = parser.parse_args(splitInput) - - # When there are more args than the command has declared, assume - # the initial args form an expression and combine them into a single arg. - if len(args) > len(command.args()): - overhead = len(args) - len(command.args()) - head = args[: overhead + 1] # Take N+1 and reduce to 1. - args = [" ".join(head)] + args[-overhead:] - - if validateArgsForCommand(args, command): - command.run(args, options) - - runCommand.__doc__ = helpForCommand(command, filename) - return runCommand - - -def validateArgsForCommand(args, command): - if len(args) < len(command.args()): - defaultArgs = [arg.default for arg in command.args()] - defaultArgsToAppend = defaultArgs[len(args) :] - - index = len(args) - for defaultArg in defaultArgsToAppend: - if not defaultArg: - arg = command.args()[index] - print("Whoops! You are missing the <" + arg.argName + "> argument.") - print("\nUsage: " + usageForCommand(command)) - return - index += 1 - - args.extend(defaultArgsToAppend) - return True - - -def optionParserForCommand(command): - parser = OptionParser() - - for argument in command.options(): - if argument.boolean: - parser.add_option( - argument.shortName, - argument.longName, - dest=argument.argName, - help=argument.help, - action=("store_false" if argument.default else "store_true"), - ) - else: - parser.add_option( - argument.shortName, - argument.longName, - dest=argument.argName, - help=argument.help, - default=argument.default, - ) - - return parser - - -def helpForCommand(command, filename): - help = command.description() - - argSyntax = "" - optionSyntax = "" - - if command.args(): - help += "\n\nArguments:" - for arg in command.args(): - help += "\n <" + arg.argName + ">; " - if arg.argType: - help += "Type: " + arg.argType + "; " - help += arg.help - argSyntax += " <" + arg.argName + ">" - - if command.options(): - help += "\n\nOptions:" - for option in command.options(): - - if option.longName and option.shortName: - optionFlag = option.longName + "/" + option.shortName - elif option.longName: - optionFlag = option.longName - else: - optionFlag = option.shortName - - help += "\n " + optionFlag + " " - - if not option.boolean: - help += "<" + option.argName + ">; Type: " + option.argType - - help += "; " + option.help - - optionSyntax += " [{name}{arg}]".format( - name=(option.longName or option.shortName), - arg=("" if option.boolean else ("=" + option.argName)), - ) - - help += "\n\nSyntax: " + command.name() + optionSyntax + argSyntax - - help += "\n\nThis command is implemented as %s in %s." % ( - command.__class__.__name__, - filename, - ) - - return help - - -def usageForCommand(command): - usage = command.name() - for arg in command.args(): - if arg.default: - usage += " [" + arg.argName + "]" - else: - usage += " " + arg.argName - - return usage diff --git a/fblldb.py b/fblldb.py new file mode 120000 index 0000000..b730292 --- /dev/null +++ b/fblldb.py @@ -0,0 +1 @@ +fbchisellldb.py \ No newline at end of file