FLEXIvar.m 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. //
  2. // FLEXIvar.m
  3. // FLEX
  4. //
  5. // Derived from MirrorKit.
  6. // Created by Tanner on 6/30/15.
  7. // Copyright (c) 2020 FLEX Team. All rights reserved.
  8. //
  9. #import "FLEXIvar.h"
  10. #import "FLEXRuntimeUtility.h"
  11. #import "FLEXRuntimeSafety.h"
  12. #import "FLEXTypeEncodingParser.h"
  13. #import "NSString+FLEX.h"
  14. #include "FLEXObjcInternal.h"
  15. #include <dlfcn.h>
  16. @interface FLEXIvar () {
  17. NSString *_flex_description;
  18. }
  19. @end
  20. @implementation FLEXIvar
  21. #pragma mark Initializers
  22. - (id)init {
  23. [NSException
  24. raise:NSInternalInconsistencyException
  25. format:@"Class instance should not be created with -init"
  26. ];
  27. return nil;
  28. }
  29. + (instancetype)ivar:(Ivar)ivar {
  30. return [[self alloc] initWithIvar:ivar];
  31. }
  32. + (instancetype)named:(NSString *)name onClass:(Class)cls {
  33. Ivar _Nullable ivar = class_getInstanceVariable(cls, name.UTF8String);
  34. NSAssert(ivar, @"Cannot find ivar with name %@ on class %@", name, cls);
  35. return [self ivar:ivar];
  36. }
  37. - (id)initWithIvar:(Ivar)ivar {
  38. NSParameterAssert(ivar);
  39. self = [super init];
  40. if (self) {
  41. _objc_ivar = ivar;
  42. [self examine];
  43. }
  44. return self;
  45. }
  46. #pragma mark Other
  47. - (NSString *)description {
  48. if (!_flex_description) {
  49. NSString *readableType = [FLEXRuntimeUtility readableTypeForEncoding:self.typeEncoding];
  50. _flex_description = [FLEXRuntimeUtility appendName:self.name toType:readableType];
  51. }
  52. return _flex_description;
  53. }
  54. - (NSString *)debugDescription {
  55. return [NSString stringWithFormat:@"<%@ name=%@, encoding=%@, offset=%ld>",
  56. NSStringFromClass(self.class), self.name, self.typeEncoding, (long)self.offset];
  57. }
  58. - (void)examine {
  59. _name = @(ivar_getName(self.objc_ivar) ?: "(nil)");
  60. _offset = ivar_getOffset(self.objc_ivar);
  61. _typeEncoding = @(ivar_getTypeEncoding(self.objc_ivar) ?: "");
  62. NSString *typeForDetails = _typeEncoding;
  63. NSString *sizeForDetails = nil;
  64. if (_typeEncoding.length) {
  65. _type = (FLEXTypeEncoding)[_typeEncoding characterAtIndex:0];
  66. FLEXGetSizeAndAlignment(_typeEncoding.UTF8String, &_size, nil);
  67. sizeForDetails = [@(_size).stringValue stringByAppendingString:@" bytes"];
  68. } else {
  69. _type = FLEXTypeEncodingNull;
  70. typeForDetails = @"no type info";
  71. sizeForDetails = @"unknown size";
  72. }
  73. Dl_info exeInfo;
  74. if (dladdr(_objc_ivar, &exeInfo)) {
  75. _imagePath = exeInfo.dli_fname ? @(exeInfo.dli_fname) : nil;
  76. }
  77. _details = [NSString stringWithFormat:
  78. @"%@, offset %@ — %@",
  79. sizeForDetails, @(_offset), typeForDetails
  80. ];
  81. }
  82. - (id)getValue:(id)target {
  83. id value = nil;
  84. if (!FLEXIvarIsSafe(_objc_ivar) ||
  85. _type == FLEXTypeEncodingNull ||
  86. FLEXPointerIsTaggedPointer(target)) {
  87. return nil;
  88. }
  89. #ifdef __arm64__
  90. // See http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html
  91. if (self.type == FLEXTypeEncodingObjcClass && [self.name isEqualToString:@"isa"]) {
  92. value = object_getClass(target);
  93. } else
  94. #endif
  95. if (self.type == FLEXTypeEncodingObjcObject || self.type == FLEXTypeEncodingObjcClass) {
  96. value = object_getIvar(target, self.objc_ivar);
  97. } else {
  98. void *pointer = (__bridge void *)target + self.offset;
  99. value = [FLEXRuntimeUtility
  100. valueForPrimitivePointer:pointer
  101. objCType:self.typeEncoding.UTF8String
  102. ];
  103. }
  104. return value;
  105. }
  106. - (void)setValue:(id)value onObject:(id)target {
  107. const char *typeEncodingCString = self.typeEncoding.UTF8String;
  108. if (self.type == FLEXTypeEncodingObjcObject) {
  109. object_setIvar(target, self.objc_ivar, value);
  110. } else if ([value isKindOfClass:[NSValue class]]) {
  111. // Primitive - unbox the NSValue.
  112. NSValue *valueValue = (NSValue *)value;
  113. // Make sure that the box contained the correct type.
  114. NSAssert(
  115. strcmp(valueValue.objCType, typeEncodingCString) == 0,
  116. @"Type encoding mismatch (value: %s; ivar: %s) in setting ivar named: %@ on object: %@",
  117. valueValue.objCType, typeEncodingCString, self.name, target
  118. );
  119. NSUInteger bufferSize = 0;
  120. if (FLEXGetSizeAndAlignment(typeEncodingCString, &bufferSize, NULL)) {
  121. void *buffer = calloc(bufferSize, 1);
  122. [valueValue getValue:buffer];
  123. void *pointer = (__bridge void *)target + self.offset;
  124. memcpy(pointer, buffer, bufferSize);
  125. free(buffer);
  126. }
  127. }
  128. }
  129. - (id)getPotentiallyUnboxedValue:(id)target {
  130. NSString *type = self.typeEncoding;
  131. if (type.flex_typeIsNonObjcPointer && type.flex_pointeeType != FLEXTypeEncodingVoid) {
  132. return [self getValue:target];
  133. }
  134. return [FLEXRuntimeUtility
  135. potentiallyUnwrapBoxedPointer:[self getValue:target]
  136. type:type.UTF8String
  137. ];
  138. }
  139. @end