def sandbox(debugger, command, exe_ctx, result, internal_dict): """ Syntax: sandbox Examples: (lldb) sandbox This command is implemented in HMSandbox.py """ HMSandboxViewController.register() command_script = f''' UIViewController *vc = (UIViewController *)[[NSClassFromString(@"{HMSandboxViewController.gClassName}") alloc] init]; UINavigationController *nv = [[UINavigationController alloc] initWithRootViewController:vc]; ((void (*)(id, SEL, long)) objc_msgSend)((id)nv, @selector(setModalPresentationStyle:), 0); // UIModalPresentationFullScreen UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController; if ([rootVC presentedViewController]) {{ [[rootVC presentedViewController] presentViewController:nv animated:YES completion:nil]; }} else {{ [rootVC presentViewController:nv animated:YES completion:nil]; }} ''' HM.evaluateExpressionValue(command_script) HM.processContinue()
def hide() -> None: command_script = f''' Class progressHUDCls = (Class)objc_lookUpClass("{gClassName}"); (UIView *)[progressHUDCls performSelector:@selector(hideHUD)]; (void)[CATransaction flush]; ''' HM.evaluateExpressionValue(command_script)
def registerProtocol(): global gProtocolName if HM.existClass(gProtocolName): return # Register class HM.DPrint(f"Register {gProtocolName}...") classValue = HM.allocateClass(gProtocolName, "NSURLProtocol") HM.registerClass(classValue.GetValue()) # Add methods HM.DPrint(f"Add methods to {gProtocolName}...") canInitWithRequestIMPValue = makeCanInitWithRequestIMP() if not HM.judgeSBValueHasValue(canInitWithRequestIMPValue): return HM.addClassMethod(gProtocolName, "canInitWithRequest:", canInitWithRequestIMPValue.GetValue(), "B@:@") HM.DPrint(f"Register {gProtocolName} done!") # register NSURLProtocol registerClassExp = f"[NSURLProtocol registerClass:(Class){classValue.GetValue()}]" HM.evaluateExpressionValue(registerClassExp)
def inspect(debugger, command, exe_ctx, result, internal_dict): """ Syntax: inspect Examples: (lldb) inspect Summary: Inspect UIView. The "infoView" is based on https://github.com/QMUI/LookinServer This command is implemented in HMInspectView.py """ HMInspectViewController.register() command_script = f''' Class objClass = (Class)objc_lookUpClass("{HMInspectViewController.gClassName}"); if ((BOOL)[(Class)objClass respondsToSelector:@selector(start)]) {{ (void)[objClass performSelector:@selector(start)]; }} ''' HM.evaluateExpressionValue(command_script) HM.processContinue()
def pBundlePath(debugger, command, exe_ctx, result, internal_dict): """ Syntax: pbundlepath [--open] Options: --open/-o; open in Finder Examples: (lldb) pbundlepath (lldb) pbundlepath -o This command is implemented in HMFileCommands.py """ command_args = shlex.split(command) parser = generate_option_parser("pbundlepath") try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return bundlePathValue = HM.evaluateExpressionValue( '(NSString*)[[NSBundle mainBundle] bundlePath]') path = bundlePathValue.GetObjectDescription() HM.DPrint(path) if options.open: # Cannot open the bundle, so we open the directory where the bundle is located. directoryValue = HM.evaluateExpressionValue( f'(NSString *)[(NSString *){bundlePathValue.GetValue()} stringByDeletingLastPathComponent]' ) os.system('open ' + directoryValue.GetObjectDescription())
def properties(debugger, command, exe_ctx, result, internal_dict): """ Syntax: properties <className/classInstance> Examples: (lldb) properties UIViewController (lldb) properties [NSObject new] (lldb) expression -l objc -O -- [NSObject new] <NSObject: 0x60000372f760> (lldb) properties 0x60000372f760 This command is implemented in HMClassInfoCommands.py """ if len(command) == 0: HM.DPrint( "Requires a argument, Please enter \"help properties\" for help.") return value = HM.evaluateExpressionValue( expression= f'(NSString *)[{command} performSelector:NSSelectorFromString(@"_propertyDescription")]', printErrors=False) if HM.successOfSBError(value.GetError()): HM.DPrint(value.GetObjectDescription()) return clsPrefixesValue = HM.getClassPrefixes()[1] command_script = f''' Class inputClass = objc_lookUpClass("{command}"); if (inputClass == nil) {{ // Find prefixed class for (NSString *prefix in (NSMutableArray *){clsPrefixesValue.GetValue()}) {{ NSString *clsName = [prefix stringByAppendingString:@".{command}"]; inputClass = objc_lookUpClass((char *)[clsName UTF8String]); if (inputClass) {{ break; }} }} }} NSMutableString *result = [[NSMutableString alloc] init]; if (inputClass == nil) {{ [result appendString:@"Unable to resolve {command} or find {command} class, maybe {command} is not a subclass of NSObject\\n"]; }} else {{ if ((BOOL)[(Class)inputClass respondsToSelector:(SEL)NSSelectorFromString(@"_propertyDescription")]) {{ [result appendString:(NSString *)[inputClass performSelector:NSSelectorFromString(@"_propertyDescription")]]; }} else {{ [result appendString:@"{command} is not a subclass of NSObject"]; }} }} result; ''' result = HM.evaluateExpressionValue(command_script).GetObjectDescription() HM.DPrint(result)
def plifecycle(debugger, command, exe_ctx, result, internal_dict): """ Syntax: plifecycle [-i/--ignore_system_classes] Options: --ignore_system_classes/-i; Ignore the system generated UIViewController classes Notice: You should use plifecycle in symbolic breakpoint(UIViewController's life cycle) for easier control. This command can ignore the system generated UIViewController classes, Otherwise you may use the following command directly. Method A: expression -l objc -O -- [[$arg1 description] stringByAppendingString:@" dealloc/viewDidAppear:/..."] Method B: expression -l objc -O -- @import UIKit; [[NSString alloc] initWithFormat:@"%@ %s", (id)$arg1, (char *)$arg2] Method C: Add symbolic breakpoint of "UIApplicationMain" with command "expression -l objc -O -- @import UIKit", then add symbolic breakpoint(UIViewController's life cycle) with command "expression -l objc -O -- [[NSString alloc] initWithFormat:@"%@ %s", (id)$arg1, (char *)$arg2]" This command is implemented in HMLifeCycle.py """ command_args = shlex.split(command) parser = generate_option_parser() try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return selfDescription = HM.evaluateExpressionValue( "(id)$arg1").GetObjectDescription() ignore = False if options.ignore_system_classes: ignoreClasses = [ "UIAlertController", "_UIAlertControllerTextFieldViewController", "UIInputWindowController", "UICompatibilityInputViewController", "UIKeyboardCandidateGridCollectionViewController", "UISystemKeyboardDockController", "_UIRemoteInputViewController", "UIApplicationRotationFollowingController", "UISystemInputAssistantViewController", "UIPredictionViewController", "UICandidateViewController", "_SFAppPasswordSavingViewController", "SFPasswordSavingRemoteViewController" ] for className in ignoreClasses: if className in selfDescription: ignore = True break if not ignore: selectorDescription = HM.evaluateExpressionValue( "(char *)$arg2").GetSummary().strip('"') HM.DPrint(selfDescription + ' ' + selectorDescription + '\n')
def redirect(debugger, command, exe_ctx, result, internal_dict): """ Syntax: redirect [--append] <stdout/stderr/both> <path> Options: --append/-a; Use "a+" mode instead of "w+" mode in freopen function Examples: (lldb) redirect both /dev/ttys000 (Simulator) (lldb) redirect stdout /path/to/file (lldb) redirect -a stderr /path/to/file This command is implemented in HMRedirectStdout.py """ command_args = shlex.split(command) parser = generate_option_parser() try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return argsLen = len(args) if argsLen != 2: HM.DPrint( "Error input, plase enter 'help redirect' for more infomation") return stream = args[0] path = args[1] mode = "w+" if options.append: mode = "a+" if stream == "stdout" or stream == "stderr": redirectValue = HM.evaluateExpressionValue( f"freopen(\"{path}\", \"{mode}\", {stream})") logRedirectResult(redirectValue, stream) elif stream == "both": stdoutValue = HM.evaluateExpressionValue( f"freopen(\"{path}\", \"{mode}\", stdout)") logRedirectResult(stdoutValue, "stdout") stderrValue = HM.evaluateExpressionValue( f"freopen(\"{path}\", \"{mode}\", stderr)") logRedirectResult(stderrValue, "stderr") else: HM.DPrint( f"Error input(\"{stream}\"), plase enter \"help redirect\" for more infomation" )
def getNavigationVC() -> Optional[str]: rootViewController = HM.evaluateExpressionValue("[[[UIApplication sharedApplication] keyWindow] rootViewController]").GetValue() if verifyObjIsKindOfClass(rootViewController, "UINavigationController"): return rootViewController elif verifyObjIsKindOfClass(rootViewController, "UITabBarController"): selectedViewController = HM.evaluateExpressionValue(f"[(UITabBarController *){rootViewController} selectedViewController]").GetValue() if verifyObjIsKindOfClass(selectedViewController, "UINavigationController"): return selectedViewController else: return None else: return None
def show(text: Optional[str]) -> None: register() command_script = f''' Class progressHUDCls = (Class)objc_lookUpClass("{gClassName}"); (UIView *)[progressHUDCls performSelector:@selector(showHUD)]; ''' if len(text) > 0: command_script += f'(void)[progressHUDCls performSelector:@selector(setText:) withObject:@"{text}"];' command_script += "(void)[CATransaction flush];" HM.evaluateExpressionValue(command_script)
def showFPS(debugger, command, exe_ctx, result, internal_dict): """ Syntax: showfps Examples: (lldb) showfps Notice: showfps is deprecated. Use showhud instead. This command is implemented in HMFPSLabel.py """ HM.DPrint("showfps is deprecated. Use showhud instead.") FPSClassName = "HMFPSLabel" if HM.existClass(FPSClassName): HM.DPrint("HMFPSLabel is already on display") return # Register class FPSLabelClassValue = HM.allocateClass(FPSClassName, "UILabel") HM.addIvar(FPSLabelClassValue.GetValue(), "_link", "CADisplayLink *") HM.addIvar(FPSLabelClassValue.GetValue(), "_count", "int") HM.addIvar(FPSLabelClassValue.GetValue(), "_lastTime", "double") HM.registerClass(FPSLabelClassValue.GetValue()) addToKeyWindowIMPValue = makeAddToKeyWindowIMP() if not HM.judgeSBValueHasValue(addToKeyWindowIMPValue): return HM.addClassMethod(FPSClassName, "addToKeyWindow", addToKeyWindowIMPValue.GetValue(), "@@:") tickIMPValue = makeTickIMP() if not HM.judgeSBValueHasValue(tickIMPValue): return HM.addInstanceMethod(FPSClassName, "tick:", tickIMPValue.GetValue(), "v@:@") # Show fps command addToKeyWindowCommand = ''' Class fps = NSClassFromString(@"HMFPSLabel"); (UILabel *)[fps performSelector:@selector(addToKeyWindow)]; ''' HM.evaluateExpressionValue(addToKeyWindowCommand) HM.DPrint("showfps Done!") HM.processContinue()
def environment(debugger, command, exe_ctx, result, internal_dict): """ Syntax: environment Examples: (lldb) environment This command is implemented in HMEnvironment.py """ # Python version # LLDB version # Target triple # Git commit hash # Optimized # Xcode version # Xcode build version # Model identifier # System version HM.DPrint('[Python version] ' + sys.version.replace('\n', '\n\t\t')) HM.DPrint('[LLDB version] ' + debugger.GetVersionString().replace('\n', '\n\t\t')) HM.DPrint('[Target triple] ' + debugger.GetSelectedTarget().GetTriple()) HM.DPrint('[Git commit hash] ' + getGitCommitHash()) HM.DPrint('[Optimized] ' + getOptimizedStr()) XcodeVersionValue = HM.evaluateExpressionValue( '(NSString *)([NSBundle mainBundle].infoDictionary[@"DTXcode"] ?: @"-")' ) HM.DPrint('[Xcode version] ' + XcodeVersionValue.GetObjectDescription()) XcodeBuildVersionValue = HM.evaluateExpressionValue( '(NSString *)([NSBundle mainBundle].infoDictionary[@"DTXcodeBuild"] ?: @"-")' ) HM.DPrint('[Xcode build version] ' + XcodeBuildVersionValue.GetObjectDescription()) HM.DPrint('[Model identifier] ' + getModelIdentifier()) SystemVersionValue = HM.evaluateExpressionValue( '(NSString *)[[NSString alloc] initWithFormat:@"%@ %@", [[UIDevice currentDevice] systemName], [[UIDevice currentDevice] systemVersion]]' ) HM.DPrint('[System version] ' + SystemVersionValue.GetObjectDescription())
def makeStartIMP() -> lldb.SBValue: command_script = f''' UIViewController * (^IMPBlock)(id) = ^UIViewController *(id classSelf) {{ UIViewController *vc = (UIViewController *)[[(Class)objc_lookUpClass("{gClassName}") alloc] init]; [vc setValue:[UIApplication sharedApplication].keyWindow forKey:@"_previousKeyWindow"]; UIWindow *window = (UIWindow *)[[(Class)objc_lookUpClass("{HMDebugWindow.gClassName}") alloc] init]; if ((BOOL)[[UIApplication sharedApplication] respondsToSelector:@selector(connectedScenes)]) {{ NSSet *scenes = [[UIApplication sharedApplication] connectedScenes]; // NSSet<UIScene *> *scenes for (id scene in scenes) {{ if ((long)[scene activationState] == 0 && (BOOL)[scene isKindOfClass:NSClassFromString(@"UIWindowScene")]) {{ // UISceneActivationStateForegroundActive (void)[window setWindowScene:scene]; break; }} }} }} (void)[window setFrame:(CGRect)UIScreen.mainScreen.bounds]; (void)[window setBackgroundColor:[UIColor clearColor]]; window.windowLevel = UIWindowLevelAlert - 1; window.tag = {gWindowTag}; window.rootViewController = vc; [window makeKeyAndVisible]; return vc; }}; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(command_script, prefix=HMExpressionPrefix.gPrefix)
def removeDebugHUD(debugger, command, exe_ctx, result, internal_dict) -> None: """ Syntax: removehud Examples: (lldb) removehud This command is implemented in HMDebugHUD.py """ global gClassName if not HM.existClass(gClassName): HM.DPrint(f"{gClassName} does not exist.") return command_script = f''' UIView *keyWindow = [UIApplication sharedApplication].keyWindow; Class HUDClass = (Class)objc_lookUpClass("{gClassName}"); UIView *objView = nil; for (UIView *subView in keyWindow.subviews) {{ if ([subView isKindOfClass:HUDClass]) {{ objView = subView; break; }} }} [objView removeFromSuperview]; objView; ''' val = HM.evaluateExpressionValue(command_script) if HM.judgeSBValueHasValue(val): HM.DPrint("remove done!") else: HM.DPrint(f"{gClassName} does not exist.")
def makeAddToKeyWindowIMP() -> lldb.SBValue: command_script = ''' UILabel * (^addToKeyWindowBlock)(id) = ^UILabel *(id classSelf) { UILabel *fpsLabel = (UILabel *)[[NSClassFromString(@"HMFPSLabel") alloc] init]; (void)[fpsLabel setFrame:(CGRect){60, [UIApplication sharedApplication].statusBarFrame.size.height, 58, 20}]; fpsLabel.layer.zPosition = 100; fpsLabel.layer.cornerRadius = 5; fpsLabel.clipsToBounds = YES; fpsLabel.textAlignment = (NSTextAlignment)1; fpsLabel.userInteractionEnabled = NO; (void)[fpsLabel setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.6]]; fpsLabel.font = [UIFont systemFontOfSize:14]; CADisplayLink *link = [CADisplayLink displayLinkWithTarget:fpsLabel selector:NSSelectorFromString(@"tick:")]; [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; [fpsLabel setValue:link forKey:@"_link"]; [[UIApplication sharedApplication].keyWindow addSubview:fpsLabel]; return fpsLabel; }; imp_implementationWithBlock(addToKeyWindowBlock); ''' return HM.evaluateExpressionValue(expression=command_script, prefix=HMExpressionPrefix.gPrefix)
def makeViewDidLoadIMP() -> lldb.SBValue: command_script = f''' void (^IMPBlock)(UIViewController *) = ^(UIViewController *vc) {{ Class cls = objc_lookUpClass("{gClassName}"); struct objc_super superInfo = {{ .receiver = vc, .super_class = (Class)class_getSuperclass((Class)cls) }}; ((void (*)(struct objc_super *, SEL))objc_msgSendSuper)(&superInfo, @selector(viewDidLoad)); // property initialize (void)[vc.view setBackgroundColor:[UIColor whiteColor]]; vc.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:vc action:@selector(dismissSelf)]; vc.navigationItem.title = @"Tool"; // tableView UITableView *tv = [[UITableView alloc] init]; tv.frame = vc.view.bounds; tv.delegate = (id)vc; tv.dataSource = (id)vc; tv.rowHeight = 50; tv.tableFooterView = [[UIView alloc] init]; if ([tv respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {{ // UIScrollViewContentInsetAdjustmentAutomatic ((void (*)(id, SEL, long)) objc_msgSend)((id)tv, @selector(setContentInsetAdjustmentBehavior:), 0); }} [vc.view addSubview:tv]; }}; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(command_script)
def makeFindSubviewAtPointInViewIMP() -> lldb.SBValue: command_script = ''' UIView * (^IMPBlock)(UIViewController *, CGPoint, UIView *) = ^UIView *(UIViewController *vc, CGPoint point, UIView *view) { NSArray *clsArr = @[[UITextField class], [UITextView class], [UIProgressView class], [UIActivityIndicatorView class], [UISlider class], [UISwitch class], [UIPageControl class], [UIStepper class]]; for (Class cls in clsArr) { if ([view isKindOfClass:cls]) { return view; } } UIView *targetView = nil; for (UIView *sView in view.subviews.reverseObjectEnumerator) { if ([sView isHidden] || sView.alpha <= 0.01) { continue; } if (CGRectContainsPoint(sView.frame, point)) { targetView = sView; break;; } } if (!targetView) { return view; } CGPoint tPoint = [targetView convertPoint:point fromView:view]; targetView = ((id (*)(id, SEL, CGPoint, id)) objc_msgSend)((id)vc, @selector(findSubviewAtPoint:inView:), tPoint, (id)targetView); return targetView; }; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(expression=command_script, prefix=HMExpressionPrefix.gPrefix)
def makeUpdateFPSIMP() -> lldb.SBValue: command_script = ''' void (^updateFPSBlock)(UIView *, int) = ^(UIView *HUD, int fps) { UIColor *valueColor = [UIColor whiteColor]; if (fps < 45) { valueColor = [[UIColor alloc] initWithRed:0.88 green:0.36 blue:0.36 alpha:1]; } else if (fps < 52) { valueColor = [[UIColor alloc] initWithRed:0.91 green:0.73 blue:0.45 alpha:1]; } UIFont *valueFont = [UIFont systemFontOfSize:12]; UIColor *unitColor = [UIColor whiteColor]; UIFont *unitFont = [UIFont systemFontOfSize:8]; NSMutableAttributedString *valueText = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%d", fps] attributes:@{(id)NSFontAttributeName: valueFont, (id)NSForegroundColorAttributeName: valueColor}]; NSAttributedString *unitText = [[NSAttributedString alloc] initWithString:@" FPS" attributes:@{(id)NSFontAttributeName: unitFont, (id)NSForegroundColorAttributeName: unitColor}]; [valueText appendAttributedString:unitText]; UILabel *fpsLab = [HUD valueForKey:@"_fpsLab"]; fpsLab.attributedText = valueText; }; imp_implementationWithBlock(updateFPSBlock); ''' return HM.evaluateExpressionValue(command_script)
def makeViewForHeaderInSectionIMP() -> lldb.SBValue: command_script = ''' UIView * (^IMPBlock)(UIViewController *, UITableView *, long) = ^UIView *(UIViewController *vc, UITableView *tv, long section) { UITableViewHeaderFooterView *header = [tv dequeueReusableHeaderFooterViewWithIdentifier:@"Header"]; if (header == nil) { header = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:@"Header"]; UIView *backgroundView = [[UIView alloc] init]; (void)[backgroundView setBackgroundColor:(UIColor *)[vc.view backgroundColor]]; [header setBackgroundView:backgroundView]; UILabel *titleLab = [[UILabel alloc] init]; titleLab.tag = 11111; titleLab.font = [UIFont systemFontOfSize:16]; titleLab.textColor = [UIColor grayColor]; titleLab.numberOfLines = 0; [header.contentView addSubview:titleLab]; titleLab.translatesAutoresizingMaskIntoConstraints = NO; [[NSLayoutConstraint constraintWithItem:titleLab attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:header.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:8] setActive:YES]; [[NSLayoutConstraint constraintWithItem:titleLab attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:header.contentView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:16] setActive:YES]; [[NSLayoutConstraint constraintWithItem:titleLab attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:header.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-8] setActive:YES]; [[NSLayoutConstraint constraintWithItem:titleLab attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:header.contentView attribute:NSLayoutAttributeRight multiplier:1.0 constant:-16] setActive:YES]; } UILabel *titleLab = (UILabel *)[header.contentView viewWithTag:11111]; NSString *currentPath = (NSString *)[vc valueForKey:@"_currentPath"]; titleLab.text = [currentPath stringByAbbreviatingWithTildeInPath]; return header; }; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(command_script)
def pHomeDirectory(debugger, command, exe_ctx, result, internal_dict): """ Syntax: phomedirectory [--open] Options: --open/-o; open in Finder Examples: (lldb) phomedirectory (lldb) phomedirectory -o This command is implemented in HMFileCommands.py """ command_args = shlex.split(command) parser = generate_option_parser("phomedirectory") try: # options: optparse.Values # args: list (options, args) = parser.parse_args(command_args) except: result.SetError(parser.usage) return homeDirectoryValue = HM.evaluateExpressionValue( '(NSString *)NSHomeDirectory()') path = homeDirectoryValue.GetObjectDescription() HM.DPrint(path) if options.open: os.system('open ' + path)
def makeLoadPathIMP() -> lldb.SBValue: command_script = ''' void (^IMPBlock)(UIViewController *, NSString *) = ^(UIViewController *vc, NSString *path) { [vc setValue:path forKey:@"_currentPath"]; if ([path isEqual:(NSString *)NSHomeDirectory()]) { vc.navigationItem.title = @"SandBox"; } else { vc.navigationItem.title = [path lastPathComponent]; } NSMutableArray *childPaths = [[NSMutableArray alloc] init]; // NSMutableArray<NSString *> *childPaths NSArray *subpaths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL]; // NSArray<NSString *> *subpaths for (NSString *subpath in subpaths) { [childPaths addObject:[path stringByAppendingPathComponent:subpath]]; } [vc setValue:childPaths forKey:@"_childPaths"]; UITableView *tableView = (UITableView *)[vc valueForKey:@"_tableView"]; if ([tableView respondsToSelector:@selector(adjustedContentInset)]) { [tableView setContentOffset:(CGPoint){0, -tableView.adjustedContentInset.top} animated:NO]; } else { [tableView setContentOffset:(CGPoint){0, -tableView.contentInset.top} animated:NO]; } [tableView reloadData]; }; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(expression=command_script, prefix=HMExpressionPrefix.gPrefix)
def makeCellForRowAtIndexPathIMP() -> lldb.SBValue: command_script = ''' UITableViewCell * (^IMPBlock)(UIViewController *, UITableView *, NSIndexPath *) = ^UITableViewCell *(UIViewController *vc, UITableView *tv, NSIndexPath *indexPath) { NSString * reuseIdentifier = @"Cell"; UITableViewCell *cell = [tv dequeueReusableCellWithIdentifier:reuseIdentifier]; if (cell == nil) { // UITableViewCellStyleDefault cell = [UITableViewCell alloc]; cell = ((UITableViewCell * (*)(id, SEL, long, id)) objc_msgSend)((id)cell, @selector(initWithStyle:reuseIdentifier:), 0, reuseIdentifier); // UITableViewCellSelectionStyleNone ((void (*)(id, SEL, long)) objc_msgSend)((id)cell, @selector(setSelectionStyle:), 0); // UITableViewCellAccessoryDisclosureIndicator ((void (*)(id, SEL, long)) objc_msgSend)((id)cell, @selector(setAccessoryType:), 1); } long row = indexPath.row; if (row == 0) { cell.textLabel.text = @"App and system information"; } else if (row == 1) { cell.textLabel.text = @"Sandbox"; } else if (row == 2) { cell.textLabel.text = @"Inspect view"; } return cell; }; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(command_script)
def makeViewDidLayoutSubviewsIMP() -> lldb.SBValue: command_script = f''' void (^IMPBlock)(UIViewController *) = ^(UIViewController *vc) {{ Class cls = objc_lookUpClass("{gClassName}"); struct objc_super superInfo = {{ .receiver = vc, .super_class = (Class)class_getSuperclass((Class)cls) }}; ((void (*)(struct objc_super *, SEL))objc_msgSendSuper)(&superInfo, @selector(viewDidLayoutSubviews)); UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero; if ([UIApplication.sharedApplication.keyWindow respondsToSelector:@selector(safeAreaInsets)]) {{ safeAreaInsets = [UIApplication.sharedApplication.keyWindow safeAreaInsets]; }} UIButton *_exitBtn = (UIButton *)[vc valueForKey:@"_exitBtn"]; CGSize exitBtnSize = (CGSize){{_exitBtn.intrinsicContentSize.width + 20, _exitBtn.intrinsicContentSize.height}}; CGFloat exitBtnY = vc.view.frame.size.height - safeAreaInsets.bottom - 10 - exitBtnSize.height; (void)[_exitBtn setFrame:(CGRect){{(vc.view.frame.size.width - exitBtnSize.width) / 2, exitBtnY, exitBtnSize.width, exitBtnSize.height}}]; _exitBtn.layer.cornerRadius = exitBtnSize.height / 2; }}; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(expression=command_script, prefix=HMExpressionPrefix.gPrefix)
def makeHandleTapRecognizerIMP() -> lldb.SBValue: command_script = ''' void (^IMPBlock)(UIViewController *, UITapGestureRecognizer *) = ^(UIViewController *vc, UITapGestureRecognizer *tapRecognizer) { // find targetView CGPoint point = [tapRecognizer locationInView:vc.view]; UIView *_targetView = nil; UIWindow *_previousKeyWindow = (UIWindow *)[vc valueForKey:@"_previousKeyWindow"]; for (UIWindow *window in [UIApplication sharedApplication].windows.reverseObjectEnumerator) { if (_targetView) { break; } if (window == vc.view.window) { continue; } if ([window isHidden]) { continue; } CGPoint wPoint = [window convertPoint:point fromWindow:vc.view.window]; if (window == _previousKeyWindow) { _targetView = ((id (*)(id, SEL, CGPoint, id)) objc_msgSend)((id)vc, @selector(findSubviewAtPoint:inView:), wPoint, (id)window); } else { _targetView = [window hitTest:wPoint withEvent:nil]; } } printf("\\n[HMLLDB]: %s\\n", (char *)[[_targetView description] UTF8String]); (void)[vc performSelector:@selector(refreshTargetView:) withObject:(id)_targetView]; }; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(expression=command_script, prefix=HMExpressionPrefix.gPrefix)
def makeDebugHUDtickIMP() -> lldb.SBValue: command_script = ''' void (^debugHUDtickBlock)(UIView *, CADisplayLink *) = ^(UIView *HUD, CADisplayLink *link) { NSNumber *countNum = [HUD valueForKey:@"_count"]; int count = [countNum intValue] + 1; [HUD setValue:@(count) forKey:@"_count"]; NSNumber *lastTimeNum = [HUD valueForKey:@"_lastTime"]; double delta = link.timestamp - [lastTimeNum doubleValue]; if (delta < 1) { return; } [HUD setValue:@(link.timestamp) forKey:@"_lastTime"]; [HUD setValue:@(0) forKey:@"_count"]; int fps = (int)((count / delta) + 0.5); (void)[HUD updateMemoryFootprint]; (void)[HUD updateCPUUtilization]; (void)[HUD updateFPS:fps]; }; imp_implementationWithBlock(debugHUDtickBlock); ''' return HM.evaluateExpressionValue(command_script)
def deleteAllFileInDirectory(directoryPath: str) -> None: command_script = f''' NSString *directoryPath = @"{directoryPath}"; NSMutableString *result = [[NSMutableString alloc] init]; NSFileManager *fileMgr = [NSFileManager defaultManager]; if ([fileMgr fileExistsAtPath:directoryPath]) {{ NSArray *subFileArray = [fileMgr contentsOfDirectoryAtPath:directoryPath error:nil]; for (NSString *subFileName in subFileArray) {{ NSString *subFilePath = [directoryPath stringByAppendingPathComponent:subFileName]; if ([fileMgr removeItemAtPath:subFilePath error:nil]) {{ [result appendFormat:@"removed file: %@\\n", subFilePath]; }} else {{ [result appendFormat:@"failed to remove file: %@\\n", subFilePath]; }} }} }} else {{ [result appendFormat:@"failed to remove non-existing file: %@\\n", directoryPath]; }} if ([result length] == 0) {{ [result appendString:@"There are no files in this directory.\\n"]; }} result; ''' result = HM.evaluateExpressionValue(command_script).GetObjectDescription() HM.DPrint(result)
def makeShowHUDIMP() -> lldb.SBValue: command_script = f''' UIView * (^IMPBlock)(id) = ^UIView *(id classSelf) {{ UIView *HUD = (UIView *)[(Class)objc_lookUpClass("{gClassName}") performSelector:@selector(sharedInstance)]; if ([HUD superview] == nil) {{ [[UIApplication sharedApplication].keyWindow addSubview:HUD]; }} else {{ [[HUD superview] bringSubviewToFront:HUD]; }} UIActivityIndicatorView *indicator = (UIActivityIndicatorView *)[HUD valueForKey:@"_indicator"]; [indicator setHidden:NO]; [indicator startAnimating]; [HUD setNeedsLayout]; [HUD layoutIfNeeded]; return HUD; }}; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(command_script)
def makeViewDidLoadIMP() -> lldb.SBValue: command_script = f''' void (^IMPBlock)(UIViewController *) = ^(UIViewController *vc) {{ Class cls = objc_lookUpClass("{gClassName}"); struct objc_super superInfo = {{ .receiver = vc, .super_class = (Class)class_getSuperclass((Class)cls) }}; ((void (*)(struct objc_super *, SEL))objc_msgSendSuper)(&superInfo, @selector(viewDidLoad)); // property initialize (void)[vc.view setBackgroundColor:[UIColor whiteColor]]; (void)[vc setExtendedLayoutIncludesOpaqueBars:YES]; (void)[vc setAutomaticallyAdjustsScrollViewInsets:YES]; if ([vc respondsToSelector:@selector(setOverrideUserInterfaceStyle:)]) {{ [vc setOverrideUserInterfaceStyle:UIUserInterfaceStyleLight]; }} }}; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(command_script)
def makeEditingStyleForRowAtIndexPathIMP() -> lldb.SBValue: command_script = ''' UITableViewCellEditingStyle (^IMPBlock)(UIViewController *, UITableView *, NSIndexPath *) = ^UITableViewCellEditingStyle(UIViewController *vc, UITableView *tv, NSIndexPath *indexPath) { return UITableViewCellEditingStyleDelete; }; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(command_script)
def makeCanEditRowAtIndexPathIMP() -> lldb.SBValue: command_script = ''' BOOL (^IMPBlock)(UIViewController *, UITableView *, NSIndexPath *) = ^BOOL(UIViewController *vc, UITableView *tv, NSIndexPath * indexPath) { return YES; }; imp_implementationWithBlock(IMPBlock); ''' return HM.evaluateExpressionValue(command_script)