Compare commits

..

32 Commits

Author SHA1 Message Date
Tanner Bennett b908d66b94 Argument input view work 2020-10-18 17:09:47 -05:00
Tanner Bennett e6c324d761 FLEXMethod.imagePath should use the method's IMP
We can already tell which image the class comes from, and that is usually the same as the original method implementation. What is more useful to know is the image of the _current_ implementation.
2020-10-18 17:04:44 -05:00
Tanner Bennett 784e030e44 Silence new weird documentation warning… 2020-10-18 17:04:44 -05:00
Tanner Bennett 046eb49f7b Commit FLEXTests scheme 2020-10-18 17:04:44 -05:00
Tanner Bennett 6ec1814f2e Xcode 12 upgrade check for main scheme 2020-10-18 17:04:44 -05:00
Tanner Bennett f179545972 Add FLEX_EXIT_IF_NO_CTORS 2020-10-18 17:02:09 -05:00
Justin Lam d0d1632ca1 Add foundation header to ActivityStreamSPI. (#461)
* Add C++ ifdefs around C++ code.

The purpose of this change is to gate C++ code behind a cplusplus define so that FLEX can be made into a module for Swift code.  Swift modules do not allow for C++ code.  However this means if FLEX is used a Swift module, it will not have functionality of the excluded code. Ideally in the future, FLEX can be converted fully to support Swift without bridging headers.

* Revert "Add C++ ifdefs around C++ code."

This reverts commit 77c02207f9.

* Add include <Foundation/Foundation.h> to ActivityStreamSPI header

We use a different build setup internally, and we need all headers to be able to stand on their own. This file is using NS_ENUM, which is defined in Foundation, without importing it, so we're adding the import to resolve that.
2020-10-13 14:31:31 -05:00
matrush 79e22cf828 Remove a retain cylce in FLEXSystemLogViewController 2020-10-13 14:31:31 -05:00
Iulian Onofrei 563cb514a1 Fix crash when opening Keychain item without a password 2020-10-13 14:31:31 -05:00
matrush 84131d8533 Return empty array when the rows are nil in FLEXSQLResult 2020-10-13 14:31:31 -05:00
Evan Emelga 55cb7ebf8f Sort realm table names alphabetically (#453) 2020-10-12 23:07:18 -05:00
Anıl Taşkıran b9e2c1ebd9 fix baseResumeClass name for iOS14+ 2020-10-12 23:07:18 -05:00
Maxime Ollivier 7377399673 Skip keyboard shortcut override when setting defaults 2020-10-12 23:07:18 -05:00
Tanner Bennett 21199b2a56 Add sharedApplication property at runtime for iOS 9 2020-10-12 23:07:18 -05:00
matrush e72c6aa349 Add better debugging message for FLEXProperty and FLEXIvar 2020-10-12 23:06:55 -05:00
ph661 13c583d32b Add SceneKit to podspec frameworks 2020-10-12 19:19:34 -05:00
Chaoshuai Lü 129c291469 Move custom additions to the top (#439)
- Move custom additions to the top
- Fix header
2020-10-12 19:19:34 -05:00
Chris Ellsworth 67609b28a4 Add sorting in File Browser 2020-10-12 19:17:58 -05:00
matrush 4dc206eaa6 Add copy action for database browser row selection alert 2020-10-12 19:17:57 -05:00
matrush 28e91507db Remove retain cycles from selectionHandler in several files 2020-07-07 12:05:05 -05:00
matrush 5f74fb0d43 Remove redundant FLEXPointerIsTaggedPointer function 2020-07-07 12:04:34 -05:00
Tanner Bennett 09f5859feb Add InAppViewDebugger thanks to README 2020-06-30 16:25:12 -05:00
Hao Nguyen cee416889a use __typeof instead of typeof to compile for Cxx instead of GNUxx 2020-06-19 01:39:48 -05:00
Tanner Bennett e2a334384a Misc cleanup and bug fixes 2020-05-25 17:23:18 -05:00
Tanner Bennett a840e909a1 Add UIApplication shortcuts 2020-05-25 17:12:46 -05:00
Tanner Bennett 2f952c380f Automatically activate search bar in heap explorer 2020-04-27 17:53:34 -05:00
Tanner Bennett 5d919eb329 Don't access ivars on tagged pointers 2020-04-25 00:51:58 -05:00
Tanner Bennett 1d5d825135 Fix class properties not showing previews 2020-04-25 00:51:36 -05:00
opa334 2a8cdbdb84 Fix heap enumeration on arm64e 2020-04-23 22:04:41 -05:00
Tanner Bennett 5e2081b8f9 Various performance improvements
- Remove ARC from -[FLEXTypeEncodingParser canScanChar:]
- Make FLEXMethod.imagePath lazy
2020-04-23 22:04:41 -05:00
Bas Broek fbaeda1956 Remove parenthesis in Swift (#414) 2020-04-16 10:34:27 -05:00
Tanner Bennett c761865b9b Namespace all fishhook functions, fix #408
I know not all of them needed to be namespaced as the private functions are static, but I did it anyway for consistency.
2020-04-06 17:47:47 -05:00
65 changed files with 784 additions and 298 deletions
@@ -25,7 +25,7 @@
@property (nonatomic, copy) NSArray<FLEXTableViewSection *> *allSections;
/// This computed property should filter \c allSections for assignment to \c sections
@property (nonatomic, readonly) NSArray<FLEXTableViewSection *> *nonemptySections;
@property (nonatomic, readonly, copy) NSArray<FLEXTableViewSection *> *nonemptySections;
/// This should be able to re-initialize \c allSections
- (NSArray<FLEXTableViewSection *> *)makeSections;
@@ -80,7 +80,7 @@
/// if using \c self as the \c filterDelegate, as is the default.
///
/// For example, the object explorer hides the description section when searching.
@property (nonatomic, readonly) NSArray<FLEXTableViewSection *> *nonemptySections;
@property (nonatomic, readonly, copy) NSArray<FLEXTableViewSection *> *nonemptySections;
/// If using \c self as the \c filterDelegate, as is the default,
/// subclasses should override to provide the sections for the table view.
@@ -89,7 +89,7 @@ extern CGFloat const kFLEXDebounceForExpensiveIO;
/// or it will not be respsected. Use this instead.
/// Defaults to NO.
@property (nonatomic) BOOL pinSearchBar;
/// By default, we will show the search bar's cancel button when
/// By default, we will show the search bar's cancel button when
/// search becomes active and hide it when search is dismissed.
///
/// Do not set the showsCancelButton property on the searchController's
@@ -102,11 +102,11 @@ extern CGFloat const kFLEXDebounceForExpensiveIO;
/// Otherwise, this is the selected index of the carousel, or NSNotFound if using neither.
@property (nonatomic) NSInteger selectedScope;
/// self.searchController.searchBar.text
@property (nonatomic, readonly) NSString *searchText;
@property (nonatomic, readonly, copy) NSString *searchText;
/// A totally optional delegate to forward search results updater calls to.
/// If a delegate is set, updateSearchResults: is not called on this view controller.
@property (nonatomic, weak ) id<FLEXSearchResultsUpdating> searchResultsUpdater;
/// If a delegate is set, updateSearchResults: is not called on this view controller.
@property (nonatomic, weak) id<FLEXSearchResultsUpdating> searchResultsUpdater;
/// self.view.window as a \c FLEXWindow
@property (nonatomic, readonly) FLEXWindow *window;
+1 -1
View File
@@ -30,7 +30,7 @@ NS_ASSUME_NONNULL_BEGIN
/// A title to be displayed for the custom section.
/// Subclasses may override or use the \c _title ivar.
@property (nonatomic, readonly, nullable) NSString *title;
@property (nonatomic, readonly, nullable, copy) NSString *title;
/// The number of rows in this section. Subclasses must override.
/// This should not change until \c filterText is changed or \c reloadData is called.
@property (nonatomic, readonly) NSInteger numberOfRows;
@@ -11,7 +11,7 @@
#import "FLEXRuntimeUtility.h"
#import "FLEXTypeEncodingParser.h"
@interface FLEXArgumentInputStructView ()
@interface FLEXArgumentInputStructView () <FLEXArgumentInputViewDelegate>
@property (nonatomic) NSArray<FLEXArgumentInputView *> *argumentInputViews;
@@ -30,8 +30,11 @@
NSUInteger fieldIndex,
NSUInteger fieldOffset) {
FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory argumentInputViewForTypeEncoding:fieldTypeEncoding];
FLEXArgumentInputView *inputView = [FLEXArgumentInputViewFactory
argumentInputViewForTypeEncoding:fieldTypeEncoding
];
inputView.targetSize = FLEXArgumentInputViewSizeSmall;
inputView.delegate = self;
if (fieldIndex < customTitles.count) {
inputView.title = customTitles[fieldIndex];
@@ -125,15 +128,45 @@
return boxedStruct;
}
- (BOOL)inputViewIsFirstResponder {
BOOL isFirstResponder = NO;
- (FLEXArgumentInputView *)firstResponderInputView {
for (FLEXArgumentInputView *inputView in self.argumentInputViews) {
if ([inputView inputViewIsFirstResponder]) {
isFirstResponder = YES;
break;
return inputView;
}
}
return isFirstResponder;
return nil;
}
- (BOOL)resignFirstResponder {
FLEXArgumentInputView *responder = [self firstResponderInputView];
if (responder) {
return [responder resignFirstResponder];
} else {
return [super resignFirstResponder];
}
}
#pragma mark - FLEXArgumentInputViewDelegate
- (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)inputView {
// Nothing to see here
}
- (void)argumentInputViewWantsNextAsFirstResponder:(FLEXArgumentInputView *)inputView {
if (self.argumentInputViews.lastObject == inputView) {
// If this is our last or only input view,
// notify the delegate or dismiss the keyboard
if (self.delegate) {
[self.delegate argumentInputViewWantsNextAsFirstResponder:self];
} else {
[inputView resignFirstResponder];
}
} else {
NSInteger idx = [self.argumentInputViews indexOfObject:inputView];
[self.argumentInputViews[idx+1] becomeFirstResponder];
}
}
@@ -8,11 +8,15 @@
#import "FLEXArgumentInputView.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLEXArgumentInputTextView : FLEXArgumentInputView <UITextViewDelegate>
// For subclass eyes only
@property (nonatomic, readonly) UITextView *inputTextView;
@property (nonatomic) NSString *inputPlaceholderText;
@property (nonatomic, nullable) NSString *inputPlaceholderText;
@end
NS_ASSUME_NONNULL_END
@@ -31,7 +31,7 @@
self.inputTextView.autocapitalizationType = UITextAutocapitalizationTypeNone;
self.inputTextView.autocorrectionType = UITextAutocorrectionTypeNo;
self.inputTextView.delegate = self;
self.inputTextView.inputAccessoryView = [self createToolBar];
self.inputAccessoryView = [self createToolBar];
if (@available(iOS 11, *)) {
[self.inputTextView.layer setValue:@YES forKey:@"continuousCorners"];
} else {
@@ -51,6 +51,14 @@
return self;
}
- (UIToolbar *)inputAccessoryView {
return (id)self.inputTextView.inputAccessoryView;
}
- (void)setInputAccessoryView:(UIToolbar *)inputAccessoryView {
self.inputTextView.inputAccessoryView = inputAccessoryView;
}
#pragma mark - Private
- (UIToolbar *)createToolBar {
@@ -62,7 +70,7 @@
];
UIBarButtonItem *pasteItem = [[UIBarButtonItem alloc]
initWithTitle:@"Paste" style:UIBarButtonItemStyleDone
target:self.inputTextView action:@selector(paste:)
target:self action:@selector(paste:)
];
UIBarButtonItem *doneItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
@@ -91,6 +99,16 @@
return self.placeholderLabel.text;
}
- (void)paste:(id)sender {
[self.inputTextView paste:sender];
if (self.delegate) {
[self.delegate argumentInputViewWantsNextAsFirstResponder:self];
} else {
[self.inputTextView resignFirstResponder];
}
}
#pragma mark - Superclass Overrides
@@ -98,6 +116,18 @@
return self.inputTextView.isFirstResponder;
}
- (BOOL)becomeFirstResponder {
return [self.inputTextView becomeFirstResponder];
}
- (BOOL)resignFirstResponder {
if (self.inputViewIsFirstResponder) {
return self.inputTextView.resignFirstResponder;
} else {
return [super resignFirstResponder];
}
}
#pragma mark - Layout and Sizing
@@ -19,6 +19,8 @@ typedef NS_ENUM(NSUInteger, FLEXArgumentInputViewSize) {
@protocol FLEXArgumentInputViewDelegate;
NS_ASSUME_NONNULL_BEGIN
@interface FLEXArgumentInputView : UIView
- (instancetype)initWithArgumentTypeEncoding:(const char *)typeEncoding;
@@ -31,23 +33,25 @@ typedef NS_ENUM(NSUInteger, FLEXArgumentInputViewSize) {
/// Primitive types and structs should/will be boxed in NSValue containers.
/// Concrete subclasses should override both the setter and getter for this property.
/// Subclasses can call super.inputValue to access a backing store for the value.
@property (nonatomic) id inputValue;
@property (nonatomic, nullable) id inputValue;
/// Setting this value to large will make some argument input views increase the size of their input field(s).
/// Useful to increase the use of space if there is only one input view on screen (i.e. for property and ivar editing).
@property (nonatomic) FLEXArgumentInputViewSize targetSize;
/// Users of the input view can get delegate callbacks for incremental changes in user input.
@property (nonatomic, weak) id <FLEXArgumentInputViewDelegate> delegate;
@property (nonatomic, weak, nullable) id <FLEXArgumentInputViewDelegate> delegate;
// Subclasses can override
@property (nonatomic, nullable) UIToolbar *inputAccessoryView;
/// If the input view has one or more text views, returns YES when one of them is focused.
@property (nonatomic, readonly) BOOL inputViewIsFirstResponder;
/// For subclasses to indicate that they can handle editing a field the give type and value.
/// Used by FLEXArgumentInputViewFactory to create appropriate input views.
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(id)value;
+ (BOOL)supportsObjCType:(const char *)type withCurrentValue:(nullable id)value;
// For subclass eyes only
@@ -59,6 +63,11 @@ typedef NS_ENUM(NSUInteger, FLEXArgumentInputViewSize) {
@protocol FLEXArgumentInputViewDelegate <NSObject>
//- (void)
- (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)argumentInputView;
- (void)argumentInputViewWantsNextAsFirstResponder:(FLEXArgumentInputView *)argumentInputView;
@end
NS_ASSUME_NONNULL_END
@@ -46,6 +46,7 @@
];
inputView.backgroundColor = self.view.backgroundColor;
inputView.inputValue = currentValue;
inputView.delegate = self;
self.fieldEditorView.argumentInputViews = @[inputView];
}
@@ -118,8 +118,8 @@
[self exploreObjectOrPopViewController:self.currentValue];
}
- (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)argumentInputView {
if ([argumentInputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
- (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)inputView {
if ([inputView isKindOfClass:[FLEXArgumentInputSwitchView class]]) {
[self actionButtonPressed:nil];
}
}
@@ -64,6 +64,7 @@
inputView.backgroundColor = self.view.backgroundColor;
inputView.title = methodComponent;
inputView.delegate = self;
[argumentInputViews addObject:inputView];
argumentIndex++;
}
@@ -6,19 +6,20 @@
// Copyright (c) 2020 Flipboard. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "FLEXArgumentInputView.h"
@class FLEXFieldEditorView;
@class FLEXArgumentInputView;
/// Provides a screen for editing or configuring one or more variables.
@interface FLEXVariableEditorViewController : UIViewController
@interface FLEXVariableEditorViewController : UIViewController <FLEXArgumentInputViewDelegate>
+ (instancetype)target:(id)target;
- (id)initWithTarget:(id)target;
// Convenience accessor since many subclasses only use one input view
@property (nonatomic, readonly) FLEXArgumentInputView *firstInputView;
// Also a convenience accessor
@property (nonatomic, readonly) NSArray<FLEXArgumentInputView *> *inputViews;
// For subclass use only.
@property (nonatomic, readonly) id target;
@@ -54,8 +54,8 @@
#pragma mark - UIViewController methods
- (void)keyboardDidShow:(NSNotification *)notification {
CGRect keyboardRectInWindow = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGSize keyboardSize = [self.view convertRect:keyboardRectInWindow fromView:nil].size;
CGRect keyboardRect = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGSize keyboardSize = [self.view convertRect:keyboardRect fromView:nil].size;
UIEdgeInsets scrollInsets = self.scrollView.contentInset;
scrollInsets.bottom = keyboardSize.height;
self.scrollView.contentInset = scrollInsets;
@@ -114,7 +114,11 @@
#pragma mark - Public
- (FLEXArgumentInputView *)firstInputView {
return [self.fieldEditorView argumentInputViews].firstObject;
return self.fieldEditorView.argumentInputViews.firstObject;
}
- (NSArray<FLEXArgumentInputView *> *)inputViews {
return self.fieldEditorView.argumentInputViews;
}
- (void)actionButtonPressed:(id)sender {
@@ -134,4 +138,20 @@
}
}
#pragma mark - FLEXArgumentInputViewDelegate
- (void)argumentInputViewValueDidChange:(FLEXArgumentInputView *)inputView {
// Subclasses might want to do something here but we don't
}
- (void)argumentInputViewWantsNextAsFirstResponder:(FLEXArgumentInputView *)inputView {
if (self.inputViews.lastObject == inputView) {
// If this is our last or only input view, dismiss the keyboard
[inputView resignFirstResponder];
} else {
NSInteger idx = [self.inputViews indexOfObject:inputView];
[self.inputViews[idx+1] becomeFirstResponder];
}
}
@end
@@ -68,9 +68,11 @@ static Class RLMRealmClass = nil;
- (NSArray<NSString *> *)queryAllTables {
// Map each schema to its name
return [self.realm.schema.objectSchema flex_mapped:^id(RLMObjectSchema *schema, NSUInteger idx) {
NSArray<NSString *> *tableNames = [self.realm.schema.objectSchema flex_mapped:^id(RLMObjectSchema *schema, NSUInteger idx) {
return schema.className ?: nil;
}];
return [tableNames sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
}
- (NSArray<NSString *> *)queryAllColumnsOfTable:(NSString *)tableName {
@@ -101,22 +101,22 @@ static NSString * const QUERY_TABLENAMES = @"SELECT name FROM sqlite_master WHER
- (NSArray<NSString *> *)queryAllTables {
return [[self executeStatement:QUERY_TABLENAMES].rows flex_mapped:^id(NSArray *table, NSUInteger idx) {
return table.firstObject;
}];
}] ?: @[];
}
- (NSArray<NSString *> *)queryAllColumnsOfTable:(NSString *)tableName {
NSString *sql = [NSString stringWithFormat:@"PRAGMA table_info('%@')",tableName];
FLEXSQLResult *results = [self executeStatement:sql];
return [results.keyedRows flex_mapped:^id(NSDictionary *column, NSUInteger idx) {
return column[@"name"];
}];
}] ?: @[];
}
- (NSArray<NSArray *> *)queryAllDataInTable:(NSString *)tableName {
return [self executeStatement:[@"SELECT * FROM "
stringByAppendingString:tableName
]].rows;
]].rows ?: @[];
}
- (FLEXSQLResult *)executeStatement:(NSString *)sql {
@@ -113,7 +113,11 @@
[FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title([@"Row " stringByAppendingString:@(row).stringValue]);
make.message([fields componentsJoinedByString:@"\n\n"]);
NSString *message = [fields componentsJoinedByString:@"\n\n"];
make.message(message);
make.button(@"Copy").handler(^(NSArray<NSString *> *strings) {
UIPasteboard.generalPasteboard.string = message;
});
make.button(@"Dismiss").cancelStyle();
} showFrom:self];
}
@@ -34,6 +34,7 @@ static const NSInteger kFLEXLiveObjectsSortBySizeIndex = 2;
[super viewDidLoad];
self.showsSearchBar = YES;
self.showSearchBarInitially = YES;
self.searchBarDebounceInterval = kFLEXDebounceInstant;
self.showsCarousel = YES;
self.carousel.items = @[@"A→Z", @"Count", @"Size"];
@@ -125,14 +125,14 @@
}
}
}];
NSArray<FLEXObjectRef *> *references = [FLEXObjectRef referencingAll:instances];
if (references.count == 1) {
return [FLEXObjectExplorerFactory
explorerViewControllerForObject:references.firstObject.object
];
}
FLEXObjectListViewController *controller = [[self alloc] initWithReferences:references];
controller.title = [NSString stringWithFormat:@"%@ (%lu)", className, (unsigned long)instances.count];
return controller;
@@ -145,7 +145,7 @@
controller.title = [NSString stringWithFormat:@"Subclasses of %@ (%lu)",
className, (unsigned long)classes.count
];
return controller;
}
@@ -158,7 +158,7 @@
SwiftObjectClass = NSClassFromString(@"Swift._SwiftObject");
}
});
NSMutableArray<FLEXObjectRef *> *instances = [NSMutableArray new];
[FLEXHeapEnumerator enumerateLiveObjectsUsingBlock:^(__unsafe_unretained id tryObject, __unsafe_unretained Class actualClass) {
// Get all the ivars on the object. Start with the class and and travel up the inheritance chain.
@@ -167,15 +167,15 @@
while (tryClass) {
unsigned int ivarCount = 0;
Ivar *ivars = class_copyIvarList(tryClass, &ivarCount);
for (unsigned int ivarIndex = 0; ivarIndex < ivarCount; ivarIndex++) {
Ivar ivar = ivars[ivarIndex];
NSString *typeEncoding = @(ivar_getTypeEncoding(ivar) ?: "");
if (typeEncoding.flex_typeIsObjectOrClass) {
ptrdiff_t offset = ivar_getOffset(ivar);
uintptr_t *fieldPointer = (__bridge void *)tryObject + offset;
if (*fieldPointer == (uintptr_t)(__bridge void *)object) {
NSString *ivarName = @(ivar_getName(ivar) ?: "???");
[instances addObject:[FLEXObjectRef referencing:tryObject ivar:ivarName]];
@@ -183,7 +183,7 @@
}
}
}
tryClass = class_getSuperclass(tryClass);
}
}];
@@ -206,7 +206,7 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.showsSearchBar = YES;
}
@@ -224,7 +224,7 @@
- (NSArray *)buildSections:(NSArray<NSString *> *)titles predicates:(NSArray<NSPredicate *> *)predicates {
NSParameterAssert(titles.count == predicates.count);
NSParameterAssert(titles); NSParameterAssert(predicates);
return [NSArray flex_forEachUpTo:titles.count map:^id(NSUInteger i) {
NSArray *rows = [self.references filteredArrayUsingPredicate:predicates[i]];
return [self makeSection:rows title:titles[i]];
@@ -241,18 +241,22 @@
if (ref.summary && [ref.summary localizedCaseInsensitiveContainsString:filterText]) {
return YES;
}
return [ref.reference localizedCaseInsensitiveContainsString:filterText];
}
];
__weak __typeof(self) weakSelf = self;
section.selectionHandler = ^(__kindof UIViewController *host, FLEXObjectRef *ref) {
[self.navigationController pushViewController:[
FLEXObjectExplorerFactory explorerViewControllerForObject:ref.object
] animated:YES];
__strong __typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.navigationController pushViewController:[
FLEXObjectExplorerFactory explorerViewControllerForObject:ref.object
] animated:YES];
}
};
section.customTitle = title;
section.customTitle = title;
return section;
}
@@ -18,6 +18,12 @@
@interface FLEXFileBrowserTableViewCell : UITableViewCell
@end
typedef NS_ENUM(NSUInteger, FLEXFileBrowserSortAttribute) {
FLEXFileBrowserSortAttributeNone = 0,
FLEXFileBrowserSortAttributeName,
FLEXFileBrowserSortAttributeCreationDate,
};
@interface FLEXFileBrowserController () <FLEXFileBrowserSearchOperationDelegate>
@property (nonatomic, copy) NSString *path;
@@ -27,6 +33,7 @@
@property (nonatomic) NSNumber *searchPathsSize;
@property (nonatomic) NSOperationQueue *operationQueue;
@property (nonatomic) UIDocumentInteractionController *documentController;
@property (nonatomic) FLEXFileBrowserSortAttribute sortAttribute;
@end
@@ -84,6 +91,39 @@
self.showsSearchBar = YES;
self.searchBarDebounceInterval = kFLEXDebounceForAsyncSearch;
[self addToolbarItems:@[
[[UIBarButtonItem alloc] initWithTitle:@"Sort"
style:UIBarButtonItemStylePlain
target:self
action:@selector(sortDidTouchUpInside:)]
]];
}
- (void)sortDidTouchUpInside:(UIBarButtonItem *)sortButton {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Sort"
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
[alertController addAction:[UIAlertAction actionWithTitle:@"None"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * _Nonnull action) {
[self sortWithAttribute:FLEXFileBrowserSortAttributeNone];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:@"Name"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[self sortWithAttribute:FLEXFileBrowserSortAttributeName];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:@"Creation Date"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[self sortWithAttribute:FLEXFileBrowserSortAttributeCreationDate];
}]];
[self presentViewController:alertController animated:YES completion:nil];
}
- (void)sortWithAttribute:(FLEXFileBrowserSortAttribute)attribute {
self.sortAttribute = attribute;
[self reloadDisplayedPaths];
}
#pragma mark - FLEXGlobalsEntry
@@ -321,7 +361,7 @@
#if FLEX_AT_LEAST_IOS13_SDK
- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point __IOS_AVAILABLE(13.0) {
__weak typeof(self) weakSelf = self;
__weak __typeof__(self) weakSelf = self;
return [UIContextMenuConfiguration configurationWithIdentifier:nil
previewProvider:nil
actionProvider:^UIMenu * _Nullable(NSArray<UIMenuElement *> * _Nonnull suggestedActions) {
@@ -452,6 +492,27 @@
for (NSString *subpath in subpaths) {
[childPaths addObject:[self.path stringByAppendingPathComponent:subpath]];
}
if (self.sortAttribute != FLEXFileBrowserSortAttributeNone) {
[childPaths sortUsingComparator:^NSComparisonResult(NSString *path1, NSString *path2) {
switch (self.sortAttribute) {
case FLEXFileBrowserSortAttributeNone:
// invalid state
return NSOrderedSame;
case FLEXFileBrowserSortAttributeName:
return [path1 compare:path2];
case FLEXFileBrowserSortAttributeCreationDate: {
NSDictionary<NSFileAttributeKey, id> *path1Attributes = [NSFileManager.defaultManager attributesOfItemAtPath:path1
error:NULL];
NSDictionary<NSFileAttributeKey, id> *path2Attributes = [NSFileManager.defaultManager attributesOfItemAtPath:path2
error:NULL];
NSDate *path1Date = path1Attributes[NSFileCreationDate];
NSDate *path2Date = path2Attributes[NSFileCreationDate];
return [path1Date compare:path2Date];
}
}
}];
}
self.childPaths = childPaths;
}
@@ -10,6 +10,7 @@
@protocol FLEXGlobalsTableViewControllerDelegate;
typedef NS_ENUM(NSUInteger, FLEXGlobalsSectionKind) {
FLEXGlobalsSectionCustom,
/// NSProcessInfo, Network history, system log,
/// heap, address explorer, libraries, app classes
FLEXGlobalsSectionProcessAndEvents,
@@ -19,7 +20,6 @@ typedef NS_ENUM(NSUInteger, FLEXGlobalsSectionKind) {
FLEXGlobalsSectionAppShortcuts,
/// UIPasteBoard.general, UIScreen, UIDevice
FLEXGlobalsSectionMisc,
FLEXGlobalsSectionCustom,
FLEXGlobalsSectionCount
};
@@ -39,14 +39,14 @@
+ (NSString *)globalsTitleForSection:(FLEXGlobalsSectionKind)section {
switch (section) {
case FLEXGlobalsSectionCustom:
return @"Custom Additions";
case FLEXGlobalsSectionProcessAndEvents:
return @"Process and Events";
case FLEXGlobalsSectionAppShortcuts:
return @"App Shortcuts";
case FLEXGlobalsSectionMisc:
return @"Miscellaneous";
case FLEXGlobalsSectionCustom:
return @"Custom Additions";
default:
@throw NSInternalInconsistencyException;
@@ -104,11 +104,11 @@
}
+ (NSArray<FLEXGlobalsSection *> *)defaultGlobalSections {
static NSArray<FLEXGlobalsSection *> *sections = nil;
static NSMutableArray<FLEXGlobalsSection *> *sections = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSArray *rowsBySection = @[
@[
NSDictionary<NSNumber *, NSArray<FLEXGlobalsEntry *> *> *rowsBySection = @{
@(FLEXGlobalsSectionProcessAndEvents) : @[
[self globalsEntryForRow:FLEXGlobalsRowNetworkHistory],
[self globalsEntryForRow:FLEXGlobalsRowSystemLog],
[self globalsEntryForRow:FLEXGlobalsRowProcessInfo],
@@ -116,7 +116,7 @@
[self globalsEntryForRow:FLEXGlobalsRowAddressInspector],
[self globalsEntryForRow:FLEXGlobalsRowBrowseRuntime],
],
@[ // FLEXGlobalsSectionAppShortcuts
@(FLEXGlobalsSectionAppShortcuts) : @[
[self globalsEntryForRow:FLEXGlobalsRowBrowseBundle],
[self globalsEntryForRow:FLEXGlobalsRowBrowseContainer],
[self globalsEntryForRow:FLEXGlobalsRowMainBundle],
@@ -128,7 +128,7 @@
[self globalsEntryForRow:FLEXGlobalsRowRootViewController],
[self globalsEntryForRow:FLEXGlobalsRowCookies],
],
@[ // FLEXGlobalsSectionMisc
@(FLEXGlobalsSectionMisc) : @[
[self globalsEntryForRow:FLEXGlobalsRowPasteboard],
[self globalsEntryForRow:FLEXGlobalsRowMainScreen],
[self globalsEntryForRow:FLEXGlobalsRowCurrentDevice],
@@ -144,12 +144,13 @@
[self globalsEntryForRow:FLEXGlobalsRowMainThread],
[self globalsEntryForRow:FLEXGlobalsRowOperationQueue],
]
];
sections = [NSArray flex_forEachUpTo:rowsBySection.count map:^FLEXGlobalsSection *(NSUInteger i) {
};
sections = [NSMutableArray array];
for (FLEXGlobalsSectionKind i = FLEXGlobalsSectionCustom + 1; i < FLEXGlobalsSectionCount; ++i) {
NSString *title = [self globalsTitleForSection:i];
return [FLEXGlobalsSection title:title rows:rowsBySection[i]];
}];
[sections addObject:[FLEXGlobalsSection title:title rows:rowsBySection[@(i)]]];
}
});
return sections;
@@ -180,8 +181,7 @@
}
- (NSArray<FLEXGlobalsSection *> *)makeSections {
NSArray *sections = [self.class defaultGlobalSections];
NSMutableArray<FLEXGlobalsSection *> *sections = [NSMutableArray array];
// Do we have custom sections to add?
if (FLEXManager.sharedManager.userGlobalEntries.count) {
NSString *title = [[self class] globalsTitleForSection:FLEXGlobalsSectionCustom];
@@ -189,9 +189,11 @@
title:title
rows:FLEXManager.sharedManager.userGlobalEntries
];
sections = [sections arrayByAddingObject:custom];
[sections addObject:custom];
}
[sections addObjectsFromArray:[self.class defaultGlobalSections]];
return sections;
}
@@ -14,6 +14,8 @@
#ifndef ActivityStreamSPI_h
#define ActivityStreamSPI_h
#include <Foundation/Foundation.h>
#include <sys/time.h>
// #include <xpc/xpc.h>
@@ -46,7 +46,7 @@ static BOOL my_os_log_shim_enabled(void *addr) {
if (!NSUserDefaults.standardUserDefaults.flex_disableOSLog) {
return;
}
// Thanks to @Ram4096 on GitHub for telling me that
// os_log is conditionally enabled by the SDK version
void *addr = __builtin_return_address(0);
@@ -56,28 +56,28 @@ static BOOL my_os_log_shim_enabled(void *addr) {
return;
}
FLEXDidHookNSLog = rebind_symbols((struct rebinding[1]) {{
FLEXDidHookNSLog = flex_rebind_symbols((struct rebinding[1]) {{
"os_log_shim_enabled",
(void *)my_os_log_shim_enabled,
(void **)&orig_os_log_shim_enabled
}}, 1) == 0;
if (FLEXDidHookNSLog && orig_os_log_shim_enabled != nil) {
// Check if our rebinding worked
FLEXNSLogHookWorks = my_os_log_shim_enabled(addr) == NO;
}
// So, just because we rebind the lazily loaded symbol for
// this function doesn't mean it's even going to be used.
// While it seems to be sufficient for the simulator, for
// whatever reason it is not sufficient on-device. We need
// to actually hook the function with something like Substrate.
// Check if we have substrate, and if so use that instead
void *handle = dlopen("/usr/lib/libsubstrate.dylib", RTLD_LAZY);
if (handle) {
MSHookFunction = dlsym(handle, "MSHookFunction");
if (MSHookFunction) {
// Set the hook and check if it worked
void *unused;
@@ -96,16 +96,16 @@ static BOOL my_os_log_shim_enabled(void *addr) {
- (void)viewDidLoad {
[super viewDidLoad];
self.showsSearchBar = YES;
self.showSearchBarInitially = NO;
__weak __typeof(self) weakSelf = self;
id logHandler = ^(NSArray<FLEXSystemLogMessage *> *newMessages) {
__strong __typeof(weakSelf) self = weakSelf;
[self handleUpdateWithNewMessages:newMessages];
__strong __typeof(weakSelf) strongSelf = weakSelf;
[strongSelf handleUpdateWithNewMessages:newMessages];
};
if (FLEXOSLogAvailable() && !FLEXNSLogHookWorks) {
_logController = [FLEXOSLogController withUpdateHandler:logHandler];
} else {
@@ -114,9 +114,9 @@ static BOOL my_os_log_shim_enabled(void *addr) {
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.title = @"Waiting for Logs...";
// Toolbar buttons //
UIBarButtonItem *scrollDown = [UIBarButtonItem
itemWithImage:FLEXResources.scrollToBottomIcon
target:self
@@ -127,7 +127,7 @@ static BOOL my_os_log_shim_enabled(void *addr) {
target:self
action:@selector(showLogSettings)
];
[self addToolbarItems:@[scrollDown, settings]];
}
@@ -138,26 +138,30 @@ static BOOL my_os_log_shim_enabled(void *addr) {
}
- (NSArray<FLEXTableViewSection *> *)makeSections {
__weak __typeof(self) weakSelf = self;
_logMessages = [FLEXMutableListSection list:@[]
cellConfiguration:^(FLEXSystemLogCell *cell, FLEXSystemLogMessage *message, NSInteger row) {
cell.logMessage = message;
cell.highlightedText = self.filterText;
if (row % 2 == 0) {
cell.backgroundColor = FLEXColor.primaryBackgroundColor;
} else {
cell.backgroundColor = FLEXColor.secondaryBackgroundColor;
__strong __typeof(self) strongSelf = weakSelf;
if (strongSelf) {
cell.logMessage = message;
cell.highlightedText = strongSelf.filterText;
if (row % 2 == 0) {
cell.backgroundColor = FLEXColor.primaryBackgroundColor;
} else {
cell.backgroundColor = FLEXColor.secondaryBackgroundColor;
}
}
} filterMatcher:^BOOL(NSString *filterText, FLEXSystemLogMessage *message) {
NSString *displayedText = [FLEXSystemLogCell displayedTextForLogMessage:message];
return [displayedText localizedCaseInsensitiveContainsString:filterText];
}
];
self.logMessages.cellRegistrationMapping = @{
kFLEXSystemLogCellIdentifier : [FLEXSystemLogCell class]
};
return @[self.logMessages];
}
@@ -198,27 +202,27 @@ static BOOL my_os_log_shim_enabled(void *addr) {
NSString *aslToggle = disableOSLog ? @"Enable os_log (default)" : @"Disable os_log";
NSString *persistence = persistent ? @"Disable persistent logging" : @"Enable persistent logging";
NSString *title = @"System Log Settings";
NSString *body = @"In iOS 10 and up, ASL has been replaced by os_log. "
"The os_log API is much more limited. Below, you can opt-into the old behavior "
"if you want cleaner, more reliable logs within FLEX, but this will break "
"anything that expects os_log to be working, such as Console.app. "
"This setting requires the app to restart to take effect. \n\n"
"To get as close to the old behavior as possible with os_log enabled, logs must "
"be collected manually at launch and stored. This setting has no effect "
"on iOS 9 and below, or if os_log is disabled. "
"You should only enable persistent logging when you need it.";
FLEXOSLogController *logController = (FLEXOSLogController *)self.logController;
[FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title(title).message(body);
make.button(aslToggle).destructiveStyle().handler(^(NSArray<NSString *> *strings) {
[defaults toggleBoolForKey:kFLEXDefaultsDisableOSLogForceASLKey];
});
make.button(persistence).handler(^(NSArray<NSString *> *strings) {
[defaults toggleBoolForKey:kFLEXDefaultsiOSPersistentOSLogKey];
logController.persistent = !persistent;
+25 -15
View File
@@ -63,7 +63,7 @@
- (void)registerSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description {
#if TARGET_OS_SIMULATOR
[FLEXKeyboardShortcutManager.sharedManager registerSimulatorShortcutWithKey:key modifiers:modifiers action:action description:description];
[FLEXKeyboardShortcutManager.sharedManager registerSimulatorShortcutWithKey:key modifiers:modifiers action:action description:description allowOverride:YES];
#endif
}
@@ -81,37 +81,47 @@
#endif
}
#pragma mark - Shortcuts Defaults
- (void)registerDefaultSimulatorShortcutWithKey:(NSString *)key modifiers:(UIKeyModifierFlags)modifiers action:(dispatch_block_t)action description:(NSString *)description {
#if TARGET_OS_SIMULATOR
// Don't allow override to avoid changing keys registered by the app
[FLEXKeyboardShortcutManager.sharedManager registerSimulatorShortcutWithKey:key modifiers:modifiers action:action description:description allowOverride:NO];
#endif
}
- (void)registerDefaultSimulatorShortcuts {
[self registerSimulatorShortcutWithKey:@"f" modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:@"f" modifiers:0 action:^{
[self toggleExplorer];
} description:@"Toggle FLEX toolbar"];
[self registerSimulatorShortcutWithKey:@"g" modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:@"g" modifiers:0 action:^{
[self showExplorerIfNeeded];
[self.explorerViewController toggleMenuTool];
} description:@"Toggle FLEX globals menu"];
[self registerSimulatorShortcutWithKey:@"v" modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:@"v" modifiers:0 action:^{
[self showExplorerIfNeeded];
[self.explorerViewController toggleViewsTool];
} description:@"Toggle view hierarchy menu"];
[self registerSimulatorShortcutWithKey:@"s" modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:@"s" modifiers:0 action:^{
[self showExplorerIfNeeded];
[self.explorerViewController toggleSelectTool];
} description:@"Toggle select tool"];
[self registerSimulatorShortcutWithKey:@"m" modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:@"m" modifiers:0 action:^{
[self showExplorerIfNeeded];
[self.explorerViewController toggleMoveTool];
} description:@"Toggle move tool"];
[self registerSimulatorShortcutWithKey:@"n" modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:@"n" modifiers:0 action:^{
[self toggleTopViewControllerOfClass:[FLEXNetworkMITMViewController class]];
} description:@"Toggle network history view"];
// 't' is for testing: quickly present an object explorer for debugging
[self registerSimulatorShortcutWithKey:@"t" modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:@"t" modifiers:0 action:^{
[self showExplorerIfNeeded];
[self.explorerViewController toggleToolWithViewControllerProvider:^UINavigationController *{
@@ -121,25 +131,25 @@
} completion:nil];
} description:@"Present an object explorer for debugging"];
[self registerSimulatorShortcutWithKey:UIKeyInputDownArrow modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:UIKeyInputDownArrow modifiers:0 action:^{
if (self.isHidden || ![self.explorerViewController handleDownArrowKeyPressed]) {
[self tryScrollDown];
}
} description:@"Cycle view selection\n\t\tMove view down\n\t\tScroll down"];
[self registerSimulatorShortcutWithKey:UIKeyInputUpArrow modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:UIKeyInputUpArrow modifiers:0 action:^{
if (self.isHidden || ![self.explorerViewController handleUpArrowKeyPressed]) {
[self tryScrollUp];
}
} description:@"Cycle view selection\n\t\tMove view up\n\t\tScroll up"];
[self registerSimulatorShortcutWithKey:UIKeyInputRightArrow modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:UIKeyInputRightArrow modifiers:0 action:^{
if (!self.isHidden) {
[self.explorerViewController handleRightArrowKeyPressed];
}
} description:@"Move selected view right"];
[self registerSimulatorShortcutWithKey:UIKeyInputLeftArrow modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:UIKeyInputLeftArrow modifiers:0 action:^{
if (self.isHidden) {
[self tryGoBack];
} else {
@@ -147,15 +157,15 @@
}
} description:@"Move selected view left"];
[self registerSimulatorShortcutWithKey:@"?" modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:@"?" modifiers:0 action:^{
[self toggleTopViewControllerOfClass:[FLEXKeyboardHelpViewController class]];
} description:@"Toggle (this) help menu"];
[self registerSimulatorShortcutWithKey:UIKeyInputEscape modifiers:0 action:^{
[self registerDefaultSimulatorShortcutWithKey:UIKeyInputEscape modifiers:0 action:^{
[[self.topViewController presentingViewController] dismissViewControllerAnimated:YES completion:nil];
} description:@"End editing text\n\t\tDismiss top view controller"];
[self registerSimulatorShortcutWithKey:@"o" modifiers:UIKeyModifierCommand|UIKeyModifierShift action:^{
[self registerDefaultSimulatorShortcutWithKey:@"o" modifiers:UIKeyModifierCommand|UIKeyModifierShift action:^{
[self toggleTopViewControllerOfClass:[FLEXFileBrowserController class]];
} description:@"Toggle file browser menu"];
}
@@ -278,17 +278,18 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id<NSUR
// In iOS 7 resume lives in __NSCFLocalSessionTask
// In iOS 8 resume lives in NSURLSessionTask
// In iOS 9 resume lives in __NSCFURLSessionTask
// In iOS 14 resume lives in NSURLSessionTask
Class baseResumeClass = Nil;
if (![NSProcessInfo.processInfo respondsToSelector:@selector(operatingSystemVersion)]) {
// iOS ... 7
baseResumeClass = NSClassFromString(@"__NSCFLocalSessionTask");
} else {
NSInteger majorVersion = NSProcessInfo.processInfo.operatingSystemVersion.majorVersion;
if (majorVersion < 9) {
// iOS 8
if (majorVersion < 9 || majorVersion >= 14) {
// iOS 8 or iOS 14+
baseResumeClass = [NSURLSessionTask class];
} else {
// iOS 9+
// iOS 9 ... 13
baseResumeClass = NSClassFromString(@"__NSCFURLSessionTask");
}
}
+5 -1
View File
@@ -250,7 +250,11 @@
// Set up UIKit helper data
// Really, we only need to call this on properties and ivars
// because no other metadata types support editing.
for (NSArray *matrix in @[_allProperties, _allIvars, /* _allMethods, _allClassMethods, _allConformedProtocols */]) {
NSArray<NSArray *>*metadatas = @[
_allProperties, _allClassProperties, _allIvars,
/* _allMethods, _allClassMethods, _allConformedProtocols */
];
for (NSArray *matrix in metadatas) {
for (NSArray *metadataByClass in matrix) {
[FLEXObjectExplorer configureDefaultsForItems:metadataByClass];
}
@@ -11,6 +11,7 @@
#import "FLEXClassShortcuts.h"
#import "FLEXViewShortcuts.h"
#import "FLEXViewControllerShortcuts.h"
#import "FLEXUIAppShortcuts.h"
#import "FLEXImageShortcuts.h"
#import "FLEXLayerShortcuts.h"
#import "FLEXColorPreviewSection.h"
@@ -35,6 +36,7 @@ static NSMutableDictionary<Class, Class> *classesToRegisteredSections = nil;
ClassKey(NSOrderedSet) : [FLEXCollectionContentSection class],
ClassKey(NSUserDefaults) : [FLEXDefaultsContentSection class],
ClassKey(UIViewController) : [FLEXViewControllerShortcuts class],
ClassKey(UIApplication) : [FLEXUIAppShortcuts class],
ClassKey(UIView) : [FLEXViewShortcuts class],
ClassKey(UIImage) : [FLEXImageShortcuts class],
ClassKey(CALayer) : [FLEXLayerShortcuts class],
@@ -77,7 +77,7 @@ typedef id<FLEXCollection>(^FLEXCollectionContentFuture)(__kindof FLEXCollection
/// Defaults to \c NO
@property (nonatomic) BOOL hideSectionTitle;
/// Defaults to \c nil
@property (nonatomic) NSString *customTitle;
@property (nonatomic, copy) NSString *customTitle;
/// Defaults to \c NO
///
/// Settings this to \c NO will not display the element index for ordered collections.
@@ -36,7 +36,7 @@ typedef void (^FLEXMutableListCellForElement)(__kindof UITableViewCell *cell, id
/// By default, rows are not selectable. If you want rows
/// to be selectable, provide a selection handler here.
@property (nonatomic) void (^selectionHandler)(__kindof UIViewController * host, id element);
@property (nonatomic, copy) void (^selectionHandler)(__kindof UIViewController *host, id element);
/// The objects representing all possible rows in the section.
@property (nonatomic) NSArray<ObjectType> *list;
@@ -29,12 +29,12 @@ configurationBlock:(FLEXMutableListCellForElement)cellConfig
self = [super init];
if (self) {
_configureCell = cellConfig;
self.list = list.mutableCopy;
self.customFilter = filterBlock;
self.hideSectionTitle = YES;
}
return self;
}
@@ -48,7 +48,7 @@ configurationBlock:(FLEXMutableListCellForElement)cellConfig
- (void)setList:(NSMutableArray *)list {
NSParameterAssert(list);
_collection = list;
[self reloadData];
}
@@ -79,11 +79,15 @@ configurationBlock:(FLEXMutableListCellForElement)cellConfig
- (void (^)(__kindof UIViewController *))didSelectRowAction:(NSInteger)row {
if (self.selectionHandler) {
__weak __typeof(self) weakSelf = self;
return ^(UIViewController *host) {
self.selectionHandler(host, self.filteredList[row]);
__strong __typeof(self) strongSelf = weakSelf;
if (strongSelf) {
strongSelf.selectionHandler(host, strongSelf.filteredList[row]);
}
};
}
return nil;
}
@@ -95,7 +99,7 @@ configurationBlock:(FLEXMutableListCellForElement)cellConfig
if (self.cellRegistrationMapping.count) {
return self.cellRegistrationMapping.allKeys.firstObject;
}
return [super reuseIdentifierForRow:row];
}
@@ -18,6 +18,7 @@
#pragma mark Overrides
+ (instancetype)forObject:(NSBundle *)bundle {
__weak __typeof(self) weakSelf = self;
return [self forObject:bundle additionalRows:@[
[FLEXActionShortcut
title:@"Browse Bundle Directory" subtitle:nil
@@ -30,7 +31,10 @@
],
[FLEXActionShortcut title:@"Browse Bundle as Database…" subtitle:nil
selectionHandler:^(UIViewController *host, NSBundle *bundle) {
[self promptToExportBundleAsDatabase:bundle host:host];
__strong __typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf promptToExportBundleAsDatabase:bundle host:host];
}
}
accessoryType:^UITableViewCellAccessoryType(NSBundle *bundle) {
return UITableViewCellAccessoryDisclosureIndicator;
@@ -62,22 +66,22 @@
+ (void)browseBundleAsDatabase:(NSBundle *)bundle host:(UIViewController *)host name:(NSString *)name {
NSParameterAssert(name.length);
UIAlertController *progress = [FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title(@"Generating Database");
// Some iOS version glitch out of there is
// no initial message and you add one later
make.message(@"");
}];
[host presentViewController:progress animated:YES completion:^{
// Generate path to store db
NSString *path = [NSSearchPathForDirectoriesInDomains(
NSLibraryDirectory, NSUserDomainMask, YES
)[0] stringByAppendingPathComponent:name];
progress.message = [path stringByAppendingString:@"\n\nCreating database…"];
// Generate db and show progress
[FLEXRuntimeExporter createRuntimeDatabaseAtPath:path
forImages:@[bundle.executablePath]
@@ -8,6 +8,8 @@
#import "FLEXShortcutsSection.h"
@interface FLEXShortcutsFactory (UIApplication) @end
@interface FLEXShortcutsFactory (Views) @end
@interface FLEXShortcutsFactory (ViewControllers) @end
@@ -11,11 +11,36 @@
#import "FLEXRuntimeUtility.h"
#import "NSObject+FLEX_Reflection.h"
#pragma mark - UIApplication
@implementation FLEXShortcutsFactory (UIApplication)
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
// sharedApplication class property possibly not added
// as a literal class property until iOS 10
FLEXRuntimeUtilityTryAddObjectProperty(
2, sharedApplication, UIApplication.flex_metaclass, UIApplication, PropertyKey(ReadOnly)
);
self.append.classProperties(@[@"sharedApplication"]).forClass(UIApplication.flex_metaclass);
self.append.properties(@[
@"delegate", @"keyWindow", @"windows"
]).forClass(UIApplication.class);
if (@available(iOS 13, *)) {
self.append.properties(@[
@"connectedScenes", @"openSessions", @"supportsMultipleScenes"
]).forClass(UIApplication.class);
}
}
@end
#pragma mark - Views
@implementation FLEXShortcutsFactory (Views)
+ (void)load { FLEX_EXIT_IF_TESTING()
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
// A quirk of UIView and some other classes: a lot of the `@property`s are
// not actually properties from the perspective of the runtime.
//
@@ -106,7 +131,7 @@
@implementation FLEXShortcutsFactory (ViewControllers)
+ (void)load { FLEX_EXIT_IF_TESTING()
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
// toolbarItems is not really a property, make it one
FLEXRuntimeUtilityTryAddObjectProperty(3, toolbarItems, UIViewController.class, NSArray);
@@ -126,7 +151,7 @@
@implementation FLEXShortcutsFactory (UIImage)
+ (void)load { FLEX_EXIT_IF_TESTING()
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
self.append.methods(@[
@"CGImage", @"CIImage"
]).properties(@[
@@ -135,7 +160,7 @@
]).forClass(UIImage.class);
if (@available(iOS 13, *)) {
self.append.properties(@[@"symbolImage"]);
self.append.properties(@[@"symbolImage"]).forClass(UIImage.class);
}
}
@@ -146,7 +171,7 @@
@implementation FLEXShortcutsFactory (NSBundle)
+ (void)load { FLEX_EXIT_IF_TESTING()
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
self.append.properties(@[
@"bundleIdentifier", @"principalClass",
@"infoDictionary", @"bundlePath",
@@ -161,7 +186,7 @@
@implementation FLEXShortcutsFactory (Classes)
+ (void)load { FLEX_EXIT_IF_TESTING()
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
self.append.classMethods(@[@"new", @"alloc"]).forClass(NSObject.flex_metaclass);
}
@@ -172,7 +197,7 @@
@implementation FLEXShortcutsFactory (Activities)
+ (void)load { FLEX_EXIT_IF_TESTING()
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
// Property was added in iOS 10 but we want it on iOS 9 too
FLEXRuntimeUtilityTryAddNonatomicProperty(9, item, UIActivityItemProvider.class, id, PropertyKey(ReadOnly));
@@ -192,7 +217,7 @@
@implementation FLEXShortcutsFactory (Blocks)
+ (void)load { FLEX_EXIT_IF_TESTING()
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
self.append.methods(@[@"invoke"]).forClass(NSClassFromString(@"NSBlock"));
}
@@ -202,7 +227,7 @@
@implementation FLEXShortcutsFactory (Foundation)
+ (void)load { FLEX_EXIT_IF_TESTING()
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
self.append.properties(@[
@"configuration", @"delegate", @"delegateQueue", @"sessionDescription",
]).methods(@[
@@ -237,24 +262,6 @@
self.append.classProperties(@[
@"defaultTimeZone", @"systemTimeZone", @"localTimeZone"
]).forClass(NSTimeZone.class);
// self.append.<#type#>(@[@"<#value#>"]).forClass(NSURLSession.class);
//
//
// self.append.<#type#>(@[@"<#value#>"]).forClass(NSURLSession.class);
//
//
// self.append.<#type#>(@[@"<#value#>"]).forClass(NSURLSession.class);
//
//
// self.append.<#type#>(@[@"<#value#>"]).forClass(NSURLSession.class);
//
//
// self.append.<#type#>(@[@"<#value#>"]).forClass(NSURLSession.class);
//
//
// self.append.<#type#>(@[@"<#value#>"]).forClass(NSURLSession.class);
}
@end
@@ -59,6 +59,8 @@
/// Subclasses \e may override this to hide the disclosure indicator
/// for some rows. It is shown for all rows by default, unless
/// you initialize it with \c forObject:rowTitles:rowSubtitles:
///
/// When you hide the disclosure indicator, the row is not selectable.
- (UITableViewCellAccessoryType)accessoryTypeForRow:(NSInteger)row;
/// The number of lines for the title and subtitle labels. Defaults to 1.
@@ -0,0 +1,13 @@
//
// FLEXUIAppShortcuts.h
// FLEX
//
// Created by Tanner on 5/25/20.
// Copyright © 2020 Flipboard. All rights reserved.
//
#import "FLEXShortcutsSection.h"
@interface FLEXUIAppShortcuts : FLEXShortcutsSection
@end
@@ -0,0 +1,77 @@
//
// FLEXUIAppShortcuts.m
// FLEX
//
// Created by Tanner on 5/25/20.
// Copyright © 2020 Flipboard. All rights reserved.
//
#import "FLEXUIAppShortcuts.h"
#import "FLEXRuntimeUtility.h"
#import "FLEXShortcut.h"
#import "FLEXAlert.h"
@implementation FLEXUIAppShortcuts
#pragma mark - Overrides
+ (instancetype)forObject:(UIApplication *)application {
return [self forObject:application additionalRows:@[
[FLEXActionShortcut title:@"Open URL…"
subtitle:^NSString *(UIViewController *controller) {
return nil;
}
selectionHandler:^void(UIViewController *host, UIApplication *app) {
[FLEXAlert makeAlert:^(FLEXAlert *make) {
make.title(@"Open URL");
make.message(
@"This will call openURL: or openURL:options:completion: "
"with the string below. 'Open if Universal' will only open "
"the URL if it is a registered Universal Link."
);
make.textField(@"twitter://user?id=12345");
make.button(@"Open").handler(^(NSArray<NSString *> *strings) {
[self openURL:strings[0] inApp:app onlyIfUniveral:NO host:host];
});
make.button(@"Open if Universal").handler(^(NSArray<NSString *> *strings) {
[self openURL:strings[0] inApp:app onlyIfUniveral:YES host:host];
});
make.button(@"Cancel").cancelStyle();
} showFrom:host];
}
accessoryType:^UITableViewCellAccessoryType(UIViewController *controller) {
return UITableViewCellAccessoryDisclosureIndicator;
}
]
]];
}
+ (void)openURL:(NSString *)urlString
inApp:(UIApplication *)app
onlyIfUniveral:(BOOL)universalOnly
host:(UIViewController *)host {
NSURL *url = [NSURL URLWithString:urlString];
if (url) {
if (@available(iOS 10, *)) {
[app openURL:url options:@{
UIApplicationOpenURLOptionUniversalLinksOnly: @(universalOnly)
} completionHandler:^(BOOL success) {
if (!success) {
[FLEXAlert showAlert:@"No Universal Link Handler"
message:@"No installed application is registered to handle this link."
from:host
];
}
}];
} else {
[app openURL:url];
}
} else {
[FLEXAlert showAlert:@"Error" message:@"Invalid URL" from:host];
}
}
@end
@@ -13,29 +13,10 @@
#import "FLEXAlert.h"
@interface FLEXViewControllerShortcuts ()
@property (nonatomic, readonly) UIViewController *viewController;
@property (nonatomic, readonly) BOOL viewControllerIsInUse;
@end
@implementation FLEXViewControllerShortcuts
#pragma mark - Internal
- (UIViewController *)viewController {
return self.object;
}
/// A view controller is "in use" if it's view is in a window,
/// or if it belongs to a navigation stack which is in use.
- (BOOL)viewControllerIsInUse {
if (self.viewController.view.window) {
return YES;
}
return self.viewController.navigationController != nil;
}
#pragma mark - Overrides
+ (instancetype)forObject:(UIViewController *)viewController {
@@ -156,7 +156,7 @@ FLEXObjectExplorerDefaultsImpl
if (targetNotNil) {
id value = [self currentValueBeforeUnboxingWithTarget:object];
[items addObjectsFromArray:@[
@"Value Preview", [self previewWithTarget:object],
@"Value Preview", [self previewWithTarget:object] ?: @"",
@"Value Address", returnsObject ? [FLEXUtility addressOfObject:value] : @"",
]];
}
@@ -462,7 +462,7 @@ FLEXObjectExplorerDefaultsImpl
NSString *conformances = [conformanceNames componentsJoinedByString:@"\n"];
return @[
@"Name", self.name ?: @"",
@"Conformances", conformances,
@"Conformances", conformances ?: @"",
];
}
@@ -13,7 +13,7 @@
@interface NSDictionary (ObjcRuntime)
/// \c kFLEXPropertyAttributeKeyTypeEncoding is the only required key.
/// Keys representing a boolean value should have a value of \c @YES instead of an empty string.
/// Keys representing a boolean value should have a value of \c YES instead of an empty string.
- (NSString *)propertyAttributesString;
+ (instancetype)attributesDictionaryForProperty:(objc_property_t)property;
@@ -34,6 +34,8 @@ NSArray<FLEXProperty *> *FLEXGetAllProperties(_Nullable Class cls);
/// @param instance used to mark methods as instance methods or not.
/// Not used to determine whether to get instance or class methods.
NSArray<FLEXMethod *> *FLEXGetAllMethods(_Nullable Class cls, BOOL instance);
/// @param cls a class object to get all instance and class methods.
NSArray<FLEXMethod *> *FLEXGetAllInstanceAndClassMethods(_Nullable Class cls);
@@ -146,7 +146,7 @@ NSArray<FLEXMethod *> *FLEXGetAllMethods(_Nullable Class cls, BOOL instance) {
@interface NSProxy (AnyObjectAdditions) @end
@implementation NSProxy (AnyObjectAdditions)
+ (void)load { FLEX_EXIT_IF_TESTING()
+ (void)load { FLEX_EXIT_IF_NO_CTORS()
// We need to get all of the methods in this file and add them to NSProxy.
// To do this we we need the class itself and it's metaclass.
// Edit: also add them to Swift._SwiftObject
@@ -14,6 +14,8 @@
@property (nonatomic, readonly) BOOL flex_typeIsConst;
/// @return the first char in the type encoding that is not the const specifier
@property (nonatomic, readonly) FLEXTypeEncoding flex_firstNonConstType;
/// @return the first char in the type encoding after the pointer specifier, if it is a pointer
@property (nonatomic, readonly) FLEXTypeEncoding flex_pointeeType;
/// @return whether this type is an objc object of any kind, even if it's const
@property (nonatomic, readonly) BOOL flex_typeIsObjectOrClass;
/// @return the class named in this type encoding if it is of the form \c @"MYClass"
@@ -75,6 +75,16 @@
return [self characterAtIndex:(self.flex_typeIsConst ? 1 : 0)];
}
- (FLEXTypeEncoding)flex_pointeeType {
if (!self.length) return FLEXTypeEncodingNull;
if (self.flex_firstNonConstType == FLEXTypeEncodingPointer) {
return [self characterAtIndex:(self.flex_typeIsConst ? 2 : 1)];
}
return FLEXTypeEncodingNull;
}
- (BOOL)flex_typeIsObjectOrClass {
FLEXTypeEncoding type = self.flex_firstNonConstType;
return type == FLEXTypeEncodingObjcObject || type == FLEXTypeEncodingObjcClass;
@@ -12,7 +12,7 @@
@interface NSString (Utilities)
/// A dictionary of property attributes if the receiver is a valid property attributes string.
/// Values are either a string or \c @YES. Boolean attributes which are false will not be
/// Values are either a string or \c YES. Boolean attributes which are false will not be
/// present in the dictionary. See this link on how to construct a proper attributes string:
/// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
///
+3 -3
View File
@@ -113,7 +113,7 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
- (FLEXAlertStringProperty)title {
return ^FLEXAlert *(NSString *title) {
if (self._controller.title) {
self._controller.title = [self._controller.title stringByAppendingString:title];
self._controller.title = [self._controller.title stringByAppendingString:title ?: @""];
} else {
self._controller.title = title;
}
@@ -124,7 +124,7 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
- (FLEXAlertStringProperty)message {
return ^FLEXAlert *(NSString *message) {
if (self._controller.message) {
self._controller.message = [self._controller.message stringByAppendingString:message];
self._controller.message = [self._controller.message stringByAppendingString:message ?: @""];
} else {
self._controller.message = message;
}
@@ -166,7 +166,7 @@ NSAssert(!self._action, @"Cannot mutate action after retreiving underlying UIAle
return ^FLEXAlertAction *(NSString *title) {
FLEXAlertActionMutationAssertion();
if (self._title) {
self._title = [self._title stringByAppendingString:title];
self._title = [self._title stringByAppendingString:title ?: @""];
} else {
self._title = title;
}
+5 -16
View File
@@ -7,6 +7,7 @@
//
#import "FLEXHeapEnumerator.h"
#import "FLEXObjcInternal.h"
#import <malloc/malloc.h>
#import <mach/mach.h>
#import <objc/runtime.h>
@@ -84,26 +85,14 @@ static kern_return_t reader(__unused task_t remote_task, vm_address_t remote_add
block(object, actualClass);
lock_zone(zone);
};
// The largest realistic memory address varies by platform.
// Only 48 bits are used by 64 bit machines while
// 32 bit machines use all bits.
//
// __LP64__ is defined as 1 for both arm64 and x86_64
// via: clang -dM -arch [arm64|x86_64] -E -x c /dev/null | grep LP
#if __LP64__
static uintptr_t MAX_REALISTIC_ADDRESS = 0x0000FFFFFFFFFFFF;
BOOL lockZoneValid = lock_zone != nil && (uintptr_t)lock_zone < MAX_REALISTIC_ADDRESS;
BOOL unlockZoneValid = unlock_zone != nil && (uintptr_t)unlock_zone < MAX_REALISTIC_ADDRESS;
#else
BOOL lockZoneValid = lock_zone != nil;
BOOL unlockZoneValid = unlock_zone != nil;
#endif
BOOL lockZoneValid = FLEXPointerIsReadable(lock_zone);
BOOL unlockZoneValid = FLEXPointerIsReadable(unlock_zone);
// There is little documentation on when and why
// any of these function pointers might be NULL
// or garbage, so we resort to checking for NULL
// and impossible memory addresses at least
// and whether the pointer is readable
if (introspection->enumerator && lockZoneValid && unlockZoneValid) {
lock_zone(zone);
introspection->enumerator(TASK_NULL, (void *)&callback, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, reader, &range_callback);
+12 -2
View File
@@ -9,8 +9,18 @@
#ifndef FLEXMacros_h
#define FLEXMacros_h
// Used to prevent loading of pre-registered shortcuts and runtime categories in a test environment
#define FLEX_EXIT_IF_TESTING() if (NSClassFromString(@"XCTest")) return;
#define flex_keywordify class NSObject;
#define ctor flex_keywordify __attribute__((constructor)) void __flex_ctor_##__LINE__()
#define dtor flex_keywordify __attribute__((destructor)) void __flex_dtor_##__LINE__()
// A macro to check if we are running in a test environment
#define FLEX_IS_TESTING() (NSClassFromString(@"XCTest") != nil)
/// Whether we want the majority of constructors to run upon load or not.
extern BOOL FLEXConstructorsShouldRun();
/// A macro to return from the current procedure if we don't want to run constructors
#define FLEX_EXIT_IF_NO_CTORS() if (!FLEXConstructorsShouldRun()) return;
/// Rounds down to the nearest "point" coordinate
NS_INLINE CGFloat FLEXFloor(CGFloat x) {
+15 -1
View File
@@ -11,8 +11,22 @@
#import "FLEXResources.h"
#import "FLEXWindow.h"
#import <ImageIO/ImageIO.h>
#import <zlib.h>
#import <objc/runtime.h>
#import <zlib.h>
BOOL FLEXConstructorsShouldRun() {
static BOOL _FLEXConstructorsShouldRun_storage = YES;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *key = @"FLEX_SKIP_INIT";
if (getenv(key.UTF8String) || [NSUserDefaults.standardUserDefaults boolForKey:key]) {
_FLEXConstructorsShouldRun_storage = NO;
}
});
return _FLEXConstructorsShouldRun_storage;
}
@implementation FLEXUtility
@@ -12,10 +12,16 @@
@property (nonatomic, readonly, class) FLEXKeyboardShortcutManager *sharedManager;
/// @param key A single character string matching a key on the keyboard
/// @param modifiers Modifier keys such as shift, command, or alt/option
/// @param action The block to run on the main thread when the key & modifier combination is recognized.
/// @param description Shown the the keyboard shortcut help menu, which is accessed via the '?' key.
/// @param allowOverride Allow registering even if there's an existing action associated with that key/modifier.
- (void)registerSimulatorShortcutWithKey:(NSString *)key
modifiers:(UIKeyModifierFlags)modifiers
action:(dispatch_block_t)action
description:(NSString *)description;
description:(NSString *)description
allowOverride:(BOOL)allowOverride;
@property (nonatomic, getter=isEnabled) BOOL enabled;
@property (nonatomic, readonly) NSString *keyboardShortcutsDescription;
@@ -212,9 +212,14 @@
- (void)registerSimulatorShortcutWithKey:(NSString *)key
modifiers:(UIKeyModifierFlags)modifiers
action:(dispatch_block_t)action
description:(NSString *)description {
description:(NSString *)description
allowOverride:(BOOL)allowOverride {
FLEXKeyInput *keyInput = [FLEXKeyInput keyInputForKey:key flags:modifiers helpDescription:description];
[self.actionsForKeyInputs setObject:action forKey:keyInput];
if (!allowOverride && self.actionsForKeyInputs[keyInput] != nil) {
return;
} else {
[self.actionsForKeyInputs setObject:action forKey:keyInput];
}
}
static const long kFLEXControlKeyCode = 0xe0;
@@ -11,6 +11,56 @@
extern "C" {
#endif
// The macros below are copied straight from
// objc-internal.h, objc-private.h, objc-object.h, and objc-config.h with
// as few modifications as possible. Changes are noted in boxed comments.
// https://opensource.apple.com/source/objc4/objc4-723/
// https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-internal.h.auto.html
// https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-object.h.auto.html
/////////////////////
// objc-internal.h //
/////////////////////
#if __LP64__
#define OBJC_HAVE_TAGGED_POINTERS 1
#endif
#if OBJC_HAVE_TAGGED_POINTERS
#if TARGET_OS_OSX && __x86_64__
// 64-bit Mac - tag bit is LSB
# define OBJC_MSB_TAGGED_POINTERS 0
#else
// Everything else - tag bit is MSB
# define OBJC_MSB_TAGGED_POINTERS 1
#endif
#if OBJC_MSB_TAGGED_POINTERS
# define _OBJC_TAG_MASK (1UL<<63)
# define _OBJC_TAG_EXT_MASK (0xfUL<<60)
#else
# define _OBJC_TAG_MASK 1UL
# define _OBJC_TAG_EXT_MASK 0xfUL
#endif
#endif // OBJC_HAVE_TAGGED_POINTERS
//////////////////////////////////////
// originally _objc_isTaggedPointer //
//////////////////////////////////////
NS_INLINE BOOL flex_isTaggedPointer(const void *ptr) {
#if OBJC_HAVE_TAGGED_POINTERS
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
#else
return NO;
#endif
}
#define FLEXPointerIsTaggedPointer(obj) flex_isTaggedPointer((__bridge void *)obj)
BOOL FLEXPointerIsReadable(const void * ptr);
/// @brief Assumes memory is valid and readable.
/// @discussion objc-internal.h, objc-private.h, and objc-config.h
/// https://blog.timac.org/2016/1124-testing-if-an-arbitrary-pointer-is-a-valid-objective-c-object/
@@ -7,16 +7,16 @@
/*
* Copyright (c) 2005-2007 Apple Inc. All Rights Reserved.
*
*
* @APPLE_LICENSE_HEADER_START@
*
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -24,7 +24,7 @@
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
*
* @APPLE_LICENSE_HEADER_END@
*/
@@ -35,6 +35,10 @@
// For vm_region_64
#include <mach/mach.h>
#if __arm64e__
#include <ptrauth.h>
#endif
#define ALWAYS_INLINE inline __attribute__((always_inline))
#define NEVER_INLINE inline __attribute__((noinline))
@@ -49,35 +53,8 @@
// objc-internal.h //
/////////////////////
#if __LP64__
#define OBJC_HAVE_TAGGED_POINTERS 1
#endif
#if OBJC_HAVE_TAGGED_POINTERS
#if TARGET_OS_OSX && __x86_64__
// 64-bit Mac - tag bit is LSB
# define OBJC_MSB_TAGGED_POINTERS 0
#else
// Everything else - tag bit is MSB
# define OBJC_MSB_TAGGED_POINTERS 1
#endif
#if OBJC_MSB_TAGGED_POINTERS
# define _OBJC_TAG_MASK (1UL<<63)
# define _OBJC_TAG_EXT_MASK (0xfUL<<60)
#else
# define _OBJC_TAG_MASK 1UL
# define _OBJC_TAG_EXT_MASK 0xfUL
#endif
//////////////////////////////////////
// originally _objc_isTaggedPointer //
//////////////////////////////////////
static BOOL flex_isTaggedPointer(const void *ptr) {
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
///////////////////
// objc-object.h //
///////////////////
@@ -85,11 +62,11 @@ static BOOL flex_isTaggedPointer(const void *ptr) {
////////////////////////////////////////////////
// originally objc_object::isExtTaggedPointer //
////////////////////////////////////////////////
static BOOL flex_isExtTaggedPointer(const void *ptr) {
NS_INLINE BOOL flex_isExtTaggedPointer(const void *ptr) {
return ((uintptr_t)ptr & _OBJC_TAG_EXT_MASK) == _OBJC_TAG_EXT_MASK;
}
#endif
#endif // OBJC_HAVE_TAGGED_POINTERS
/////////////////////////////////////
// FLEXObjectInternal //
@@ -98,11 +75,16 @@ static BOOL flex_isExtTaggedPointer(const void *ptr) {
extern "C" {
static BOOL FLEXPointerIsReadable(const void *inPtr) {
BOOL FLEXPointerIsReadable(const void *inPtr) {
kern_return_t error = KERN_SUCCESS;
vm_size_t vmsize;
#if __arm64e__
// On arm64e, we need to strip the PAC from the pointer so the adress is readable
vm_address_t address = (vm_address_t)ptrauth_strip(inPtr, ptrauth_key_function_pointer);
#else
vm_address_t address = (vm_address_t)inPtr;
#endif
vm_region_basic_info_data_t info;
mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
memory_object_name_t object;
@@ -127,7 +109,11 @@ static BOOL FLEXPointerIsReadable(const void *inPtr) {
// Read the memory
vm_offset_t readMem = 0;
mach_msg_type_number_t size = 0;
#if __arm64e__
address = (vm_address_t)ptrauth_strip(inPtr, ptrauth_key_function_pointer);
#else
address = (vm_address_t)inPtr;
#endif
error = vm_read(mach_task_self(), address, sizeof(uintptr_t), &readMem, &size);
if (error != KERN_SUCCESS) {
// vm_read returned an error
@@ -194,5 +180,5 @@ BOOL FLEXPointerIsValidObjcObject(const void *ptr) {
return NO;
}
} // End extern "C"
@@ -546,10 +546,15 @@ BOOL FLEXGetSizeAndAlignment(const char *type, NSUInteger *sizep, NSUInteger *al
}
- (BOOL)canScanChar:(char)c {
NSScanner *scan = self.scan;
if (scan.scanLocation >= scan.string.length) return NO;
// By avoiding any ARC calls on these two objects which we know won't be
// free'd out from under us, we're making HUGE performance savings in this
// parser, because this method is one of the most-used methods of the parser.
// This is probably the most performance-critical method in this class.
__unsafe_unretained NSScanner *scan = self.scan;
__unsafe_unretained NSString *string = scan.string;
if (scan.scanLocation >= string.length) return NO;
return [scan.string characterAtIndex:scan.scanLocation] == c;
return [string characterAtIndex:scan.scanLocation] == c;
}
- (BOOL)scanChar:(char)c {
@@ -11,6 +11,8 @@
#import "FLEXRuntimeUtility.h"
#import "FLEXRuntimeSafety.h"
#import "FLEXTypeEncodingParser.h"
#import "NSString+FLEX.h"
#include "FLEXObjcInternal.h"
#include <dlfcn.h>
@interface FLEXIvar () {
@@ -35,19 +37,20 @@
}
+ (instancetype)named:(NSString *)name onClass:(Class)cls {
Ivar ivar = class_getInstanceVariable(cls, name.UTF8String);
Ivar _Nullable ivar = class_getInstanceVariable(cls, name.UTF8String);
NSAssert(ivar, @"Cannot find ivar with name %@ on class %@", name, cls);
return [self ivar:ivar];
}
- (id)initWithIvar:(Ivar)ivar {
NSParameterAssert(ivar);
self = [super init];
if (self) {
_objc_ivar = ivar;
[self examine];
}
return self;
}
@@ -71,7 +74,7 @@
_name = @(ivar_getName(self.objc_ivar) ?: "(nil)");
_offset = ivar_getOffset(self.objc_ivar);
_typeEncoding = @(ivar_getTypeEncoding(self.objc_ivar) ?: "");
NSString *typeForDetails = _typeEncoding;
NSString *sizeForDetails = nil;
if (_typeEncoding.length) {
@@ -83,7 +86,7 @@
typeForDetails = @"no type info";
sizeForDetails = @"unknown size";
}
Dl_info exeInfo;
if (dladdr(_objc_ivar, &exeInfo)) {
_imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : nil;
@@ -97,7 +100,9 @@
- (id)getValue:(id)target {
id value = nil;
if (!FLEXIvarIsSafe(_objc_ivar) || _type == FLEXTypeEncodingNull) {
if (!FLEXIvarIsSafe(_objc_ivar) ||
_type == FLEXTypeEncodingNull ||
FLEXPointerIsTaggedPointer(target)) {
return nil;
}
@@ -147,9 +152,14 @@
}
- (id)getPotentiallyUnboxedValue:(id)target {
NSString *type = self.typeEncoding;
if (type.flex_typeIsNonObjcPointer && type.flex_pointeeType != FLEXTypeEncodingVoid) {
return [self getValue:target];
}
return [FLEXRuntimeUtility
potentiallyUnwrapBoxedPointer:[self getValue:target]
type:self.typeEncoding.UTF8String
type:type.UTF8String
];
}
@@ -14,6 +14,7 @@
#include <dlfcn.h>
@implementation FLEXMethod
@synthesize imagePath = _imagePath;
@dynamic implementation;
+ (instancetype)buildMethodNamed:(NSString *)name withTypes:(NSString *)typeEncoding implementation:(IMP)implementation {
@@ -156,11 +157,6 @@
_name = NSStringFromSelector(_selector);
_returnType = (FLEXTypeEncoding *)_signature.methodReturnType ?: "";
_returnSize = _signature.methodReturnLength;
Dl_info exeInfo;
if (dladdr(_objc_method, &exeInfo)) {
_imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : nil;
}
}
#pragma mark Public
@@ -184,6 +180,17 @@
return _typeEncoding;
}
- (NSString *)imagePath {
if (!_imagePath) {
Dl_info exeInfo;
if (dladdr(_implementation, &exeInfo)) {
_imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : @"";
}
}
return _imagePath;
}
#pragma mark Misc
- (void)swapImplementations:(FLEXMethod *)method {
@@ -45,8 +45,9 @@
}
+ (instancetype)named:(NSString *)name onClass:(Class)cls {
NSParameterAssert(class_getProperty(cls, name.UTF8String));
return [self property:class_getProperty(cls, name.UTF8String) onClass:cls];
objc_property_t _Nullable property = class_getProperty(cls, name.UTF8String);
NSAssert(property, @"Cannot find property with name %@ on class %@", name, cls);
return [self property:property onClass:cls];
}
+ (instancetype)propertyWithName:(NSString *)name attributes:(FLEXPropertyAttributes *)attributes {
@@ -46,7 +46,7 @@ NS_ASSUME_NONNULL_BEGIN
/// A human-readable version of the property attributes.
@property (nonatomic, readonly) NSString *fullDeclaration;
/// A dictionary of the property attributes.
/// Values are either a string or \c @YES. Boolean attributes
/// Values are either a string or \c YES. Boolean attributes
/// which are false will not be present in the dictionary.
@property (nonatomic, readonly) NSDictionary *dictionary;
@@ -64,6 +64,6 @@
@property (nonatomic, readonly) NSString *typeEncoding;
/// The method's return type.
@property (nonatomic, readonly) FLEXTypeEncoding returnType;
/// \c @YES if this is an instance method, \c @NO if it is a class method, or \c nil if unspecified
/// \c YES if this is an instance method, \c NO if it is a class method, or \c nil if unspecified
@property (nonatomic, readonly) NSNumber *instance;
@end
+29 -29
View File
@@ -60,10 +60,10 @@ struct rebindings_entry {
struct rebindings_entry *next;
};
static struct rebindings_entry *_rebindings_head;
static struct rebindings_entry *_flex_rebindings_head;
/// @return 0 on success
static int prepend_rebindings(struct rebindings_entry **rebindings_head,
static int flex_prepend_rebindings(struct rebindings_entry **rebindings_head,
struct rebinding rebindings[],
size_t nel) {
struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry));
@@ -85,7 +85,7 @@ static int prepend_rebindings(struct rebindings_entry **rebindings_head,
return 0;
}
static vm_prot_t get_protection(void *sectionStart) {
static vm_prot_t flex_get_protection(void *sectionStart) {
mach_port_t task = mach_task_self();
vm_size_t size = 0;
vm_address_t address = (vm_address_t)sectionStart;
@@ -110,19 +110,19 @@ static vm_prot_t get_protection(void *sectionStart) {
return VM_PROT_READ;
}
}
static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
section_t *section,
intptr_t slide,
nlist_t *symtab,
char *strtab,
uint32_t *indirect_symtab) {
static void flex_perform_rebinding_with_section(struct rebindings_entry *rebindings,
section_t *section,
intptr_t slide,
nlist_t *symtab,
char *strtab,
uint32_t *indirect_symtab) {
const bool isDataConst = strcmp(section->segname, "__DATA_CONST") == 0;
uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
vm_prot_t oldProtection = VM_PROT_READ;
if (isDataConst) {
oldProtection = get_protection(rebindings);
oldProtection = flex_get_protection(rebindings);
mprotect(indirect_symbol_bindings, section->size, PROT_READ | PROT_WRITE);
}
@@ -177,9 +177,9 @@ static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
}
}
static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
const struct mach_header *header,
intptr_t slide) {
static void flex_rebind_symbols_for_image(struct rebindings_entry *rebindings,
const struct mach_header *header,
intptr_t slide) {
Dl_info info;
if (dladdr(header, &info) == 0) {
return;
@@ -232,12 +232,12 @@ static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
section_t *sect = (section_t *)(cur + sizeof(segment_command_t)) + j;
if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
perform_rebinding_with_section(
flex_perform_rebinding_with_section(
rebindings, sect, slide, symtab, strtab, indirect_symtab
);
}
if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
perform_rebinding_with_section(
flex_perform_rebinding_with_section(
rebindings, sect, slide, symtab, strtab, indirect_symtab
);
}
@@ -246,19 +246,19 @@ static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
}
}
static void _rebind_symbols_for_image(const struct mach_header *header,
intptr_t slide) {
rebind_symbols_for_image(_rebindings_head, header, slide);
static void _flex_rebind_symbols_for_image(const struct mach_header *header,
intptr_t slide) {
flex_rebind_symbols_for_image(_flex_rebindings_head, header, slide);
}
int rebind_symbols_image(void *header,
intptr_t slide,
struct rebinding rebindings[],
size_t rebindings_nel) {
int flex_rebind_symbols_image(void *header,
intptr_t slide,
struct rebinding rebindings[],
size_t rebindings_nel) {
struct rebindings_entry *rebindings_head = NULL;
int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel);
rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide);
int retval = flex_prepend_rebindings(&rebindings_head, rebindings, rebindings_nel);
flex_rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide);
if (rebindings_head) {
free(rebindings_head->rebindings);
@@ -269,20 +269,20 @@ int rebind_symbols_image(void *header,
}
/// @return 0 on success
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel);
int flex_rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
int retval = flex_prepend_rebindings(&_flex_rebindings_head, rebindings, rebindings_nel);
if (retval < 0) {
return retval;
}
// If this was the first call, register callback for image additions (which is also invoked for
// existing images, otherwise, just run on existing images
if (!_rebindings_head->next) {
_dyld_register_func_for_add_image(_rebind_symbols_for_image);
if (!_flex_rebindings_head->next) {
_dyld_register_func_for_add_image(_flex_rebind_symbols_for_image);
} else {
uint32_t c = _dyld_image_count();
for (uint32_t i = 0; i < c; i++) {
_rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
_flex_rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
}
}
+5 -5
View File
@@ -57,7 +57,7 @@ struct rebinding {
* @return 0 on success
*/
FISHHOOK_VISIBILITY
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
int flex_rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
/**
* Rebinds as above, but only in the specified image. The header should point
@@ -65,10 +65,10 @@ int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
* @return 0 on success
*/
FISHHOOK_VISIBILITY
int rebind_symbols_image(void *header,
intptr_t slide,
struct rebinding rebindings[],
size_t rebindings_nel);
int flex_rebind_symbols_image(void *header,
intptr_t slide,
struct rebinding rebindings[],
size_t rebindings_nel);
#ifdef __cplusplus
}
+2 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = "FLEX"
spec.version = "4.1.0"
spec.version = "4.1.1"
spec.summary = "A set of in-app debugging and exploration tools for iOS"
spec.description = <<-DESC
- Inspect and modify views in the hierarchy.
@@ -32,7 +32,7 @@ Pod::Spec.new do |spec|
spec.platform = :ios, "9.0"
spec.source = { :git => "https://github.com/Flipboard/FLEX.git", :tag => "#{spec.version}" }
spec.source_files = "Classes/**/*.{h,c,m,mm}"
spec.frameworks = [ "Foundation", "UIKit", "CoreGraphics", "ImageIO", "QuartzCore", "WebKit", "Security" ]
spec.frameworks = [ "Foundation", "UIKit", "CoreGraphics", "ImageIO", "QuartzCore", "WebKit", "Security", "SceneKit" ]
spec.libraries = [ "z", "sqlite3" ]
spec.requires_arc = true
spec.compiler_flags = "-Wno-unsupported-availability-guard -Wno-strict-prototypes"
+15 -1
View File
@@ -164,6 +164,8 @@
C32A195F231732E800EB02AC /* FLEXCollectionContentSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C32A195D231732E800EB02AC /* FLEXCollectionContentSection.m */; };
C32A19622317378C00EB02AC /* FLEXDefaultsContentSection.h in Headers */ = {isa = PBXBuildFile; fileRef = C32A19602317378C00EB02AC /* FLEXDefaultsContentSection.h */; settings = {ATTRIBUTES = (Public, ); }; };
C32A19632317378C00EB02AC /* FLEXDefaultsContentSection.m in Sources */ = {isa = PBXBuildFile; fileRef = C32A19612317378C00EB02AC /* FLEXDefaultsContentSection.m */; };
C32F3A18247C6B3E0063542D /* FLEXUIAppShortcuts.h in Headers */ = {isa = PBXBuildFile; fileRef = C32F3A16247C6B3E0063542D /* FLEXUIAppShortcuts.h */; };
C32F3A19247C6B3E0063542D /* FLEXUIAppShortcuts.m in Sources */ = {isa = PBXBuildFile; fileRef = C32F3A17247C6B3E0063542D /* FLEXUIAppShortcuts.m */; };
C33C825B23159EAF00DD2451 /* FLEXTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C33C825A23159EAF00DD2451 /* FLEXTests.m */; };
C33C825E2316DC8600DD2451 /* FLEXObjectExplorer.h in Headers */ = {isa = PBXBuildFile; fileRef = C33C825C2316DC8600DD2451 /* FLEXObjectExplorer.h */; settings = {ATTRIBUTES = (Public, ); }; };
C33C825F2316DC8600DD2451 /* FLEXObjectExplorer.m in Sources */ = {isa = PBXBuildFile; fileRef = C33C825D2316DC8600DD2451 /* FLEXObjectExplorer.m */; };
@@ -523,6 +525,8 @@
C32A195D231732E800EB02AC /* FLEXCollectionContentSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXCollectionContentSection.m; sourceTree = "<group>"; };
C32A19602317378C00EB02AC /* FLEXDefaultsContentSection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXDefaultsContentSection.h; sourceTree = "<group>"; };
C32A19612317378C00EB02AC /* FLEXDefaultsContentSection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXDefaultsContentSection.m; sourceTree = "<group>"; };
C32F3A16247C6B3E0063542D /* FLEXUIAppShortcuts.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLEXUIAppShortcuts.h; sourceTree = "<group>"; };
C32F3A17247C6B3E0063542D /* FLEXUIAppShortcuts.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXUIAppShortcuts.m; sourceTree = "<group>"; };
C33C825A23159EAF00DD2451 /* FLEXTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLEXTests.m; sourceTree = "<group>"; };
C33C825C2316DC8600DD2451 /* FLEXObjectExplorer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FLEXObjectExplorer.h; path = Classes/ObjectExplorers/FLEXObjectExplorer.h; sourceTree = SOURCE_ROOT; };
C33C825D2316DC8600DD2451 /* FLEXObjectExplorer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = FLEXObjectExplorer.m; path = Classes/ObjectExplorers/FLEXObjectExplorer.m; sourceTree = SOURCE_ROOT; };
@@ -1363,6 +1367,8 @@
C34D4EB723A2B17900C1F903 /* FLEXBundleShortcuts.m */,
C38EF26123A2FCD20047A7EC /* FLEXViewControllerShortcuts.h */,
C38EF26023A2FCD20047A7EC /* FLEXViewControllerShortcuts.m */,
C32F3A16247C6B3E0063542D /* FLEXUIAppShortcuts.h */,
C32F3A17247C6B3E0063542D /* FLEXUIAppShortcuts.m */,
C31D93E223E38CBE005517BF /* FLEXBlockShortcuts.h */,
C31D93E323E38CBE005517BF /* FLEXBlockShortcuts.m */,
);
@@ -1489,6 +1495,7 @@
C3BFD070233C23ED0015FB82 /* NSArray+FLEX.h in Headers */,
C398682923AC370100E9E391 /* FLEXViewShortcuts.h in Headers */,
C3474C4023DA496400466532 /* FLEXKeyValueTableViewCell.h in Headers */,
C32F3A18247C6B3E0063542D /* FLEXUIAppShortcuts.h in Headers */,
779B1ED61C0C4D7C001F5E49 /* FLEXTableContentViewController.h in Headers */,
C3DFCDB82418336D00BB7084 /* NSUserDefaults+FLEX.h in Headers */,
3A4C95221B5B21410088C3F2 /* FLEXFileBrowserSearchOperation.h in Headers */,
@@ -1634,7 +1641,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = FLEX;
LastUpgradeCheck = 1110;
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = Flipboard;
TargetAttributes = {
1C27A8B51F0E5A0300F0D02D = {
@@ -1755,6 +1762,7 @@
3A4C94CE1B5B21410088C3F2 /* FLEXGlobalsEntry.m in Sources */,
C398625223AD6C67007E6793 /* FLEXKeyPathSearchController.m in Sources */,
C3E5D9FE2316E83700E655DB /* FLEXRuntime+Compare.m in Sources */,
C32F3A19247C6B3E0063542D /* FLEXUIAppShortcuts.m in Sources */,
71E1C2192307FBB800F5032A /* FLEXKeychainQuery.m in Sources */,
C398627323AD7951007E6793 /* UIGestureRecognizer+Blocks.m in Sources */,
C3F646F723A04A7500D4A011 /* FLEXShortcut.m in Sources */,
@@ -1880,6 +1888,7 @@
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = U3LST7M92S;
INFOPLIST_FILE = FLEXTests/Info.plist;
@@ -1896,6 +1905,7 @@
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
DEVELOPMENT_TEAM = U3LST7M92S;
INFOPLIST_FILE = FLEXTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.3;
@@ -1928,6 +1938,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -1989,6 +2000,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -2025,6 +2037,7 @@
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_STRICT_PROTOTYPES = NO;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
@@ -2057,6 +2070,7 @@
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_STRICT_PROTOTYPES = NO;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1110"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1C27A8B51F0E5A0300F0D02D"
BuildableName = "FLEXTests.xctest"
BlueprintName = "FLEXTests"
ReferencedContainer = "container:FLEX.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "FLEX_SKIP_INIT"
value = ""
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1C27A8B51F0E5A0300F0D02D"
BuildableName = "FLEXTests.xctest"
BlueprintName = "FLEXTests"
ReferencedContainer = "container:FLEX.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+21 -1
View File
@@ -14,8 +14,13 @@
#import "FLEXProperty.h"
#import "FLEXUtility.h"
#import "FLEXMethod.h"
#import "FLEXIvar.h"
@interface Subclass : NSObject @end
@interface Subclass : NSObject {
@public
NSUInteger *_indexes;
}
@end
@implementation Subclass @end
@interface FLEXTests : XCTestCase
@@ -101,4 +106,19 @@
XCTAssert(props.count == 1);
}
- (void)testIvarUnboxing {
NSUInteger array[4] = { 0xaa, 0xbb, 0xcc, 0x00 };
Subclass *obj = [Subclass new];
obj->_indexes = array;
FLEXIvar *ivar = [Subclass flex_ivarNamed:@"_indexes"];
NSValue *arrayValue = [ivar getPotentiallyUnboxedValue:obj];
NSUInteger *pointerValue = arrayValue.pointerValue;
XCTAssert(pointerValue != nil);
XCTAssertEqual(pointerValue, (NSUInteger *)&array);
XCTAssertEqual(pointerValue[0], 0xaa);
}
@end
+3 -2
View File
@@ -43,7 +43,7 @@ Short version:
```swift
// Swift
FLEXManager.shared().showExplorer()
FLEXManager.shared.showExplorer()
```
More complete version:
@@ -220,7 +220,8 @@ FLEX builds on ideas and inspiration from open source tools that came before it.
- [Gist](https://gist.github.com/samdmarshall/17f4e66b5e2e579fd396) from [@samdmarshall](https://github.com/samdmarshall): another example of enumerating malloc blocks.
- [Non-pointer isa](http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html): an explanation of changes to the isa field on iOS for ARM64 and mention of the useful `objc_debug_isa_class_mask` variable.
- [GZIP](https://github.com/nicklockwood/GZIP): A library for compressing/decompressing data on iOS using libz.
- [FMDB](https://github.com/ccgus/fmdb): This is an Objective-C wrapper around SQLite
- [FMDB](https://github.com/ccgus/fmdb): This is an Objective-C wrapper around SQLite.
- [InAppViewDebugger](https://github.com/indragiek/InAppViewDebugger): The inspiration and reference implementation for FLEX 4's 3D view explorer, by @indragiek.