RCTCxxMethod.mm 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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 "RCTCxxMethod.h"
  8. #import <React/RCTBridge+Private.h>
  9. #import <React/RCTBridge.h>
  10. #import <React/RCTConvert.h>
  11. #import <React/RCTFollyConvert.h>
  12. #import <cxxreact/JsArgumentHelpers.h>
  13. #import "RCTCxxUtils.h"
  14. #import <memory>
  15. using facebook::xplat::module::CxxModule;
  16. using namespace facebook::react;
  17. @implementation RCTCxxMethod {
  18. std::unique_ptr<CxxModule::Method> _method;
  19. }
  20. - (instancetype)initWithCxxMethod:(const CxxModule::Method &)method
  21. {
  22. if ((self = [super init])) {
  23. _method = std::make_unique<CxxModule::Method>(method);
  24. }
  25. return self;
  26. }
  27. - (const char *)JSMethodName
  28. {
  29. return _method->name.c_str();
  30. }
  31. - (RCTFunctionType)functionType
  32. {
  33. std::string type(_method->getType());
  34. if (type == "sync") {
  35. return RCTFunctionTypeSync;
  36. } else if (type == "async") {
  37. return RCTFunctionTypeNormal;
  38. } else {
  39. return RCTFunctionTypePromise;
  40. }
  41. }
  42. - (id)invokeWithBridge:(RCTBridge *)bridge module:(id)module arguments:(NSArray *)arguments
  43. {
  44. // module is unused except for printing errors. The C++ object it represents
  45. // is also baked into _method.
  46. // the last N arguments are callbacks, according to the Method data. The
  47. // preceding arguments are values which have already been parsed from JS: they
  48. // may be NSNumber (bool, int, double), NSString, NSArray, or NSObject.
  49. CxxModule::Callback first;
  50. CxxModule::Callback second;
  51. if (arguments.count < _method->callbacks) {
  52. RCTLogError(
  53. @"Method %@.%s expects at least %zu arguments, but got %tu",
  54. RCTBridgeModuleNameForClass([module class]),
  55. _method->name.c_str(),
  56. _method->callbacks,
  57. arguments.count);
  58. return nil;
  59. }
  60. if (_method->callbacks >= 1) {
  61. if (![arguments[arguments.count - 1] isKindOfClass:[NSNumber class]]) {
  62. RCTLogError(
  63. @"Argument %tu (%@) of %@.%s should be a function",
  64. arguments.count - 1,
  65. arguments[arguments.count - 1],
  66. RCTBridgeModuleNameForClass([module class]),
  67. _method->name.c_str());
  68. return nil;
  69. }
  70. NSNumber *id1;
  71. if (_method->callbacks == 2) {
  72. if (![arguments[arguments.count - 2] isKindOfClass:[NSNumber class]]) {
  73. RCTLogError(
  74. @"Argument %tu (%@) of %@.%s should be a function",
  75. arguments.count - 2,
  76. arguments[arguments.count - 2],
  77. RCTBridgeModuleNameForClass([module class]),
  78. _method->name.c_str());
  79. return nil;
  80. }
  81. id1 = arguments[arguments.count - 2];
  82. NSNumber *id2 = arguments[arguments.count - 1];
  83. second = ^(std::vector<folly::dynamic> args) {
  84. [bridge enqueueCallback:id2 args:convertFollyDynamicToId(folly::dynamic(args.begin(), args.end()))];
  85. };
  86. } else {
  87. id1 = arguments[arguments.count - 1];
  88. }
  89. first = ^(std::vector<folly::dynamic> args) {
  90. [bridge enqueueCallback:id1 args:convertFollyDynamicToId(folly::dynamic(args.begin(), args.end()))];
  91. };
  92. }
  93. folly::dynamic args = convertIdToFollyDynamic(arguments);
  94. args.resize(args.size() - _method->callbacks);
  95. try {
  96. if (_method->func) {
  97. _method->func(std::move(args), first, second);
  98. return nil;
  99. } else {
  100. auto result = _method->syncFunc(std::move(args));
  101. // TODO: we should convert this to JSValue directly
  102. return convertFollyDynamicToId(result);
  103. }
  104. } catch (const facebook::xplat::JsArgumentException &ex) {
  105. RCTLogError(
  106. @"Method %@.%s argument error: %s",
  107. RCTBridgeModuleNameForClass([module class]),
  108. _method->name.c_str(),
  109. ex.what());
  110. return nil;
  111. }
  112. }
  113. - (NSString *)description
  114. {
  115. return [NSString stringWithFormat:@"<%@: %p; name = %s>", [self class], self, self.JSMethodName];
  116. }
  117. @end