| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- /****************************************************************************
- Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
- http://www.cocos.com
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated engine source code (the "Software"), a limited,
- worldwide, royalty-free, non-assignable, revocable and non-exclusive license
- to use Cocos Creator solely to develop games on your target platforms. You shall
- not use Cocos Creator software for developing other software or tools that's
- used for developing games. You are not granted to publish, distribute,
- sublicense, and/or sell copies of Cocos Creator.
- The software or tools in this License Agreement are licensed, not sold.
- Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
- 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 "JavaScriptObjCBridge.h"
- #include "cocos/scripting/js-bindings/manual/jsb_conversions.hpp"
- #include "cocos/scripting/js-bindings/manual/jsb_global.h"
- #include "cocos/scripting/js-bindings/jswrapper/SeApi.h"
- #include <string>
- #include <vector>
- #import <Foundation/Foundation.h>
- #define JSO_ERR_OK (0)
- #define JSO_ERR_TYPE_NOT_SUPPORT (-1)
- #define JSO_ERR_INVALID_ARGUMENTS (-2)
- #define JSO_ERR_METHOD_NOT_FOUND (-3)
- #define JSO_ERR_EXCEPTION_OCCURRED (-4)
- #define JSO_ERR_CLASS_NOT_FOUND (-5)
- #define JSO_ERR_VM_FAILURE (-6)
- class JavaScriptObjCBridge
- {
- public:
- class CallInfo
- {
- public:
- CallInfo(const char *className, const char* methodName)
- :_error(JSO_ERR_OK)
- ,_methodName(methodName)
- ,_className(className)
- {
- }
- ~CallInfo() {}
- int getErrorCode() const
- {
- return _error;
- }
- bool execute(const se::ValueArray& argv, se::Value& rval);
- private:
- se::Value objc_to_seval(id objcVal);
- int _error;
- std::string _className;
- std::string _methodName;
- };
- };
- bool JavaScriptObjCBridge::CallInfo::execute(const se::ValueArray& argv, se::Value& rval)
- {
- NSString *className =[NSString stringWithCString: _className.c_str() encoding:NSUTF8StringEncoding];
- NSString *methodName = [NSString stringWithCString: _methodName.c_str() encoding:NSUTF8StringEncoding];
- if(!className || !methodName)
- {
- _error = JSO_ERR_INVALID_ARGUMENTS;
- return false;
- }
- Class targetClass = NSClassFromString(className);
- if(!targetClass)
- {
- _error = JSO_ERR_CLASS_NOT_FOUND;
- return false;
- }
- SEL methodSel;
- methodSel = NSSelectorFromString(methodName);
- if (!methodSel)
- {
- _error = JSO_ERR_METHOD_NOT_FOUND;
- return false;
- }
- methodSel = NSSelectorFromString(methodName);
- NSMethodSignature *methodSig = [targetClass methodSignatureForSelector:(SEL)methodSel];
- if (methodSig == nil)
- {
- _error = JSO_ERR_METHOD_NOT_FOUND;
- NSLog(@"%@.%@ method isn't found!", className, methodName);
- return false;
- }
- @try
- {
- int argc = (int)argv.size();
- NSUInteger argumentCount = [methodSig numberOfArguments];
- if (argumentCount != argc)
- {
- _error = JSO_ERR_INVALID_ARGUMENTS;
- return false;
- }
- NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
- [invocation setTarget:targetClass];
- [invocation setSelector:methodSel];
- for(int i = 2; i < argc; ++i)
- {
- std::string argumentType = [methodSig getArgumentTypeAtIndex:i];
- const se::Value& arg = argv[i];
- /* - (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
- *
- * Refer to https://developer.apple.com/documentation/foundation/nsinvocation/1437834-setargument?language=objc
- *
- * This method copies the contents of buffer as the argument at index. The number of bytes copied is determined by the argument size.
- * When the argument value is an object, pass a pointer to the variable (or memory) from which the object should be copied:
- */
- if (arg.isString())
- {
- NSString* str = [NSString stringWithCString:arg.toString().c_str() encoding:NSUTF8StringEncoding];
- [invocation setArgument:&str atIndex:i];
- }
- else if (arg.isNumber())
- {
- if (argumentType == @encode(int))
- {
- int val = arg.toInt32();
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == @encode(long))
- {
- long val = arg.toLong();
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == @encode(short))
- {
- short val = arg.toInt16();
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == @encode(unsigned int))
- {
- unsigned int val = arg.toUint32();
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == @encode(unsigned long))
- {
- unsigned long val = arg.toUlong();
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == @encode(unsigned short))
- {
- unsigned short val = arg.toUint16();
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == @encode(float))
- {
- float val = arg.toFloat();
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == @encode(double))
- {
- double val = arg.toNumber();
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == @encode(char))
- {
- char val = arg.toInt8();
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == @encode(unsigned char))
- {
- unsigned char val = arg.toUint8();
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == "@")
- { // NSNumber*
- NSNumber* number = [NSNumber numberWithDouble:arg.toNumber()];
- [invocation setArgument:&number atIndex:i];
- }
- else
- {
- NSLog(@"Unsupported argument type: %s", argumentType.c_str());
- _error = JSO_ERR_TYPE_NOT_SUPPORT;
- return false;
- }
- }
- else if (arg.isBoolean())
- {
- if (argumentType == @encode(BOOL))
- {
- BOOL val = arg.toBoolean() ? YES : NO;
- [invocation setArgument:&val atIndex:i];
- }
- else if (argumentType == @encode(bool))
- {
- bool val = arg.toBoolean();
- [invocation setArgument:&val atIndex:i];
- }
- else
- {
- NSLog(@"Unsupported argument type: %s", argumentType.c_str());
- _error = JSO_ERR_TYPE_NOT_SUPPORT;
- return false;
- }
- }
- else if (arg.isNullOrUndefined())
- {
- // Don't call [invocation setArgument] will pass nil to relevant Objective-C argument.
- }
- else
- {
- NSLog(@"Unsupported argument type, se::Value::Type: %d", (int)arg.getType());
- _error = JSO_ERR_TYPE_NOT_SUPPORT;
- return false;
- }
- }
- NSUInteger returnLength = [methodSig methodReturnLength];
- std::string returnType = [methodSig methodReturnType];
- [invocation invoke];
- if (returnLength > 0)
- {
- if (returnType == "@")
- {
- id ret;
- [invocation getReturnValue:&ret];
- rval = objc_to_seval(ret);
- }
- else if (returnType == @encode(BOOL) || returnType == @encode(bool))
- {
- bool ret;
- [invocation getReturnValue:&ret];
- rval.setBoolean(ret);
- }
- else if (returnType == @encode(int))
- {
- int ret;
- [invocation getReturnValue:&ret];
- rval.setInt32(ret);
- }
- else if (returnType == @encode(long))
- {
- long ret;
- [invocation getReturnValue:&ret];
- rval.setLong(ret);
- }
- else if (returnType == @encode(short))
- {
- short ret;
- [invocation getReturnValue:&ret];
- rval.setInt16(ret);
- }
- else if (returnType == @encode(unsigned int))
- {
- unsigned int ret;
- [invocation getReturnValue:&ret];
- rval.setUint32(ret);
- }
- else if (returnType == @encode(unsigned long))
- {
- unsigned long ret;
- [invocation getReturnValue:&ret];
- rval.setUlong(ret);
- }
- else if (returnType == @encode(unsigned short))
- {
- unsigned short ret;
- [invocation getReturnValue:&ret];
- rval.setUint16(ret);
- }
- else if (returnType == @encode(float))
- {
- float ret;
- [invocation getReturnValue:&ret];
- rval.setFloat(ret);
- }
- else if (returnType == @encode(double))
- {
- double ret;
- [invocation getReturnValue:&ret];
- rval.setNumber(ret);
- }
- else if (returnType == @encode(char))
- {
- int8_t ret;
- [invocation getReturnValue:&ret];
- rval.setInt8(ret);
- }
- else if (returnType == @encode(unsigned char))
- {
- uint8_t ret;
- [invocation getReturnValue:&ret];
- rval.setUint8(ret);
- }
- else
- {
- _error = JSO_ERR_TYPE_NOT_SUPPORT;
- NSLog(@"not support return type = %s", returnType.c_str());
- return false;
- }
- }
- }@catch(NSException *exception)
- {
- NSLog(@"EXCEPTION THROW: %@", exception);
- _error = JSO_ERR_EXCEPTION_OCCURRED;
- return false;
- }
- return true;
- }
- se::Value JavaScriptObjCBridge::CallInfo::objc_to_seval(id objcVal)
- {
- se::Value ret;
- if (objcVal == nil)
- return ret;
- if ([objcVal isKindOfClass:[NSNumber class]])
- {
- NSNumber *number = (NSNumber *)objcVal;
- std::string numberType = [number objCType];
- if (numberType == @encode(BOOL) || numberType == @encode(bool))
- {
- ret.setBoolean([number boolValue]);
- }
- else if (numberType == @encode(int)
- || numberType == @encode(long)
- || numberType == @encode(short)
- || numberType == @encode(unsigned int)
- || numberType == @encode(unsigned long)
- || numberType == @encode(unsigned short)
- || numberType == @encode(float)
- || numberType == @encode(double)
- || numberType == @encode(char)
- || numberType == @encode(unsigned char)
- )
- {
- ret.setNumber([number doubleValue]);
- }
- else
- {
- CCLOGERROR("Unknown number type: %s", numberType.c_str());
- }
- }
- else if ([objcVal isKindOfClass:[NSString class]])
- {
- const char* content = [objcVal cStringUsingEncoding:NSUTF8StringEncoding];
- ret.setString(content);
- }
- else if ([objcVal isKindOfClass:[NSDictionary class]])
- {
- CCLOGERROR("JavaScriptObjCBridge doesn't support to bind NSDictionary!");
- }
- else
- {
- const char* content = [[NSString stringWithFormat:@"%@", objcVal] cStringUsingEncoding:NSUTF8StringEncoding];
- ret.setString(content);
- }
- return ret;
- }
- se::Class* __jsb_JavaScriptObjCBridge_class = nullptr;
- static bool JavaScriptObjCBridge_finalize(se::State& s)
- {
- JavaScriptObjCBridge* cobj = (JavaScriptObjCBridge*)s.nativeThisObject();
- delete cobj;
- return true;
- }
- SE_BIND_FINALIZE_FUNC(JavaScriptObjCBridge_finalize)
- static bool JavaScriptObjCBridge_constructor(se::State& s)
- {
- JavaScriptObjCBridge* cobj = new (std::nothrow) JavaScriptObjCBridge();
- s.thisObject()->setPrivateData(cobj);
- return true;
- }
- SE_BIND_CTOR(JavaScriptObjCBridge_constructor, __jsb_JavaScriptObjCBridge_class, JavaScriptObjCBridge_finalize)
- static bool JavaScriptObjCBridge_callStaticMethod(se::State& s)
- {
- const auto& args = s.args();
- int argc = (int)args.size();
- if (argc >= 2)
- {
- bool ok = false;
- std::string clsName, methodName;
- 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!");
- JavaScriptObjCBridge::CallInfo call(clsName.c_str(), methodName.c_str());
- ok = call.execute(args, s.rval());
- if(!ok)
- {
- s.rval().setUndefined();
- SE_REPORT_ERROR("call (%s.%s) failed, result code: %d", clsName.c_str(), methodName.c_str(), call.getErrorCode());
- return false;
- }
- return true;
- }
- SE_REPORT_ERROR("wrong number of arguments: %d, was expecting >=2", argc);
- return false;
- }
- SE_BIND_FUNC(JavaScriptObjCBridge_callStaticMethod)
- bool register_javascript_objc_bridge(se::Object* obj)
- {
- se::Class* cls = se::Class::create("JavaScriptObjCBridge", obj, nullptr, _SE(JavaScriptObjCBridge_constructor));
- cls->defineFinalizeFunction(_SE(JavaScriptObjCBridge_finalize));
- cls->defineFunction("callStaticMethod", _SE(JavaScriptObjCBridge_callStaticMethod));
- cls->install();
- __jsb_JavaScriptObjCBridge_class = cls;
- se::ScriptEngine::getInstance()->clearException();
- return true;
- }
|