diff --git a/Chisel/Chisel.xcodeproj/project.pbxproj b/Chisel/Chisel.xcodeproj/project.pbxproj new file mode 100644 index 0000000..95110e1 --- /dev/null +++ b/Chisel/Chisel.xcodeproj/project.pbxproj @@ -0,0 +1,439 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 7A04088C1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A04088A1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.h */; }; + 7A04088D1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7A04088B1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm */; }; + 7A20C4B31DFDB8D200C89959 /* CHLPredicateTools.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A20C4B11DFDB8D200C89959 /* CHLPredicateTools.h */; }; + 7A20C4B41DFDB8D200C89959 /* CHLPredicateTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A20C4B21DFDB8D200C89959 /* CHLPredicateTools.m */; }; + 7ABD17951DF7F998006118F8 /* Chisel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ABD178B1DF7F998006118F8 /* Chisel.framework */; }; + 7ABD179A1DF7F998006118F8 /* ChiselTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD17991DF7F998006118F8 /* ChiselTests.m */; }; + 7ABD179C1DF7F998006118F8 /* Chisel.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ABD178E1DF7F998006118F8 /* Chisel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7ABD17A71DF7F9FD006118F8 /* CHLAllocations.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ABD17A51DF7F9FD006118F8 /* CHLAllocations.h */; }; + 7ABD17A81DF7F9FD006118F8 /* CHLAllocations.c in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD17A61DF7F9FD006118F8 /* CHLAllocations.c */; }; + 7ABD17AB1DF7FCF9006118F8 /* CHLObjcInstances.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ABD17A91DF7FCF9006118F8 /* CHLObjcInstances.h */; }; + 7ABD17AC1DF7FCF9006118F8 /* CHLObjcInstances.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7ABD17AA1DF7FCF9006118F8 /* CHLObjcInstances.mm */; }; + 7ABD17AF1DF88520006118F8 /* zone_allocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ABD17AD1DF88520006118F8 /* zone_allocator.h */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7ABD17961DF7F998006118F8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7ABD17821DF7F998006118F8 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7ABD178A1DF7F998006118F8; + remoteInfo = Chisel; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 7A04088A1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLObjcInstanceCommands.h; sourceTree = ""; }; + 7A04088B1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHLObjcInstanceCommands.mm; sourceTree = ""; }; + 7A20C4B11DFDB8D200C89959 /* CHLPredicateTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLPredicateTools.h; sourceTree = ""; }; + 7A20C4B21DFDB8D200C89959 /* CHLPredicateTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CHLPredicateTools.m; sourceTree = ""; }; + 7ABD178B1DF7F998006118F8 /* Chisel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Chisel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7ABD178E1DF7F998006118F8 /* Chisel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Chisel.h; sourceTree = ""; }; + 7ABD178F1DF7F998006118F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7ABD17941DF7F998006118F8 /* ChiselTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChiselTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 7ABD17991DF7F998006118F8 /* ChiselTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChiselTests.m; sourceTree = ""; }; + 7ABD179B1DF7F998006118F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7ABD17A51DF7F9FD006118F8 /* CHLAllocations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLAllocations.h; sourceTree = ""; }; + 7ABD17A61DF7F9FD006118F8 /* CHLAllocations.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CHLAllocations.c; sourceTree = ""; }; + 7ABD17A91DF7FCF9006118F8 /* CHLObjcInstances.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CHLObjcInstances.h; sourceTree = ""; }; + 7ABD17AA1DF7FCF9006118F8 /* CHLObjcInstances.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CHLObjcInstances.mm; sourceTree = ""; }; + 7ABD17AD1DF88520006118F8 /* zone_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = zone_allocator.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7ABD17871DF7F998006118F8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7ABD17911DF7F998006118F8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7ABD17951DF7F998006118F8 /* Chisel.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7ABD17811DF7F998006118F8 = { + isa = PBXGroup; + children = ( + 7ABD178D1DF7F998006118F8 /* Chisel */, + 7ABD17981DF7F998006118F8 /* ChiselTests */, + 7ABD178C1DF7F998006118F8 /* Products */, + ); + sourceTree = ""; + }; + 7ABD178C1DF7F998006118F8 /* Products */ = { + isa = PBXGroup; + children = ( + 7ABD178B1DF7F998006118F8 /* Chisel.framework */, + 7ABD17941DF7F998006118F8 /* ChiselTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 7ABD178D1DF7F998006118F8 /* Chisel */ = { + isa = PBXGroup; + children = ( + 7ABD178E1DF7F998006118F8 /* Chisel.h */, + 7ABD178F1DF7F998006118F8 /* Info.plist */, + 7ABD17AD1DF88520006118F8 /* zone_allocator.h */, + 7ABD17A51DF7F9FD006118F8 /* CHLAllocations.h */, + 7ABD17A61DF7F9FD006118F8 /* CHLAllocations.c */, + 7ABD17A91DF7FCF9006118F8 /* CHLObjcInstances.h */, + 7ABD17AA1DF7FCF9006118F8 /* CHLObjcInstances.mm */, + 7A04088A1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.h */, + 7A04088B1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm */, + 7A20C4B11DFDB8D200C89959 /* CHLPredicateTools.h */, + 7A20C4B21DFDB8D200C89959 /* CHLPredicateTools.m */, + ); + path = Chisel; + sourceTree = ""; + }; + 7ABD17981DF7F998006118F8 /* ChiselTests */ = { + isa = PBXGroup; + children = ( + 7ABD17991DF7F998006118F8 /* ChiselTests.m */, + 7ABD179B1DF7F998006118F8 /* Info.plist */, + ); + path = ChiselTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 7ABD17881DF7F998006118F8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 7ABD17A71DF7F9FD006118F8 /* CHLAllocations.h in Headers */, + 7ABD179C1DF7F998006118F8 /* Chisel.h in Headers */, + 7A20C4B31DFDB8D200C89959 /* CHLPredicateTools.h in Headers */, + 7A04088C1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.h in Headers */, + 7ABD17AF1DF88520006118F8 /* zone_allocator.h in Headers */, + 7ABD17AB1DF7FCF9006118F8 /* CHLObjcInstances.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 7ABD178A1DF7F998006118F8 /* Chisel */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7ABD179F1DF7F998006118F8 /* Build configuration list for PBXNativeTarget "Chisel" */; + buildPhases = ( + 7ABD17861DF7F998006118F8 /* Sources */, + 7ABD17871DF7F998006118F8 /* Frameworks */, + 7ABD17881DF7F998006118F8 /* Headers */, + 7ABD17891DF7F998006118F8 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Chisel; + productName = Chisel; + productReference = 7ABD178B1DF7F998006118F8 /* Chisel.framework */; + productType = "com.apple.product-type.framework"; + }; + 7ABD17931DF7F998006118F8 /* ChiselTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7ABD17A21DF7F998006118F8 /* Build configuration list for PBXNativeTarget "ChiselTests" */; + buildPhases = ( + 7ABD17901DF7F998006118F8 /* Sources */, + 7ABD17911DF7F998006118F8 /* Frameworks */, + 7ABD17921DF7F998006118F8 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 7ABD17971DF7F998006118F8 /* PBXTargetDependency */, + ); + name = ChiselTests; + productName = ChiselTests; + productReference = 7ABD17941DF7F998006118F8 /* ChiselTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7ABD17821DF7F998006118F8 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0810; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 7ABD178A1DF7F998006118F8 = { + CreatedOnToolsVersion = 8.1; + ProvisioningStyle = Automatic; + }; + 7ABD17931DF7F998006118F8 = { + CreatedOnToolsVersion = 8.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 7ABD17851DF7F998006118F8 /* Build configuration list for PBXProject "Chisel" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7ABD17811DF7F998006118F8; + productRefGroup = 7ABD178C1DF7F998006118F8 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7ABD178A1DF7F998006118F8 /* Chisel */, + 7ABD17931DF7F998006118F8 /* ChiselTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7ABD17891DF7F998006118F8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7ABD17921DF7F998006118F8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7ABD17861DF7F998006118F8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7ABD17A81DF7F9FD006118F8 /* CHLAllocations.c in Sources */, + 7ABD17AC1DF7FCF9006118F8 /* CHLObjcInstances.mm in Sources */, + 7A04088D1DF9A2C7009C5BFA /* CHLObjcInstanceCommands.mm in Sources */, + 7A20C4B41DFDB8D200C89959 /* CHLPredicateTools.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7ABD17901DF7F998006118F8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7ABD179A1DF7F998006118F8 /* ChiselTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 7ABD17971DF7F998006118F8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7ABD178A1DF7F998006118F8 /* Chisel */; + targetProxy = 7ABD17961DF7F998006118F8 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 7ABD179D1DF7F998006118F8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = c11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 7ABD179E1DF7F998006118F8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = c11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 7ABD17A01DF7F998006118F8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Chisel/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.Chisel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 7ABD17A11DF7F998006118F8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Chisel/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.Chisel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 7ABD17A31DF7F998006118F8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = ChiselTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.ChiselTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 7ABD17A41DF7F998006118F8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = ChiselTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.ChiselTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7ABD17851DF7F998006118F8 /* Build configuration list for PBXProject "Chisel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7ABD179D1DF7F998006118F8 /* Debug */, + 7ABD179E1DF7F998006118F8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7ABD179F1DF7F998006118F8 /* Build configuration list for PBXNativeTarget "Chisel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7ABD17A01DF7F998006118F8 /* Debug */, + 7ABD17A11DF7F998006118F8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7ABD17A21DF7F998006118F8 /* Build configuration list for PBXNativeTarget "ChiselTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7ABD17A31DF7F998006118F8 /* Debug */, + 7ABD17A41DF7F998006118F8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7ABD17821DF7F998006118F8 /* Project object */; +} diff --git a/Chisel/Chisel.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Chisel/Chisel.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..b6c5750 --- /dev/null +++ b/Chisel/Chisel.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Chisel/Chisel/CHLAllocations.c b/Chisel/Chisel/CHLAllocations.c new file mode 100644 index 0000000..1c8c805 --- /dev/null +++ b/Chisel/Chisel/CHLAllocations.c @@ -0,0 +1,38 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include "CHLAllocations.h" + +static kern_return_t reader(__unused task_t remote_task, vm_address_t remote_address, __unused vm_size_t size, void **local_memory) +{ + *local_memory = (void *)remote_address; + return KERN_SUCCESS; +} + +typedef struct { + CHLRangeHandler handler; + void *context; +} RangeEnumeratorArgs; + +static void rangeEnumerator(__unused task_t task, void *context, __unused unsigned type, vm_range_t *ranges, unsigned int count) +{ + const RangeEnumeratorArgs *args = (RangeEnumeratorArgs *)context; + for (unsigned int i = 0; i < count; ++i) { + args->handler(ranges[i], args->context); + } +} + +void CHLScanAllocations(CHLRangeHandler handler, void *context, const malloc_zone_t *sideZone) +{ + vm_address_t *zones; + unsigned int count; + malloc_get_all_zones(TASK_NULL, &reader, &zones, &count); + + RangeEnumeratorArgs args = {handler, context}; + + for (unsigned int i = 0; i < count; ++i) { + malloc_zone_t *zone = (malloc_zone_t *)zones[i]; + if (zone != sideZone) { + zone->introspect->enumerator(TASK_NULL, &args, MALLOC_PTR_IN_USE_RANGE_TYPE, (vm_address_t)zone, reader, rangeEnumerator); + } + } +} diff --git a/Chisel/Chisel/CHLAllocations.h b/Chisel/Chisel/CHLAllocations.h new file mode 100644 index 0000000..5cd8715 --- /dev/null +++ b/Chisel/Chisel/CHLAllocations.h @@ -0,0 +1,17 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef void (*CHLRangeHandler)(vm_range_t range, void *context); + +// Enumerate live allocations in all malloc zones. If callers allocate memory in the handler, those +// allocations should be within the given `sideZone`. +void CHLScanAllocations(CHLRangeHandler handler, void *context, const malloc_zone_t *sideZone); + +#if defined(__cplusplus) +} +#endif diff --git a/Chisel/Chisel/CHLObjcInstanceCommands.h b/Chisel/Chisel/CHLObjcInstanceCommands.h new file mode 100644 index 0000000..e19f979 --- /dev/null +++ b/Chisel/Chisel/CHLObjcInstanceCommands.h @@ -0,0 +1,15 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +@class NSPredicate; + +#if defined(__cplusplus) +extern "C" { +#endif + +// Debugger interface for finding and printing instances of a type, with an optional predicate. +// The predicate format is anything supported by NSPredicate. +void PrintInstances(const char *type, const char *pred); + +#if defined(__cplusplus) +} +#endif diff --git a/Chisel/Chisel/CHLObjcInstanceCommands.mm b/Chisel/Chisel/CHLObjcInstanceCommands.mm new file mode 100644 index 0000000..faa6c20 --- /dev/null +++ b/Chisel/Chisel/CHLObjcInstanceCommands.mm @@ -0,0 +1,183 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "CHLObjcInstanceCommands.h" + +#include +#include + +#import +#import + +#import "CHLObjcInstances.h" +#import "CHLPredicateTools.h" +#include "zone_allocator.h" + +#if __has_feature(objc_arc) +#error Disable ARC for this file +#endif + +struct IsValidArgs { + const std::unordered_set &classSet; + bool isValid = true; +}; + +static void isValidObject(const void *value, void *context) +{ + const auto args = reinterpret_cast(context); + if (!args->isValid) { + return; + } + + vm_range_t range = {(vm_address_t)value, malloc_size(value)}; + if (CHLViableObjcInstance(range, args->classSet) == nil) { + args->isValid = false; + } +} + +static void isValidKeyValue(const void *key, const void *value, void *context) +{ + const auto args = reinterpret_cast(context); + isValidObject(key, context); + if (args->isValid) { + isValidObject(value, context); + } +} + +static bool predicatePrecheck(id obj, const std::unordered_set &classSet) +{ + IsValidArgs args{classSet}; + + if ([obj isKindOfClass:objc_getClass("__NSCFDictionary")]) { + CFDictionaryApplyFunction((CFDictionaryRef)obj, &isValidKeyValue, &args); + } else if ([obj isKindOfClass:objc_getClass("__NSCFSet")]) { + CFSetApplyFunction((CFSetRef)obj, &isValidObject, &args); + } else { + // Skip classes containing NSPlaceholder. + // TODO: Figure out better way to ignore invalid instances. + char *name = (char *)object_getClassName(obj); + while (*name == '_') ++name; + if (strncmp(name, "NSPlaceholder", sizeof("NSPlaceholder") - 1) == 0) { + args.isValid = false; + } + } + + if (!args.isValid && getenv("FINDINSTANCES_DEBUG")) { + printf("%p has class %s but contains non objc data\n", obj, object_getClassName(obj)); + } + + return args.isValid; +} + +static void printObject(id obj, NSSet *keyPaths) { + printf("<%s: %p", object_getClassName(obj), obj); + for (NSString *keyPath in keyPaths) { + printf("; %s = %s", keyPath.UTF8String, [[obj valueForKeyPath:keyPath] description].UTF8String); + } + printf(">\n"); +} + +static bool objectIsMatch(NSPredicate *predicate, id obj, const std::unordered_set &classSet) +{ + if (!predicate) { + return true; + } + + bool debug = getenv("FINDINSTANCES_DEBUG"); + + if (!predicatePrecheck(obj, classSet)) { + if (debug) { + printf("%p has class %s but has non objc contents\n", obj, object_getClassName(obj)); + } + return false; + } + + @try { + return [predicate evaluateWithObject:obj]; + } @catch (...) { + if (debug) { + printf("%p has class %s but failed predicate evaluation\n", obj, object_getClassName(obj)); + } + return false; + } +} + +// Function reimplementation of +[NSObject isSubclassOf:] to avoid the objc runtime side +// effects that can happen when calling methods, like realizing classes, +initialize, etc. +static bool isSubclassOf(Class base, Class target) +{ + for (auto cls = base; cls != Nil; cls = class_getSuperclass(cls)) { + if (cls == target) { + return true; + } + } + return false; +} + +// Function reimplementation of +[NSObject conformsToProtocol:] to avoid the objc runtime side +// effects that can happen when calling methods, like realizing classes, +initialize, etc. +static bool conformsToProtocol(Class base, Protocol *protocol) +{ + for (auto cls = base; cls != Nil; cls = class_getSuperclass(cls)) { + if (class_conformsToProtocol(cls, protocol)) { + return true; + } + } + return false; +} + +void PrintInstances(const char *type, const char *pred) +{ + NSPredicate *predicate = nil; + if (pred != nullptr && *pred != '\0') { + predicate = [NSPredicate predicateWithFormat:@(pred)]; + } + + const std::unordered_set objcClasses = CHLObjcClassSet(); + std::unordered_set matchClasses; + + Protocol *protocol = objc_getProtocol(type); + if (protocol != nullptr && strcmp("NSObject", type) != 0) { + for (auto cls : objcClasses) { + if (conformsToProtocol(cls, protocol)) { + matchClasses.insert(cls); + } + } + } + + if (type[0] == '*') { + ++type; + Class cls = objc_getClass(type); + if (cls != nullptr) { + matchClasses.insert(cls); + } + } else if (Class kind = objc_getClass(type)) { + // This could be optimized for type == "NSObject", but it won't be a typical search. + for (auto cls : objcClasses) { + if (isSubclassOf(cls, kind)) { + matchClasses.insert(cls); + } + } + } + + if (matchClasses.empty()) { + // TODO: Accept name of library/module, and list instances of classes defined there. + printf("Unknown type: %s\n", type); + return; + } + + NSSet *keyPaths = CHLVariableKeyPaths(predicate); + + std::vector> instances = CHLScanObjcInstances(matchClasses); + unsigned int matches = 0; + + for (id obj : instances) { + if (objectIsMatch(predicate, obj, objcClasses)) { + ++matches; + printObject(obj, keyPaths); + } + } + + if (matches > 1) { + printf("%d matches\n", matches); + } +} diff --git a/Chisel/Chisel/CHLObjcInstances.h b/Chisel/Chisel/CHLObjcInstances.h new file mode 100644 index 0000000..d0545d0 --- /dev/null +++ b/Chisel/Chisel/CHLObjcInstances.h @@ -0,0 +1,21 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#if defined(__cplusplus) + +#include +#include +#include + +#include "zone_allocator.h" + +// Create a set containing all known Classes. +std::unordered_set CHLObjcClassSet(); + +// Enumerates the heap and returns all objects that appear to be legitimate. +std::vector> CHLScanObjcInstances(const std::unordered_set &classSet); + +// Performs a number of heuristic checks on the memory range, to determine if the memory appears to +// be a viable Objective-C object. +id CHLViableObjcInstance(vm_range_t range, const std::unordered_set &classSet); + +#endif diff --git a/Chisel/Chisel/CHLObjcInstances.mm b/Chisel/Chisel/CHLObjcInstances.mm new file mode 100644 index 0000000..4d7fef3 --- /dev/null +++ b/Chisel/Chisel/CHLObjcInstances.mm @@ -0,0 +1,164 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "CHLObjcInstances.h" + +#import + +#include "CHLAllocations.h" + +#include +#include +#include +#include +#if !defined(__LP64__) +using mach_header_t = mach_header; +#else +using mach_header_t = mach_header_64; +#endif + +#include +#include + +#if __has_feature(objc_arc) +#error Disable ARC for this file +#endif + +// Informal protocol to make it easy to call -_isDeallocating +@interface NSObject (Private) +- (BOOL)_isDeallocating; +@end + +static id embeddedObjcInstance(vm_range_t range) { + Dl_info info; + bool aligned = range.address % alignof(void *) == 0; + uint8_t *pointer = (uint8_t *)range.address; + if (aligned && dladdr(pointer, &info)) { + unsigned long size = 0; + uint8_t *start = getsectiondata((mach_header_t *)info.dli_fbase, SEG_DATA, "__cfstring", &size); + uint8_t *end = start + size; + if (start <= pointer || pointer < end) { + // Found NSString/CFString constant. + return reinterpret_cast(range.address); + } + } + return nil; +} + +// TODO: Should this cache results instead of repeated lookups. +bool isFixedSizeClass(Class cls) { + const auto meta = object_getClass(cls); + const auto root = objc_getMetaClass("NSObject"); + + SEL allocs[] = { @selector(allocWithZone:), @selector(alloc) }; + for (const auto &sel : allocs) { + IMP imp = class_getMethodImplementation(meta, sel); + if (imp != class_getMethodImplementation(root, sel)) { + // Class overrides NSObject alloc method, may not have fixed sizes. + return false; + } + } + + return true; +} + +// Runs a number of heuristics on the given address. Returns nil if any heuristic fails, otherwise +// returns that address casted as an object. +// +// Currently the heuristics don't fully guarantee that the returned object is an actual object, but +// when using MallocScribble=1, false positives are unlikely. Further, callers will generally do +// higher level filtering, for example checking a value on the object, which can further eliminate +// false positives. +// +// There's also one known false negative case, NSConcreteValue can store data in the malloc memory +// beyond its instance size. Currently this false negative is allowed. +id CHLViableObjcInstance(vm_range_t range, const std::unordered_set &classSet) +{ + // Check if this address points to an object embedded into Mach-O. + if (range.size == 0) { + return embeddedObjcInstance(range); + } + + id obj = reinterpret_cast(range.address); + + // It's safe to call object_getClass on memory that isn't objc objects. + // Check that the returned Class points to an expected class. + Class cls = object_getClass(obj); + if (classSet.find(cls) == classSet.end()) { + return nil; + } + + // Instance size is the byte count needed for an object's ivars, plus any padding. + // Allocation size is the byte count that malloc will actually allocate for instances of a Class. + const auto instanceSize = class_getInstanceSize(cls); + const auto expectedAllocationSize = malloc_good_size(instanceSize); + const auto extraSize = expectedAllocationSize - instanceSize; + + const bool debug = getenv("FINDINSTANCES_DEBUG") != NULL; + + if (range.size < expectedAllocationSize) { + if (debug) { + printf("%p has class %s but is too small\n", obj, class_getName(cls)); + } + return nil; + } + + if (range.size > expectedAllocationSize && isFixedSizeClass(cls)) { + // Range is too big and the class has no way of allocating larger instances. + if (debug) { + printf("%p has fixed size class %s but is too large\n", obj, class_getName(cls)); + } + return nil; + } + + if (range.size == expectedAllocationSize && extraSize) { + // ObjC instances are allocated with calloc, memory beyond the instance size should be zeros. + // Some classes have been known to store data in the extra space, ex NSConcreteValue. + static const unsigned char ZEROS[1024] = {0}; + auto extra = object_getIndexedIvars(obj); + auto compareSize = std::min(extraSize, sizeof(ZEROS)); + if (memcmp(extra, &ZEROS, compareSize) != 0) { + if (debug) { + printf("%p has class %s but has non-zero memory\n", obj, class_getName(cls)); + } + return nil; + } + } + + // Ignore deallocating objects. + if ([obj _isDeallocating]) { + return nil; + } + + return obj; +} + +struct FindViableObjcInstancesArgs { + const std::unordered_set &classSet; + std::vector> &instances; +}; + +static void findViableObjcInstances(vm_range_t range, void *context) +{ + const auto args = reinterpret_cast(context); + id obj = CHLViableObjcInstance(range, args->classSet); + if (obj != nil) { + args->instances.push_back(obj); + } +} + +std::vector> CHLScanObjcInstances(const std::unordered_set &classSet) +{ + std::vector> instances; + FindViableObjcInstancesArgs args{classSet, instances}; + CHLScanAllocations(&findViableObjcInstances, &args, instances.get_allocator().zone()); + return instances; +} + +std::unordered_set CHLObjcClassSet() +{ + unsigned int count = 0; + auto classList = objc_copyClassList(&count); + std::unordered_set classSet{classList, classList + count, count}; + free(classList); + return classSet; +} diff --git a/Chisel/Chisel/CHLPredicateTools.h b/Chisel/Chisel/CHLPredicateTools.h new file mode 100644 index 0000000..9dd473e --- /dev/null +++ b/Chisel/Chisel/CHLPredicateTools.h @@ -0,0 +1,13 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +#if defined(__cplusplus) +extern "C" { +#endif + +NSSet *CHLVariableKeyPaths(NSPredicate *predicate); + +#if defined(__cplusplus) +} +#endif diff --git a/Chisel/Chisel/CHLPredicateTools.m b/Chisel/Chisel/CHLPredicateTools.m new file mode 100644 index 0000000..1145cc5 --- /dev/null +++ b/Chisel/Chisel/CHLPredicateTools.m @@ -0,0 +1,53 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import "CHLPredicateTools.h" + +static bool isEqualToConstantComparison(NSComparisonPredicate *predicate) +{ + bool equality = predicate.predicateOperatorType == NSEqualToPredicateOperatorType; + bool direct = predicate.comparisonPredicateModifier == NSDirectPredicateModifier; + bool constantLeft = predicate.leftExpression.expressionType == NSConstantValueExpressionType; + bool constantRight = predicate.rightExpression.expressionType == NSConstantValueExpressionType; + return equality && direct && (constantLeft || constantRight); +} + +NSSet *CHLVariableKeyPaths(NSPredicate *predicate) +{ + if (predicate == nil) { + return nil; + } + + NSMutableSet *keyPaths = [NSMutableSet new]; + + NSMutableArray *predicateStack = [NSMutableArray arrayWithObject:predicate]; + while (predicateStack.count > 0) { + NSPredicate *subpredicate = [predicateStack lastObject]; + [predicateStack removeLastObject]; + + if ([subpredicate isKindOfClass:[NSCompoundPredicate class]]) { + NSCompoundPredicate *compoundPredicate = (NSCompoundPredicate *)subpredicate; + [predicateStack addObjectsFromArray:compoundPredicate.subpredicates]; + continue; + } + + if ([subpredicate isKindOfClass:[NSComparisonPredicate class]]) { + NSComparisonPredicate *comparisonPredicate = (NSComparisonPredicate *)subpredicate; + + if (isEqualToConstantComparison(comparisonPredicate)) { + // Keypaths equal to constants are not variable. Skip these to not be noisy. + // ex `username == "jonalan"` or `alpha == 0` + continue; + } + + // TODO: Handle NSFunctionExpressionType + if (comparisonPredicate.leftExpression.expressionType == NSKeyPathExpressionType) { + [keyPaths addObject:comparisonPredicate.leftExpression.keyPath]; + } + if (comparisonPredicate.rightExpression.expressionType == NSKeyPathExpressionType) { + [keyPaths addObject:comparisonPredicate.rightExpression.keyPath]; + } + } + } + + return keyPaths; +} diff --git a/Chisel/Chisel/Chisel.h b/Chisel/Chisel/Chisel.h new file mode 100644 index 0000000..3ba7cdb --- /dev/null +++ b/Chisel/Chisel/Chisel.h @@ -0,0 +1,11 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +//! Project version number for Chisel. +FOUNDATION_EXPORT double ChiselVersionNumber; + +//! Project version string for Chisel. +FOUNDATION_EXPORT const unsigned char ChiselVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import diff --git a/Chisel/Chisel/Info.plist b/Chisel/Chisel/Info.plist new file mode 100644 index 0000000..fbe1e6b --- /dev/null +++ b/Chisel/Chisel/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Chisel/Chisel/zone_allocator.h b/Chisel/Chisel/zone_allocator.h new file mode 100644 index 0000000..cab8ff2 --- /dev/null +++ b/Chisel/Chisel/zone_allocator.h @@ -0,0 +1,43 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#pragma once + +#include + +#include + +template +struct zone_allocator { + using value_type = T; + + T *allocate(std::size_t n) + { + auto allocation = malloc_zone_malloc(_zone.get(), n * sizeof(T)); + return reinterpret_cast(allocation); + } + + void deallocate(T *p, __unused std::size_t n) + { + malloc_zone_free(_zone.get(), p); + } + + const malloc_zone_t *zone() const + { + return _zone.get(); + } + +private: + std::shared_ptr _zone{malloc_create_zone(0x200, 0), &malloc_destroy_zone}; +}; + +template +bool operator==(const zone_allocator &a, const zone_allocator &b) noexcept +{ + return a.zone() == b.zone(); +} + +template +bool operator!=(const zone_allocator &a, const zone_allocator &b) noexcept +{ + return !(a == b); +} diff --git a/Chisel/ChiselTests/ChiselTests.m b/Chisel/ChiselTests/ChiselTests.m new file mode 100644 index 0000000..4ad3af8 --- /dev/null +++ b/Chisel/ChiselTests/ChiselTests.m @@ -0,0 +1,33 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#import + +@interface ChiselTests : XCTestCase + +@end + +@implementation ChiselTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/Chisel/ChiselTests/Info.plist b/Chisel/ChiselTests/Info.plist new file mode 100644 index 0000000..6c6c23c --- /dev/null +++ b/Chisel/ChiselTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + +