RCTParserUtils.m 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import "RCTParserUtils.h"
  8. #import "RCTLog.h"
  9. @implementation RCTParserUtils
  10. BOOL RCTReadChar(const char **input, char c)
  11. {
  12. if (**input == c) {
  13. (*input)++;
  14. return YES;
  15. }
  16. return NO;
  17. }
  18. BOOL RCTReadString(const char **input, const char *string)
  19. {
  20. int i;
  21. for (i = 0; string[i] != 0; i++) {
  22. if (string[i] != (*input)[i]) {
  23. return NO;
  24. }
  25. }
  26. *input += i;
  27. return YES;
  28. }
  29. void RCTSkipWhitespace(const char **input)
  30. {
  31. while (isspace(**input)) {
  32. (*input)++;
  33. }
  34. }
  35. static BOOL RCTIsIdentifierHead(const char c)
  36. {
  37. return isalpha(c) || c == '_';
  38. }
  39. static BOOL RCTIsIdentifierTail(const char c)
  40. {
  41. return isalnum(c) || c == '_';
  42. }
  43. BOOL RCTParseArgumentIdentifier(const char **input, NSString **string)
  44. {
  45. const char *start = *input;
  46. do {
  47. if (!RCTIsIdentifierHead(**input)) {
  48. return NO;
  49. }
  50. (*input)++;
  51. while (RCTIsIdentifierTail(**input)) {
  52. (*input)++;
  53. }
  54. // allow namespace resolution operator
  55. } while (RCTReadString(input, "::"));
  56. if (string) {
  57. *string = [[NSString alloc] initWithBytes:start length:(NSInteger)(*input - start) encoding:NSASCIIStringEncoding];
  58. }
  59. return YES;
  60. }
  61. BOOL RCTParseSelectorIdentifier(const char **input, NSString **string)
  62. {
  63. const char *start = *input;
  64. if (!RCTIsIdentifierHead(**input)) {
  65. return NO;
  66. }
  67. (*input)++;
  68. while (RCTIsIdentifierTail(**input)) {
  69. (*input)++;
  70. }
  71. if (string) {
  72. *string = [[NSString alloc] initWithBytes:start length:(NSInteger)(*input - start) encoding:NSASCIIStringEncoding];
  73. }
  74. return YES;
  75. }
  76. static BOOL RCTIsCollectionType(NSString *type)
  77. {
  78. static NSSet *collectionTypes;
  79. static dispatch_once_t onceToken;
  80. dispatch_once(&onceToken, ^{
  81. collectionTypes = [[NSSet alloc] initWithObjects:@"NSArray", @"NSSet", @"NSDictionary", nil];
  82. });
  83. return [collectionTypes containsObject:type];
  84. }
  85. NSString *RCTParseType(const char **input)
  86. {
  87. NSString *type;
  88. RCTParseArgumentIdentifier(input, &type);
  89. RCTSkipWhitespace(input);
  90. if (RCTReadChar(input, '<')) {
  91. RCTSkipWhitespace(input);
  92. NSString *subtype = RCTParseType(input);
  93. if (RCTIsCollectionType(type)) {
  94. if ([type isEqualToString:@"NSDictionary"]) {
  95. // Dictionaries have both a key *and* value type, but the key type has
  96. // to be a string for JSON, so we only care about the value type
  97. if (RCT_DEBUG && ![subtype isEqualToString:@"NSString"]) {
  98. RCTLogError(@"%@ is not a valid key type for a JSON dictionary", subtype);
  99. }
  100. RCTSkipWhitespace(input);
  101. RCTReadChar(input, ',');
  102. RCTSkipWhitespace(input);
  103. subtype = RCTParseType(input);
  104. }
  105. if (![subtype isEqualToString:@"id"]) {
  106. type = [type stringByReplacingCharactersInRange:(NSRange){0, 2 /* "NS" */} withString:subtype];
  107. }
  108. } else {
  109. // It's a protocol rather than a generic collection - ignore it
  110. }
  111. RCTSkipWhitespace(input);
  112. RCTReadChar(input, '>');
  113. }
  114. RCTSkipWhitespace(input);
  115. if (!RCTReadChar(input, '*')) {
  116. RCTReadChar(input, '&');
  117. }
  118. return type;
  119. }
  120. @end