JavaScriptJavaBridge.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. /*
  2. * Copyright (c) 2013-2016 Chukong Technologies Inc.
  3. * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files (the "Software"), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. * THE SOFTWARE.
  22. */
  23. #include "scripting/js-bindings/manual/JavaScriptJavaBridge.h"
  24. #include "platform/android/jni/JniHelper.h"
  25. #include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
  26. #include "cocos/scripting/js-bindings/manual/jsb_conversions.hpp"
  27. #include "cocos/base/ccUTF8.h"
  28. #include <android/log.h>
  29. #include <vector>
  30. #include <string>
  31. #ifdef LOG_TAG
  32. #undef LOG_TAG
  33. #endif
  34. #define LOG_TAG "JavaScriptJavaBridge"
  35. #ifndef ORG_JAVABRIDGE_CLASS_NAME
  36. #define ORG_JAVABRIDGE_CLASS_NAME org_cocos2dx_lib_Cocos2dxJavascriptJavaBridge
  37. #endif
  38. #define JNI_JSJAVABRIDGE(FUNC) JNI_METHOD1(ORG_JAVABRIDGE_CLASS_NAME,FUNC)
  39. extern "C" {
  40. JNIEXPORT jint JNICALL JNI_JSJAVABRIDGE(evalString)
  41. (JNIEnv *env, jclass cls, jstring value)
  42. {
  43. if (!se::ScriptEngine::getInstance()->isValid()) {
  44. CCLOG("ScriptEngine has not been initialized");
  45. return 0;
  46. }
  47. se::AutoHandleScope hs;
  48. bool strFlag = false;
  49. std::string strValue = cocos2d::StringUtils::getStringUTFCharsJNI(env, value, &strFlag);
  50. if (!strFlag)
  51. {
  52. CCLOG("JavaScriptJavaBridge_evalString error, invalid string code");
  53. return 0;
  54. }
  55. se::ScriptEngine::getInstance()->evalString(strValue.c_str());
  56. return 1;
  57. }
  58. } // extern "C"
  59. #define JSJ_ERR_OK (0)
  60. #define JSJ_ERR_TYPE_NOT_SUPPORT (-1)
  61. #define JSJ_ERR_INVALID_SIGNATURES (-2)
  62. #define JSJ_ERR_METHOD_NOT_FOUND (-3)
  63. #define JSJ_ERR_EXCEPTION_OCCURRED (-4)
  64. #define JSJ_ERR_VM_THREAD_DETACHED (-5)
  65. #define JSJ_ERR_VM_FAILURE (-6)
  66. #define JSJ_ERR_CLASS_NOT_FOUND (-7)
  67. class JavaScriptJavaBridge
  68. {
  69. public:
  70. enum class ValueType: char
  71. {
  72. INVALID,
  73. VOID,
  74. INTEGER,
  75. LONG,
  76. FLOAT,
  77. BOOLEAN,
  78. STRING,
  79. VECTOR,
  80. FUNCTION
  81. };
  82. typedef std::vector<ValueType> ValueTypes;
  83. typedef union
  84. {
  85. int intValue;
  86. long longValue;
  87. float floatValue;
  88. int boolValue;
  89. std::string *stringValue;
  90. } ReturnValue;
  91. class CallInfo
  92. {
  93. public:
  94. CallInfo(const char *className, const char *methodName, const char *methodSig)
  95. : m_valid(false)
  96. , m_error(JSJ_ERR_OK)
  97. , m_className(className)
  98. , m_methodName(methodName)
  99. , m_methodSig(methodSig)
  100. , m_returnType(ValueType::VOID)
  101. , m_argumentsCount(0)
  102. , m_retjstring(NULL)
  103. , m_env(NULL)
  104. , m_classID(NULL)
  105. , m_methodID(NULL)
  106. {
  107. memset(&m_ret, 0, sizeof(m_ret));
  108. m_valid = validateMethodSig() && getMethodInfo();
  109. }
  110. ~CallInfo();
  111. bool isValid() {
  112. return m_valid;
  113. }
  114. int getErrorCode() {
  115. return m_error;
  116. }
  117. JNIEnv *getEnv() {
  118. return m_env;
  119. }
  120. ValueType argumentTypeAtIndex(size_t index) {
  121. return m_argumentsType.at(index);
  122. }
  123. int getArgumentsCount(){
  124. return m_argumentsCount;
  125. }
  126. ValueType getReturnValueType(){
  127. return m_returnType;
  128. }
  129. ReturnValue getReturnValue(){
  130. return m_ret;
  131. }
  132. bool execute();
  133. bool executeWithArgs(jvalue *args);
  134. private:
  135. bool m_valid;
  136. int m_error;
  137. std::string m_className;
  138. std::string m_methodName;
  139. std::string m_methodSig;
  140. int m_argumentsCount;
  141. ValueTypes m_argumentsType;
  142. ValueType m_returnType;
  143. ReturnValue m_ret;
  144. jstring m_retjstring;
  145. JNIEnv *m_env;
  146. jclass m_classID;
  147. jmethodID m_methodID;
  148. bool validateMethodSig();
  149. bool getMethodInfo();
  150. ValueType checkType(const std::string& sig, size_t *pos);
  151. };
  152. static bool convertReturnValue(ReturnValue retValue, ValueType type, se::Value* ret);
  153. };
  154. JavaScriptJavaBridge::CallInfo::~CallInfo()
  155. {
  156. if (m_returnType == ValueType::STRING && m_ret.stringValue)
  157. {
  158. delete m_ret.stringValue;
  159. }
  160. }
  161. bool JavaScriptJavaBridge::CallInfo::execute()
  162. {
  163. switch (m_returnType)
  164. {
  165. case JavaScriptJavaBridge::ValueType::VOID:
  166. m_env->CallStaticVoidMethod(m_classID, m_methodID);
  167. break;
  168. case JavaScriptJavaBridge::ValueType::INTEGER:
  169. m_ret.intValue = m_env->CallStaticIntMethod(m_classID, m_methodID);
  170. break;
  171. case JavaScriptJavaBridge::ValueType::LONG:
  172. m_ret.longValue = m_env->CallStaticLongMethod(m_classID, m_methodID);
  173. break;
  174. case JavaScriptJavaBridge::ValueType::FLOAT:
  175. m_ret.floatValue = m_env->CallStaticFloatMethod(m_classID, m_methodID);
  176. break;
  177. case JavaScriptJavaBridge::ValueType::BOOLEAN:
  178. m_ret.boolValue = m_env->CallStaticBooleanMethod(m_classID, m_methodID);
  179. break;
  180. case JavaScriptJavaBridge::ValueType::STRING:
  181. {
  182. m_retjstring = (jstring)m_env->CallStaticObjectMethod(m_classID, m_methodID);
  183. if(m_env->ExceptionCheck()) {
  184. m_env->ExceptionDescribe();
  185. m_env->ExceptionClear();
  186. m_retjstring = nullptr;
  187. }
  188. if (m_retjstring)
  189. {
  190. std::string strValue = cocos2d::StringUtils::getStringUTFCharsJNI(m_env, m_retjstring);
  191. m_ret.stringValue = new std::string(strValue);
  192. }
  193. else
  194. m_ret.stringValue = nullptr;
  195. break;
  196. }
  197. default:
  198. m_error = JSJ_ERR_TYPE_NOT_SUPPORT;
  199. SE_LOGD("Return type '%d' is not supported", static_cast<int>(m_returnType));
  200. return false;
  201. }
  202. if (m_env->ExceptionCheck() == JNI_TRUE)
  203. {
  204. m_env->ExceptionDescribe();
  205. m_env->ExceptionClear();
  206. m_error = JSJ_ERR_EXCEPTION_OCCURRED;
  207. return false;
  208. }
  209. return true;
  210. }
  211. bool JavaScriptJavaBridge::CallInfo::executeWithArgs(jvalue *args)
  212. {
  213. switch (m_returnType)
  214. {
  215. case JavaScriptJavaBridge::ValueType::VOID:
  216. m_env->CallStaticVoidMethodA(m_classID, m_methodID, args);
  217. break;
  218. case JavaScriptJavaBridge::ValueType::INTEGER:
  219. m_ret.intValue = m_env->CallStaticIntMethodA(m_classID, m_methodID, args);
  220. break;
  221. case JavaScriptJavaBridge::ValueType::LONG:
  222. m_ret.longValue = m_env->CallStaticIntMethodA(m_classID, m_methodID, args);
  223. break;
  224. case JavaScriptJavaBridge::ValueType::FLOAT:
  225. m_ret.floatValue = m_env->CallStaticFloatMethodA(m_classID, m_methodID, args);
  226. break;
  227. case JavaScriptJavaBridge::ValueType::BOOLEAN:
  228. m_ret.boolValue = m_env->CallStaticBooleanMethodA(m_classID, m_methodID, args);
  229. break;
  230. case JavaScriptJavaBridge::ValueType::STRING:
  231. {
  232. m_retjstring = (jstring)m_env->CallStaticObjectMethodA(m_classID, m_methodID, args);
  233. std::string strValue = cocos2d::StringUtils::getStringUTFCharsJNI(m_env, m_retjstring);
  234. m_ret.stringValue = new std::string(strValue);
  235. break;
  236. }
  237. default:
  238. m_error = JSJ_ERR_TYPE_NOT_SUPPORT;
  239. SE_LOGD("Return type '%d' is not supported", static_cast<int>(m_returnType));
  240. return false;
  241. }
  242. if (m_env->ExceptionCheck() == JNI_TRUE)
  243. {
  244. m_env->ExceptionDescribe();
  245. m_env->ExceptionClear();
  246. m_error = JSJ_ERR_EXCEPTION_OCCURRED;
  247. return false;
  248. }
  249. return true;
  250. }
  251. bool JavaScriptJavaBridge::CallInfo::validateMethodSig()
  252. {
  253. size_t len = m_methodSig.length();
  254. if (len < 3 || m_methodSig[0] != '(') // min sig is "()V"
  255. {
  256. m_error = JSJ_ERR_INVALID_SIGNATURES;
  257. return false;
  258. }
  259. size_t pos = 1;
  260. while (pos < len && m_methodSig[pos] != ')')
  261. {
  262. JavaScriptJavaBridge::ValueType type = checkType(m_methodSig, &pos);
  263. if (type == ValueType::INVALID) return false;
  264. m_argumentsCount++;
  265. m_argumentsType.push_back(type);
  266. pos++;
  267. }
  268. if (pos >= len || m_methodSig[pos] != ')')
  269. {
  270. m_error = JSJ_ERR_INVALID_SIGNATURES;
  271. return false;
  272. }
  273. pos++;
  274. m_returnType = checkType(m_methodSig, &pos);
  275. return true;
  276. }
  277. JavaScriptJavaBridge::ValueType JavaScriptJavaBridge::CallInfo::checkType(const std::string& sig, size_t *pos)
  278. {
  279. switch (sig[*pos])
  280. {
  281. case 'I':
  282. return JavaScriptJavaBridge::ValueType::INTEGER;
  283. case 'J':
  284. return JavaScriptJavaBridge::ValueType::LONG;
  285. case 'F':
  286. return JavaScriptJavaBridge::ValueType::FLOAT;
  287. case 'Z':
  288. return JavaScriptJavaBridge::ValueType::BOOLEAN;
  289. case 'V':
  290. return JavaScriptJavaBridge::ValueType::VOID;
  291. case 'L':
  292. size_t pos2 = sig.find_first_of(';', *pos + 1);
  293. if (pos2 == std::string::npos)
  294. {
  295. m_error = JSJ_ERR_INVALID_SIGNATURES;
  296. return ValueType::INVALID;
  297. }
  298. const std::string t = sig.substr(*pos, pos2 - *pos + 1);
  299. if (t.compare("Ljava/lang/String;") == 0)
  300. {
  301. *pos = pos2;
  302. return ValueType::STRING;
  303. }
  304. else if (t.compare("Ljava/util/Vector;") == 0)
  305. {
  306. *pos = pos2;
  307. return ValueType::VECTOR;
  308. }
  309. else
  310. {
  311. m_error = JSJ_ERR_TYPE_NOT_SUPPORT;
  312. return ValueType::INVALID;
  313. }
  314. }
  315. m_error = JSJ_ERR_TYPE_NOT_SUPPORT;
  316. return ValueType::INVALID;
  317. }
  318. bool JavaScriptJavaBridge::CallInfo::getMethodInfo()
  319. {
  320. m_methodID = 0;
  321. m_env = 0;
  322. JavaVM* jvm = cocos2d::JniHelper::getJavaVM();
  323. jint ret = jvm->GetEnv((void**)&m_env, JNI_VERSION_1_4);
  324. switch (ret) {
  325. case JNI_OK:
  326. break;
  327. case JNI_EDETACHED :
  328. if (jvm->AttachCurrentThread(&m_env, NULL) < 0)
  329. {
  330. SE_LOGD("%s", "Failed to get the environment using AttachCurrentThread()");
  331. m_error = JSJ_ERR_VM_THREAD_DETACHED;
  332. return false;
  333. }
  334. break;
  335. case JNI_EVERSION :
  336. default :
  337. SE_LOGD("%s", "Failed to get the environment using GetEnv()");
  338. m_error = JSJ_ERR_VM_FAILURE;
  339. return false;
  340. }
  341. jstring _jstrClassName = m_env->NewStringUTF(m_className.c_str());
  342. m_classID = (jclass) m_env->CallObjectMethod(cocos2d::JniHelper::classloader,
  343. cocos2d::JniHelper::loadclassMethod_methodID,
  344. _jstrClassName);
  345. if (NULL == m_classID) {
  346. SE_LOGD("Classloader failed to find class of %s", m_className.c_str());
  347. m_env->DeleteLocalRef(_jstrClassName);
  348. m_env->ExceptionClear();
  349. m_error = JSJ_ERR_CLASS_NOT_FOUND;
  350. return false;
  351. }
  352. m_env->DeleteLocalRef(_jstrClassName);
  353. m_methodID = m_env->GetStaticMethodID(m_classID, m_methodName.c_str(), m_methodSig.c_str());
  354. if (!m_methodID)
  355. {
  356. m_env->ExceptionClear();
  357. SE_LOGD("Failed to find method id of %s.%s %s",
  358. m_className.c_str(),
  359. m_methodName.c_str(),
  360. m_methodSig.c_str());
  361. m_error = JSJ_ERR_METHOD_NOT_FOUND;
  362. return false;
  363. }
  364. return true;
  365. }
  366. bool JavaScriptJavaBridge::convertReturnValue(ReturnValue retValue, ValueType type, se::Value* ret)
  367. {
  368. assert(ret != nullptr);
  369. switch (type)
  370. {
  371. case JavaScriptJavaBridge::ValueType::INTEGER:
  372. ret->setInt32(retValue.intValue);
  373. break;
  374. case JavaScriptJavaBridge::ValueType::LONG:
  375. ret->setLong(retValue.longValue);
  376. break;
  377. case JavaScriptJavaBridge::ValueType::FLOAT:
  378. ret->setFloat(retValue.floatValue);
  379. break;
  380. case JavaScriptJavaBridge::ValueType::BOOLEAN:
  381. ret->setBoolean(retValue.boolValue);
  382. break;
  383. case JavaScriptJavaBridge::ValueType::STRING:
  384. if (retValue.stringValue)
  385. ret->setString(*retValue.stringValue);
  386. else
  387. ret->setNull();
  388. break;
  389. default:
  390. ret->setUndefined();
  391. break;
  392. }
  393. return true;
  394. }
  395. se::Class* __jsb_JavaScriptJavaBridge_class = nullptr;
  396. static bool JavaScriptJavaBridge_finalize(se::State& s)
  397. {
  398. JavaScriptJavaBridge* cobj = (JavaScriptJavaBridge*)s.nativeThisObject();
  399. delete cobj;
  400. return true;
  401. }
  402. SE_BIND_FINALIZE_FUNC(JavaScriptJavaBridge_finalize)
  403. static bool JavaScriptJavaBridge_constructor(se::State& s)
  404. {
  405. JavaScriptJavaBridge* cobj = new (std::nothrow) JavaScriptJavaBridge();
  406. s.thisObject()->setPrivateData(cobj);
  407. return true;
  408. }
  409. SE_BIND_CTOR(JavaScriptJavaBridge_constructor, __jsb_JavaScriptJavaBridge_class, JavaScriptJavaBridge_finalize)
  410. static bool JavaScriptJavaBridge_callStaticMethod(se::State& s)
  411. {
  412. const auto& args = s.args();
  413. int argc = (int)args.size();
  414. if (argc == 3)
  415. {
  416. bool ok = false;
  417. std::string clsName, methodName, methodSig;
  418. ok = seval_to_std_string(args[0], &clsName);
  419. SE_PRECONDITION2(ok, false, "Converting class name failed!");
  420. ok = seval_to_std_string(args[1], &methodName);
  421. SE_PRECONDITION2(ok, false, "Converting method name failed!");
  422. ok = seval_to_std_string(args[2], &methodSig);
  423. SE_PRECONDITION2(ok, false, "Converting method signature failed!");
  424. JavaScriptJavaBridge::CallInfo call(clsName.c_str(), methodName.c_str(), methodSig.c_str());
  425. if (call.isValid())
  426. {
  427. ok = call.execute();
  428. int errorCode = call.getErrorCode();
  429. if (!ok || errorCode < 0)
  430. {
  431. SE_REPORT_ERROR("call result code: %d", call.getErrorCode());
  432. return false;
  433. }
  434. JavaScriptJavaBridge::convertReturnValue(call.getReturnValue(), call.getReturnValueType(), &s.rval());
  435. return true;
  436. }
  437. SE_REPORT_ERROR("JavaScriptJavaBridge::CallInfo isn't valid!");
  438. return false;
  439. }
  440. else if (argc > 3)
  441. {
  442. bool ok = false;
  443. std::string clsName, methodName, methodSig;
  444. ok = seval_to_std_string(args[0], &clsName);
  445. SE_PRECONDITION2(ok, false, "Converting class name failed!");
  446. ok = seval_to_std_string(args[1], &methodName);
  447. SE_PRECONDITION2(ok, false, "Converting method name failed!");
  448. ok = seval_to_std_string(args[2], &methodSig);
  449. SE_PRECONDITION2(ok, false, "Converting method signature failed!");
  450. JavaScriptJavaBridge::CallInfo call(clsName.c_str(), methodName.c_str(), methodSig.c_str());
  451. if (call.isValid() && call.getArgumentsCount() == (argc - 3))
  452. {
  453. int count = argc - 3;
  454. jvalue* jargs = new jvalue[count];
  455. std::vector<jobject> toReleaseObjects;
  456. for (int i = 0; i < count; ++i)
  457. {
  458. int index = i + 3;
  459. switch (call.argumentTypeAtIndex(i))
  460. {
  461. case JavaScriptJavaBridge::ValueType::INTEGER:
  462. {
  463. int integer = 0;
  464. seval_to_int32(args[index], &integer);
  465. jargs[i].i = integer;
  466. break;
  467. }
  468. case JavaScriptJavaBridge::ValueType::LONG:
  469. {
  470. long longVal = 0L;
  471. seval_to_long(args[index], &longVal);
  472. jargs[i].j = longVal;
  473. break;
  474. }
  475. case JavaScriptJavaBridge::ValueType::FLOAT:
  476. {
  477. float floatNumber = 0.0f;
  478. seval_to_float(args[index], &floatNumber);
  479. jargs[i].f = floatNumber;
  480. break;
  481. }
  482. case JavaScriptJavaBridge::ValueType::BOOLEAN:
  483. {
  484. jargs[i].z = args[index].isBoolean() && args[index].toBoolean() ? JNI_TRUE : JNI_FALSE;
  485. break;
  486. }
  487. case JavaScriptJavaBridge::ValueType::STRING:
  488. {
  489. const auto &arg = args[index];
  490. if (arg.isNull() || arg.isUndefined())
  491. jargs[i].l = nullptr;
  492. else
  493. {
  494. std::string str;
  495. seval_to_std_string(args[index], &str);
  496. jargs[i].l = call.getEnv()->NewStringUTF(str.c_str());
  497. toReleaseObjects.push_back(jargs[i].l);
  498. }
  499. break;
  500. }
  501. default:
  502. SE_REPORT_ERROR("Unsupport type of parameter %d", i);
  503. break;
  504. }
  505. }
  506. ok = call.executeWithArgs(jargs);
  507. for (const auto& obj : toReleaseObjects)
  508. {
  509. call.getEnv()->DeleteLocalRef(obj);
  510. }
  511. if (jargs)
  512. delete[] jargs;
  513. int errorCode = call.getErrorCode();
  514. if (!ok || errorCode < 0)
  515. {
  516. SE_REPORT_ERROR("js_JSJavaBridge : call result code: %d", errorCode);
  517. return false;
  518. }
  519. JavaScriptJavaBridge::convertReturnValue(call.getReturnValue(), call.getReturnValueType(), &s.rval());
  520. return true;
  521. }
  522. SE_REPORT_ERROR("call valid: %d, call.getArgumentsCount()= %d", call.isValid(), call.getArgumentsCount());
  523. return false;
  524. }
  525. SE_REPORT_ERROR("wrong number of arguments: %d, was expecting >=3", argc);
  526. return false;
  527. }
  528. SE_BIND_FUNC(JavaScriptJavaBridge_callStaticMethod)
  529. bool register_javascript_java_bridge(se::Object* obj)
  530. {
  531. se::Class* cls = se::Class::create("JavascriptJavaBridge", obj, nullptr, _SE(JavaScriptJavaBridge_constructor));
  532. cls->defineFinalizeFunction(_SE(JavaScriptJavaBridge_finalize));
  533. cls->defineFunction("callStaticMethod", _SE(JavaScriptJavaBridge_callStaticMethod));
  534. cls->install();
  535. __jsb_JavaScriptJavaBridge_class = cls;
  536. se::ScriptEngine::getInstance()->clearException();
  537. return true;
  538. }