JavaScriptObjCBridge.mm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /****************************************************************************
  2. Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
  3. http://www.cocos.com
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated engine source code (the "Software"), a limited,
  6. worldwide, royalty-free, non-assignable, revocable and non-exclusive license
  7. to use Cocos Creator solely to develop games on your target platforms. You shall
  8. not use Cocos Creator software for developing other software or tools that's
  9. used for developing games. You are not granted to publish, distribute,
  10. sublicense, and/or sell copies of Cocos Creator.
  11. The software or tools in this License Agreement are licensed, not sold.
  12. Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. THE SOFTWARE.
  20. ****************************************************************************/
  21. #include "JavaScriptObjCBridge.h"
  22. #include "cocos/scripting/js-bindings/manual/jsb_conversions.hpp"
  23. #include "cocos/scripting/js-bindings/manual/jsb_global.h"
  24. #include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
  25. #include <string>
  26. #include <vector>
  27. #import <Foundation/Foundation.h>
  28. #define JSO_ERR_OK (0)
  29. #define JSO_ERR_TYPE_NOT_SUPPORT (-1)
  30. #define JSO_ERR_INVALID_ARGUMENTS (-2)
  31. #define JSO_ERR_METHOD_NOT_FOUND (-3)
  32. #define JSO_ERR_EXCEPTION_OCCURRED (-4)
  33. #define JSO_ERR_CLASS_NOT_FOUND (-5)
  34. #define JSO_ERR_VM_FAILURE (-6)
  35. class JavaScriptObjCBridge
  36. {
  37. public:
  38. class CallInfo
  39. {
  40. public:
  41. CallInfo(const char *className, const char* methodName)
  42. :_error(JSO_ERR_OK)
  43. ,_methodName(methodName)
  44. ,_className(className)
  45. {
  46. }
  47. ~CallInfo() {}
  48. int getErrorCode() const
  49. {
  50. return _error;
  51. }
  52. bool execute(const se::ValueArray& argv, se::Value& rval);
  53. private:
  54. se::Value objc_to_seval(id objcVal);
  55. int _error;
  56. std::string _className;
  57. std::string _methodName;
  58. };
  59. };
  60. bool JavaScriptObjCBridge::CallInfo::execute(const se::ValueArray& argv, se::Value& rval)
  61. {
  62. NSString *className =[NSString stringWithCString: _className.c_str() encoding:NSUTF8StringEncoding];
  63. NSString *methodName = [NSString stringWithCString: _methodName.c_str() encoding:NSUTF8StringEncoding];
  64. if(!className || !methodName)
  65. {
  66. _error = JSO_ERR_INVALID_ARGUMENTS;
  67. return false;
  68. }
  69. Class targetClass = NSClassFromString(className);
  70. if(!targetClass)
  71. {
  72. _error = JSO_ERR_CLASS_NOT_FOUND;
  73. return false;
  74. }
  75. SEL methodSel;
  76. methodSel = NSSelectorFromString(methodName);
  77. if (!methodSel)
  78. {
  79. _error = JSO_ERR_METHOD_NOT_FOUND;
  80. return false;
  81. }
  82. methodSel = NSSelectorFromString(methodName);
  83. NSMethodSignature *methodSig = [targetClass methodSignatureForSelector:(SEL)methodSel];
  84. if (methodSig == nil)
  85. {
  86. _error = JSO_ERR_METHOD_NOT_FOUND;
  87. NSLog(@"%@.%@ method isn't found!", className, methodName);
  88. return false;
  89. }
  90. @try
  91. {
  92. int argc = (int)argv.size();
  93. NSUInteger argumentCount = [methodSig numberOfArguments];
  94. if (argumentCount != argc)
  95. {
  96. _error = JSO_ERR_INVALID_ARGUMENTS;
  97. return false;
  98. }
  99. NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
  100. [invocation setTarget:targetClass];
  101. [invocation setSelector:methodSel];
  102. for(int i = 2; i < argc; ++i)
  103. {
  104. std::string argumentType = [methodSig getArgumentTypeAtIndex:i];
  105. const se::Value& arg = argv[i];
  106. /* - (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
  107. *
  108. * Refer to https://developer.apple.com/documentation/foundation/nsinvocation/1437834-setargument?language=objc
  109. *
  110. * This method copies the contents of buffer as the argument at index. The number of bytes copied is determined by the argument size.
  111. * When the argument value is an object, pass a pointer to the variable (or memory) from which the object should be copied:
  112. */
  113. if (arg.isString())
  114. {
  115. NSString* str = [NSString stringWithCString:arg.toString().c_str() encoding:NSUTF8StringEncoding];
  116. [invocation setArgument:&str atIndex:i];
  117. }
  118. else if (arg.isNumber())
  119. {
  120. if (argumentType == @encode(int))
  121. {
  122. int val = arg.toInt32();
  123. [invocation setArgument:&val atIndex:i];
  124. }
  125. else if (argumentType == @encode(long))
  126. {
  127. long val = arg.toLong();
  128. [invocation setArgument:&val atIndex:i];
  129. }
  130. else if (argumentType == @encode(short))
  131. {
  132. short val = arg.toInt16();
  133. [invocation setArgument:&val atIndex:i];
  134. }
  135. else if (argumentType == @encode(unsigned int))
  136. {
  137. unsigned int val = arg.toUint32();
  138. [invocation setArgument:&val atIndex:i];
  139. }
  140. else if (argumentType == @encode(unsigned long))
  141. {
  142. unsigned long val = arg.toUlong();
  143. [invocation setArgument:&val atIndex:i];
  144. }
  145. else if (argumentType == @encode(unsigned short))
  146. {
  147. unsigned short val = arg.toUint16();
  148. [invocation setArgument:&val atIndex:i];
  149. }
  150. else if (argumentType == @encode(float))
  151. {
  152. float val = arg.toFloat();
  153. [invocation setArgument:&val atIndex:i];
  154. }
  155. else if (argumentType == @encode(double))
  156. {
  157. double val = arg.toNumber();
  158. [invocation setArgument:&val atIndex:i];
  159. }
  160. else if (argumentType == @encode(char))
  161. {
  162. char val = arg.toInt8();
  163. [invocation setArgument:&val atIndex:i];
  164. }
  165. else if (argumentType == @encode(unsigned char))
  166. {
  167. unsigned char val = arg.toUint8();
  168. [invocation setArgument:&val atIndex:i];
  169. }
  170. else if (argumentType == "@")
  171. { // NSNumber*
  172. NSNumber* number = [NSNumber numberWithDouble:arg.toNumber()];
  173. [invocation setArgument:&number atIndex:i];
  174. }
  175. else
  176. {
  177. NSLog(@"Unsupported argument type: %s", argumentType.c_str());
  178. _error = JSO_ERR_TYPE_NOT_SUPPORT;
  179. return false;
  180. }
  181. }
  182. else if (arg.isBoolean())
  183. {
  184. if (argumentType == @encode(BOOL))
  185. {
  186. BOOL val = arg.toBoolean() ? YES : NO;
  187. [invocation setArgument:&val atIndex:i];
  188. }
  189. else if (argumentType == @encode(bool))
  190. {
  191. bool val = arg.toBoolean();
  192. [invocation setArgument:&val atIndex:i];
  193. }
  194. else
  195. {
  196. NSLog(@"Unsupported argument type: %s", argumentType.c_str());
  197. _error = JSO_ERR_TYPE_NOT_SUPPORT;
  198. return false;
  199. }
  200. }
  201. else if (arg.isNullOrUndefined())
  202. {
  203. // Don't call [invocation setArgument] will pass nil to relevant Objective-C argument.
  204. }
  205. else
  206. {
  207. NSLog(@"Unsupported argument type, se::Value::Type: %d", (int)arg.getType());
  208. _error = JSO_ERR_TYPE_NOT_SUPPORT;
  209. return false;
  210. }
  211. }
  212. NSUInteger returnLength = [methodSig methodReturnLength];
  213. std::string returnType = [methodSig methodReturnType];
  214. [invocation invoke];
  215. if (returnLength > 0)
  216. {
  217. if (returnType == "@")
  218. {
  219. id ret;
  220. [invocation getReturnValue:&ret];
  221. rval = objc_to_seval(ret);
  222. }
  223. else if (returnType == @encode(BOOL) || returnType == @encode(bool))
  224. {
  225. bool ret;
  226. [invocation getReturnValue:&ret];
  227. rval.setBoolean(ret);
  228. }
  229. else if (returnType == @encode(int))
  230. {
  231. int ret;
  232. [invocation getReturnValue:&ret];
  233. rval.setInt32(ret);
  234. }
  235. else if (returnType == @encode(long))
  236. {
  237. long ret;
  238. [invocation getReturnValue:&ret];
  239. rval.setLong(ret);
  240. }
  241. else if (returnType == @encode(short))
  242. {
  243. short ret;
  244. [invocation getReturnValue:&ret];
  245. rval.setInt16(ret);
  246. }
  247. else if (returnType == @encode(unsigned int))
  248. {
  249. unsigned int ret;
  250. [invocation getReturnValue:&ret];
  251. rval.setUint32(ret);
  252. }
  253. else if (returnType == @encode(unsigned long))
  254. {
  255. unsigned long ret;
  256. [invocation getReturnValue:&ret];
  257. rval.setUlong(ret);
  258. }
  259. else if (returnType == @encode(unsigned short))
  260. {
  261. unsigned short ret;
  262. [invocation getReturnValue:&ret];
  263. rval.setUint16(ret);
  264. }
  265. else if (returnType == @encode(float))
  266. {
  267. float ret;
  268. [invocation getReturnValue:&ret];
  269. rval.setFloat(ret);
  270. }
  271. else if (returnType == @encode(double))
  272. {
  273. double ret;
  274. [invocation getReturnValue:&ret];
  275. rval.setNumber(ret);
  276. }
  277. else if (returnType == @encode(char))
  278. {
  279. int8_t ret;
  280. [invocation getReturnValue:&ret];
  281. rval.setInt8(ret);
  282. }
  283. else if (returnType == @encode(unsigned char))
  284. {
  285. uint8_t ret;
  286. [invocation getReturnValue:&ret];
  287. rval.setUint8(ret);
  288. }
  289. else
  290. {
  291. _error = JSO_ERR_TYPE_NOT_SUPPORT;
  292. NSLog(@"not support return type = %s", returnType.c_str());
  293. return false;
  294. }
  295. }
  296. }@catch(NSException *exception)
  297. {
  298. NSLog(@"EXCEPTION THROW: %@", exception);
  299. _error = JSO_ERR_EXCEPTION_OCCURRED;
  300. return false;
  301. }
  302. return true;
  303. }
  304. se::Value JavaScriptObjCBridge::CallInfo::objc_to_seval(id objcVal)
  305. {
  306. se::Value ret;
  307. if (objcVal == nil)
  308. return ret;
  309. if ([objcVal isKindOfClass:[NSNumber class]])
  310. {
  311. NSNumber *number = (NSNumber *)objcVal;
  312. std::string numberType = [number objCType];
  313. if (numberType == @encode(BOOL) || numberType == @encode(bool))
  314. {
  315. ret.setBoolean([number boolValue]);
  316. }
  317. else if (numberType == @encode(int)
  318. || numberType == @encode(long)
  319. || numberType == @encode(short)
  320. || numberType == @encode(unsigned int)
  321. || numberType == @encode(unsigned long)
  322. || numberType == @encode(unsigned short)
  323. || numberType == @encode(float)
  324. || numberType == @encode(double)
  325. || numberType == @encode(char)
  326. || numberType == @encode(unsigned char)
  327. )
  328. {
  329. ret.setNumber([number doubleValue]);
  330. }
  331. else
  332. {
  333. CCLOGERROR("Unknown number type: %s", numberType.c_str());
  334. }
  335. }
  336. else if ([objcVal isKindOfClass:[NSString class]])
  337. {
  338. const char* content = [objcVal cStringUsingEncoding:NSUTF8StringEncoding];
  339. ret.setString(content);
  340. }
  341. else if ([objcVal isKindOfClass:[NSDictionary class]])
  342. {
  343. CCLOGERROR("JavaScriptObjCBridge doesn't support to bind NSDictionary!");
  344. }
  345. else
  346. {
  347. const char* content = [[NSString stringWithFormat:@"%@", objcVal] cStringUsingEncoding:NSUTF8StringEncoding];
  348. ret.setString(content);
  349. }
  350. return ret;
  351. }
  352. se::Class* __jsb_JavaScriptObjCBridge_class = nullptr;
  353. static bool JavaScriptObjCBridge_finalize(se::State& s)
  354. {
  355. JavaScriptObjCBridge* cobj = (JavaScriptObjCBridge*)s.nativeThisObject();
  356. delete cobj;
  357. return true;
  358. }
  359. SE_BIND_FINALIZE_FUNC(JavaScriptObjCBridge_finalize)
  360. static bool JavaScriptObjCBridge_constructor(se::State& s)
  361. {
  362. JavaScriptObjCBridge* cobj = new (std::nothrow) JavaScriptObjCBridge();
  363. s.thisObject()->setPrivateData(cobj);
  364. return true;
  365. }
  366. SE_BIND_CTOR(JavaScriptObjCBridge_constructor, __jsb_JavaScriptObjCBridge_class, JavaScriptObjCBridge_finalize)
  367. static bool JavaScriptObjCBridge_callStaticMethod(se::State& s)
  368. {
  369. const auto& args = s.args();
  370. int argc = (int)args.size();
  371. if (argc >= 2)
  372. {
  373. bool ok = false;
  374. std::string clsName, methodName;
  375. ok = seval_to_std_string(args[0], &clsName);
  376. SE_PRECONDITION2(ok, false, "Converting class name failed!");
  377. ok = seval_to_std_string(args[1], &methodName);
  378. SE_PRECONDITION2(ok, false, "Converting method name failed!");
  379. JavaScriptObjCBridge::CallInfo call(clsName.c_str(), methodName.c_str());
  380. ok = call.execute(args, s.rval());
  381. if(!ok)
  382. {
  383. s.rval().setUndefined();
  384. SE_REPORT_ERROR("call (%s.%s) failed, result code: %d", clsName.c_str(), methodName.c_str(), call.getErrorCode());
  385. return false;
  386. }
  387. return true;
  388. }
  389. SE_REPORT_ERROR("wrong number of arguments: %d, was expecting >=2", argc);
  390. return false;
  391. }
  392. SE_BIND_FUNC(JavaScriptObjCBridge_callStaticMethod)
  393. bool register_javascript_objc_bridge(se::Object* obj)
  394. {
  395. se::Class* cls = se::Class::create("JavaScriptObjCBridge", obj, nullptr, _SE(JavaScriptObjCBridge_constructor));
  396. cls->defineFinalizeFunction(_SE(JavaScriptObjCBridge_finalize));
  397. cls->defineFunction("callStaticMethod", _SE(JavaScriptObjCBridge_callStaticMethod));
  398. cls->install();
  399. __jsb_JavaScriptObjCBridge_class = cls;
  400. se::ScriptEngine::getInstance()->clearException();
  401. return true;
  402. }