| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627 |
- /*
- * Copyright (c) 2013-2016 Chukong Technologies Inc.
- * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
- #include "scripting/js-bindings/manual/JavaScriptJavaBridge.h"
- #include "platform/android/jni/JniHelper.h"
- #include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
- #include "cocos/scripting/js-bindings/manual/jsb_conversions.hpp"
- #include "cocos/base/ccUTF8.h"
- #include <android/log.h>
- #include <vector>
- #include <string>
- #ifdef LOG_TAG
- #undef LOG_TAG
- #endif
- #define LOG_TAG "JavaScriptJavaBridge"
- #ifndef ORG_JAVABRIDGE_CLASS_NAME
- #define ORG_JAVABRIDGE_CLASS_NAME org_cocos2dx_lib_Cocos2dxJavascriptJavaBridge
- #endif
- #define JNI_JSJAVABRIDGE(FUNC) JNI_METHOD1(ORG_JAVABRIDGE_CLASS_NAME,FUNC)
- extern "C" {
- JNIEXPORT jint JNICALL JNI_JSJAVABRIDGE(evalString)
- (JNIEnv *env, jclass cls, jstring value)
- {
- if (!se::ScriptEngine::getInstance()->isValid()) {
- CCLOG("ScriptEngine has not been initialized");
- return 0;
- }
- se::AutoHandleScope hs;
- bool strFlag = false;
- std::string strValue = cocos2d::StringUtils::getStringUTFCharsJNI(env, value, &strFlag);
- if (!strFlag)
- {
- CCLOG("JavaScriptJavaBridge_evalString error, invalid string code");
- return 0;
- }
- se::ScriptEngine::getInstance()->evalString(strValue.c_str());
- return 1;
- }
- } // extern "C"
- #define JSJ_ERR_OK (0)
- #define JSJ_ERR_TYPE_NOT_SUPPORT (-1)
- #define JSJ_ERR_INVALID_SIGNATURES (-2)
- #define JSJ_ERR_METHOD_NOT_FOUND (-3)
- #define JSJ_ERR_EXCEPTION_OCCURRED (-4)
- #define JSJ_ERR_VM_THREAD_DETACHED (-5)
- #define JSJ_ERR_VM_FAILURE (-6)
- #define JSJ_ERR_CLASS_NOT_FOUND (-7)
- class JavaScriptJavaBridge
- {
- public:
- enum class ValueType: char
- {
- INVALID,
- VOID,
- INTEGER,
- LONG,
- FLOAT,
- BOOLEAN,
- STRING,
- VECTOR,
- FUNCTION
- };
- typedef std::vector<ValueType> ValueTypes;
- typedef union
- {
- int intValue;
- long longValue;
- float floatValue;
- int boolValue;
- std::string *stringValue;
- } ReturnValue;
- class CallInfo
- {
- public:
- CallInfo(const char *className, const char *methodName, const char *methodSig)
- : m_valid(false)
- , m_error(JSJ_ERR_OK)
- , m_className(className)
- , m_methodName(methodName)
- , m_methodSig(methodSig)
- , m_returnType(ValueType::VOID)
- , m_argumentsCount(0)
- , m_retjstring(NULL)
- , m_env(NULL)
- , m_classID(NULL)
- , m_methodID(NULL)
- {
- memset(&m_ret, 0, sizeof(m_ret));
- m_valid = validateMethodSig() && getMethodInfo();
- }
- ~CallInfo();
- bool isValid() {
- return m_valid;
- }
- int getErrorCode() {
- return m_error;
- }
- JNIEnv *getEnv() {
- return m_env;
- }
- ValueType argumentTypeAtIndex(size_t index) {
- return m_argumentsType.at(index);
- }
- int getArgumentsCount(){
- return m_argumentsCount;
- }
- ValueType getReturnValueType(){
- return m_returnType;
- }
- ReturnValue getReturnValue(){
- return m_ret;
- }
- bool execute();
- bool executeWithArgs(jvalue *args);
- private:
- bool m_valid;
- int m_error;
- std::string m_className;
- std::string m_methodName;
- std::string m_methodSig;
- int m_argumentsCount;
- ValueTypes m_argumentsType;
- ValueType m_returnType;
- ReturnValue m_ret;
- jstring m_retjstring;
- JNIEnv *m_env;
- jclass m_classID;
- jmethodID m_methodID;
- bool validateMethodSig();
- bool getMethodInfo();
- ValueType checkType(const std::string& sig, size_t *pos);
- };
- static bool convertReturnValue(ReturnValue retValue, ValueType type, se::Value* ret);
- };
- JavaScriptJavaBridge::CallInfo::~CallInfo()
- {
- if (m_returnType == ValueType::STRING && m_ret.stringValue)
- {
- delete m_ret.stringValue;
- }
- }
- bool JavaScriptJavaBridge::CallInfo::execute()
- {
- switch (m_returnType)
- {
- case JavaScriptJavaBridge::ValueType::VOID:
- m_env->CallStaticVoidMethod(m_classID, m_methodID);
- break;
- case JavaScriptJavaBridge::ValueType::INTEGER:
- m_ret.intValue = m_env->CallStaticIntMethod(m_classID, m_methodID);
- break;
- case JavaScriptJavaBridge::ValueType::LONG:
- m_ret.longValue = m_env->CallStaticLongMethod(m_classID, m_methodID);
- break;
- case JavaScriptJavaBridge::ValueType::FLOAT:
- m_ret.floatValue = m_env->CallStaticFloatMethod(m_classID, m_methodID);
- break;
- case JavaScriptJavaBridge::ValueType::BOOLEAN:
- m_ret.boolValue = m_env->CallStaticBooleanMethod(m_classID, m_methodID);
- break;
- case JavaScriptJavaBridge::ValueType::STRING:
- {
- m_retjstring = (jstring)m_env->CallStaticObjectMethod(m_classID, m_methodID);
- if(m_env->ExceptionCheck()) {
- m_env->ExceptionDescribe();
- m_env->ExceptionClear();
- m_retjstring = nullptr;
- }
- if (m_retjstring)
- {
- std::string strValue = cocos2d::StringUtils::getStringUTFCharsJNI(m_env, m_retjstring);
- m_ret.stringValue = new std::string(strValue);
- }
- else
- m_ret.stringValue = nullptr;
- break;
- }
- default:
- m_error = JSJ_ERR_TYPE_NOT_SUPPORT;
- SE_LOGD("Return type '%d' is not supported", static_cast<int>(m_returnType));
- return false;
- }
- if (m_env->ExceptionCheck() == JNI_TRUE)
- {
- m_env->ExceptionDescribe();
- m_env->ExceptionClear();
- m_error = JSJ_ERR_EXCEPTION_OCCURRED;
- return false;
- }
- return true;
- }
- bool JavaScriptJavaBridge::CallInfo::executeWithArgs(jvalue *args)
- {
- switch (m_returnType)
- {
- case JavaScriptJavaBridge::ValueType::VOID:
- m_env->CallStaticVoidMethodA(m_classID, m_methodID, args);
- break;
- case JavaScriptJavaBridge::ValueType::INTEGER:
- m_ret.intValue = m_env->CallStaticIntMethodA(m_classID, m_methodID, args);
- break;
- case JavaScriptJavaBridge::ValueType::LONG:
- m_ret.longValue = m_env->CallStaticIntMethodA(m_classID, m_methodID, args);
- break;
- case JavaScriptJavaBridge::ValueType::FLOAT:
- m_ret.floatValue = m_env->CallStaticFloatMethodA(m_classID, m_methodID, args);
- break;
- case JavaScriptJavaBridge::ValueType::BOOLEAN:
- m_ret.boolValue = m_env->CallStaticBooleanMethodA(m_classID, m_methodID, args);
- break;
- case JavaScriptJavaBridge::ValueType::STRING:
- {
- m_retjstring = (jstring)m_env->CallStaticObjectMethodA(m_classID, m_methodID, args);
- std::string strValue = cocos2d::StringUtils::getStringUTFCharsJNI(m_env, m_retjstring);
- m_ret.stringValue = new std::string(strValue);
- break;
- }
- default:
- m_error = JSJ_ERR_TYPE_NOT_SUPPORT;
- SE_LOGD("Return type '%d' is not supported", static_cast<int>(m_returnType));
- return false;
- }
- if (m_env->ExceptionCheck() == JNI_TRUE)
- {
- m_env->ExceptionDescribe();
- m_env->ExceptionClear();
- m_error = JSJ_ERR_EXCEPTION_OCCURRED;
- return false;
- }
- return true;
- }
- bool JavaScriptJavaBridge::CallInfo::validateMethodSig()
- {
- size_t len = m_methodSig.length();
- if (len < 3 || m_methodSig[0] != '(') // min sig is "()V"
- {
- m_error = JSJ_ERR_INVALID_SIGNATURES;
- return false;
- }
- size_t pos = 1;
- while (pos < len && m_methodSig[pos] != ')')
- {
- JavaScriptJavaBridge::ValueType type = checkType(m_methodSig, &pos);
- if (type == ValueType::INVALID) return false;
- m_argumentsCount++;
- m_argumentsType.push_back(type);
- pos++;
- }
- if (pos >= len || m_methodSig[pos] != ')')
- {
- m_error = JSJ_ERR_INVALID_SIGNATURES;
- return false;
- }
- pos++;
- m_returnType = checkType(m_methodSig, &pos);
- return true;
- }
- JavaScriptJavaBridge::ValueType JavaScriptJavaBridge::CallInfo::checkType(const std::string& sig, size_t *pos)
- {
- switch (sig[*pos])
- {
- case 'I':
- return JavaScriptJavaBridge::ValueType::INTEGER;
- case 'J':
- return JavaScriptJavaBridge::ValueType::LONG;
- case 'F':
- return JavaScriptJavaBridge::ValueType::FLOAT;
- case 'Z':
- return JavaScriptJavaBridge::ValueType::BOOLEAN;
- case 'V':
- return JavaScriptJavaBridge::ValueType::VOID;
- case 'L':
- size_t pos2 = sig.find_first_of(';', *pos + 1);
- if (pos2 == std::string::npos)
- {
- m_error = JSJ_ERR_INVALID_SIGNATURES;
- return ValueType::INVALID;
- }
- const std::string t = sig.substr(*pos, pos2 - *pos + 1);
- if (t.compare("Ljava/lang/String;") == 0)
- {
- *pos = pos2;
- return ValueType::STRING;
- }
- else if (t.compare("Ljava/util/Vector;") == 0)
- {
- *pos = pos2;
- return ValueType::VECTOR;
- }
- else
- {
- m_error = JSJ_ERR_TYPE_NOT_SUPPORT;
- return ValueType::INVALID;
- }
- }
- m_error = JSJ_ERR_TYPE_NOT_SUPPORT;
- return ValueType::INVALID;
- }
- bool JavaScriptJavaBridge::CallInfo::getMethodInfo()
- {
- m_methodID = 0;
- m_env = 0;
- JavaVM* jvm = cocos2d::JniHelper::getJavaVM();
- jint ret = jvm->GetEnv((void**)&m_env, JNI_VERSION_1_4);
- switch (ret) {
- case JNI_OK:
- break;
- case JNI_EDETACHED :
- if (jvm->AttachCurrentThread(&m_env, NULL) < 0)
- {
- SE_LOGD("%s", "Failed to get the environment using AttachCurrentThread()");
- m_error = JSJ_ERR_VM_THREAD_DETACHED;
- return false;
- }
- break;
- case JNI_EVERSION :
- default :
- SE_LOGD("%s", "Failed to get the environment using GetEnv()");
- m_error = JSJ_ERR_VM_FAILURE;
- return false;
- }
- jstring _jstrClassName = m_env->NewStringUTF(m_className.c_str());
- m_classID = (jclass) m_env->CallObjectMethod(cocos2d::JniHelper::classloader,
- cocos2d::JniHelper::loadclassMethod_methodID,
- _jstrClassName);
- if (NULL == m_classID) {
- SE_LOGD("Classloader failed to find class of %s", m_className.c_str());
- m_env->DeleteLocalRef(_jstrClassName);
- m_env->ExceptionClear();
- m_error = JSJ_ERR_CLASS_NOT_FOUND;
- return false;
- }
- m_env->DeleteLocalRef(_jstrClassName);
- m_methodID = m_env->GetStaticMethodID(m_classID, m_methodName.c_str(), m_methodSig.c_str());
- if (!m_methodID)
- {
- m_env->ExceptionClear();
- SE_LOGD("Failed to find method id of %s.%s %s",
- m_className.c_str(),
- m_methodName.c_str(),
- m_methodSig.c_str());
- m_error = JSJ_ERR_METHOD_NOT_FOUND;
- return false;
- }
- return true;
- }
- bool JavaScriptJavaBridge::convertReturnValue(ReturnValue retValue, ValueType type, se::Value* ret)
- {
- assert(ret != nullptr);
- switch (type)
- {
- case JavaScriptJavaBridge::ValueType::INTEGER:
- ret->setInt32(retValue.intValue);
- break;
- case JavaScriptJavaBridge::ValueType::LONG:
- ret->setLong(retValue.longValue);
- break;
- case JavaScriptJavaBridge::ValueType::FLOAT:
- ret->setFloat(retValue.floatValue);
- break;
- case JavaScriptJavaBridge::ValueType::BOOLEAN:
- ret->setBoolean(retValue.boolValue);
- break;
- case JavaScriptJavaBridge::ValueType::STRING:
- if (retValue.stringValue)
- ret->setString(*retValue.stringValue);
- else
- ret->setNull();
- break;
- default:
- ret->setUndefined();
- break;
- }
- return true;
- }
- se::Class* __jsb_JavaScriptJavaBridge_class = nullptr;
- static bool JavaScriptJavaBridge_finalize(se::State& s)
- {
- JavaScriptJavaBridge* cobj = (JavaScriptJavaBridge*)s.nativeThisObject();
- delete cobj;
- return true;
- }
- SE_BIND_FINALIZE_FUNC(JavaScriptJavaBridge_finalize)
- static bool JavaScriptJavaBridge_constructor(se::State& s)
- {
- JavaScriptJavaBridge* cobj = new (std::nothrow) JavaScriptJavaBridge();
- s.thisObject()->setPrivateData(cobj);
- return true;
- }
- SE_BIND_CTOR(JavaScriptJavaBridge_constructor, __jsb_JavaScriptJavaBridge_class, JavaScriptJavaBridge_finalize)
- static bool JavaScriptJavaBridge_callStaticMethod(se::State& s)
- {
- const auto& args = s.args();
- int argc = (int)args.size();
- if (argc == 3)
- {
- bool ok = false;
- std::string clsName, methodName, methodSig;
- ok = seval_to_std_string(args[0], &clsName);
- SE_PRECONDITION2(ok, false, "Converting class name failed!");
- ok = seval_to_std_string(args[1], &methodName);
- SE_PRECONDITION2(ok, false, "Converting method name failed!");
- ok = seval_to_std_string(args[2], &methodSig);
- SE_PRECONDITION2(ok, false, "Converting method signature failed!");
- JavaScriptJavaBridge::CallInfo call(clsName.c_str(), methodName.c_str(), methodSig.c_str());
- if (call.isValid())
- {
- ok = call.execute();
- int errorCode = call.getErrorCode();
- if (!ok || errorCode < 0)
- {
- SE_REPORT_ERROR("call result code: %d", call.getErrorCode());
- return false;
- }
- JavaScriptJavaBridge::convertReturnValue(call.getReturnValue(), call.getReturnValueType(), &s.rval());
- return true;
- }
- SE_REPORT_ERROR("JavaScriptJavaBridge::CallInfo isn't valid!");
- return false;
- }
- else if (argc > 3)
- {
- bool ok = false;
- std::string clsName, methodName, methodSig;
- ok = seval_to_std_string(args[0], &clsName);
- SE_PRECONDITION2(ok, false, "Converting class name failed!");
- ok = seval_to_std_string(args[1], &methodName);
- SE_PRECONDITION2(ok, false, "Converting method name failed!");
- ok = seval_to_std_string(args[2], &methodSig);
- SE_PRECONDITION2(ok, false, "Converting method signature failed!");
- JavaScriptJavaBridge::CallInfo call(clsName.c_str(), methodName.c_str(), methodSig.c_str());
- if (call.isValid() && call.getArgumentsCount() == (argc - 3))
- {
- int count = argc - 3;
- jvalue* jargs = new jvalue[count];
- std::vector<jobject> toReleaseObjects;
- for (int i = 0; i < count; ++i)
- {
- int index = i + 3;
- switch (call.argumentTypeAtIndex(i))
- {
- case JavaScriptJavaBridge::ValueType::INTEGER:
- {
- int integer = 0;
- seval_to_int32(args[index], &integer);
- jargs[i].i = integer;
- break;
- }
- case JavaScriptJavaBridge::ValueType::LONG:
- {
- long longVal = 0L;
- seval_to_long(args[index], &longVal);
- jargs[i].j = longVal;
- break;
- }
- case JavaScriptJavaBridge::ValueType::FLOAT:
- {
- float floatNumber = 0.0f;
- seval_to_float(args[index], &floatNumber);
- jargs[i].f = floatNumber;
- break;
- }
- case JavaScriptJavaBridge::ValueType::BOOLEAN:
- {
- jargs[i].z = args[index].isBoolean() && args[index].toBoolean() ? JNI_TRUE : JNI_FALSE;
- break;
- }
- case JavaScriptJavaBridge::ValueType::STRING:
- {
- const auto &arg = args[index];
- if (arg.isNull() || arg.isUndefined())
- jargs[i].l = nullptr;
- else
- {
- std::string str;
- seval_to_std_string(args[index], &str);
- jargs[i].l = call.getEnv()->NewStringUTF(str.c_str());
- toReleaseObjects.push_back(jargs[i].l);
- }
- break;
- }
- default:
- SE_REPORT_ERROR("Unsupport type of parameter %d", i);
- break;
- }
- }
- ok = call.executeWithArgs(jargs);
- for (const auto& obj : toReleaseObjects)
- {
- call.getEnv()->DeleteLocalRef(obj);
- }
- if (jargs)
- delete[] jargs;
- int errorCode = call.getErrorCode();
- if (!ok || errorCode < 0)
- {
- SE_REPORT_ERROR("js_JSJavaBridge : call result code: %d", errorCode);
- return false;
- }
- JavaScriptJavaBridge::convertReturnValue(call.getReturnValue(), call.getReturnValueType(), &s.rval());
- return true;
- }
- SE_REPORT_ERROR("call valid: %d, call.getArgumentsCount()= %d", call.isValid(), call.getArgumentsCount());
- return false;
- }
- SE_REPORT_ERROR("wrong number of arguments: %d, was expecting >=3", argc);
- return false;
- }
- SE_BIND_FUNC(JavaScriptJavaBridge_callStaticMethod)
- bool register_javascript_java_bridge(se::Object* obj)
- {
- se::Class* cls = se::Class::create("JavascriptJavaBridge", obj, nullptr, _SE(JavaScriptJavaBridge_constructor));
- cls->defineFinalizeFunction(_SE(JavaScriptJavaBridge_finalize));
- cls->defineFunction("callStaticMethod", _SE(JavaScriptJavaBridge_callStaticMethod));
- cls->install();
- __jsb_JavaScriptJavaBridge_class = cls;
- se::ScriptEngine::getInstance()->clearException();
- return true;
- }
|