/* * 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. */ #import #import "scripting/js-bindings/manual/platform/ios/JavaScriptObjCBridge.h" #include "scripting/js-bindings/manual/spidermonkey_specifics.h" #include "scripting/js-bindings/manual/ScriptingCore.h" #include "scripting/js-bindings/manual/js_manual_conversions.h" JavaScriptObjCBridge::CallInfo::~CallInfo(void) { if (m_returnType == TypeString) { delete m_ret.stringValue; } } JS::Value JavaScriptObjCBridge::convertReturnValue(JSContext *cx, ReturnValue retValue, ValueType type) { JS::Value ret = JSVAL_NULL; switch (type) { case TypeInteger: return INT_TO_JSVAL(retValue.intValue); case TypeFloat: return DOUBLE_TO_JSVAL((double)retValue.floatValue); case TypeBoolean: return BOOLEAN_TO_JSVAL(retValue.boolValue); case TypeString: return c_string_to_jsval(cx, retValue.stringValue->c_str(),retValue.stringValue->size()); default: break; } return ret; } bool JavaScriptObjCBridge::CallInfo::execute(JSContext *cx,jsval *argv,unsigned argc) { NSString * className =[NSString stringWithCString: m_className.c_str() encoding:NSUTF8StringEncoding]; NSString *methodName = [NSString stringWithCString: m_methodName.c_str() encoding:NSUTF8StringEncoding]; bool ok = true; NSMutableDictionary *m_dic = [NSMutableDictionary dictionary]; for(int i = 2;i0){ if (strcmp(returnType, "@") == 0) { id ret; [invocation getReturnValue:&ret]; pushValue(ret); } else if (strcmp(returnType, "c") == 0 || strcmp(returnType, "B") == 0) // BOOL { char ret; [invocation getReturnValue:&ret]; m_ret.boolValue = ret; m_returnType = TypeBoolean; } else if (strcmp(returnType, "i") == 0) // int { int ret; [invocation getReturnValue:&ret]; m_ret.intValue = ret; m_returnType = TypeInteger; } else if (strcmp(returnType, "f") == 0) // float { float ret; [invocation getReturnValue:&ret]; m_ret.floatValue = ret; m_returnType = TypeFloat; } else { m_error = JSO_ERR_TYPE_NOT_SUPPORT; NSLog(@"not support return type = %s", returnType); return false; } }else{ m_returnType = TypeVoid; } }@catch(NSException *exception){ NSLog(@"EXCEPTION THROW: %@", exception); m_error = JSO_ERR_EXCEPTION_OCCURRED; return false; } return true; } void JavaScriptObjCBridge::CallInfo::pushValue(void *val){ id oval = (id)val; if (oval == nil) { return; } else if ([oval isKindOfClass:[NSNumber class]]) { NSNumber *number = (NSNumber *)oval; const char *numberType = [number objCType]; if (strcmp(numberType, @encode(BOOL)) == 0) { m_ret.boolValue = [number boolValue]; m_returnType = TypeBoolean; } else if (strcmp(numberType, @encode(int)) == 0) { m_ret.intValue = [number intValue]; m_returnType = TypeInteger; } else { m_ret.floatValue = [number floatValue]; m_returnType = TypeFloat; } } else if ([oval isKindOfClass:[NSString class]]) { const char *content = [oval cStringUsingEncoding:NSUTF8StringEncoding]; m_ret.stringValue = new (std::nothrow) string(content); m_returnType = TypeString; } else if ([oval isKindOfClass:[NSDictionary class]]) { } else { const char *content = [[NSString stringWithFormat:@"%@", oval] cStringUsingEncoding:NSUTF8StringEncoding]; m_ret.stringValue = new string(content); m_returnType = TypeString; } } /** * @brief Initialize Object and needed properties. * */ JS_BINDED_CLASS_GLUE_IMPL(JavaScriptObjCBridge); /** * @brief Implementation for the Javascript Constructor * */ JS_BINDED_CONSTRUCTOR_IMPL(JavaScriptObjCBridge) { JavaScriptObjCBridge* jsj = new (std::nothrow) JavaScriptObjCBridge(); js_proxy_t *p; jsval out; JS::RootedObject proto(cx, JavaScriptObjCBridge::js_proto); JS::RootedObject parentProto(cx, JavaScriptObjCBridge::js_parent); JS::RootedObject obj(cx, JS_NewObject(cx, &JavaScriptObjCBridge::js_class, proto, parentProto)); if (obj) { JS_SetPrivate(obj, jsj); out = OBJECT_TO_JSVAL(obj); } JS::CallArgs args = JS::CallArgsFromVp(argc, vp); args.rval().set(out); p = jsb_new_proxy(jsj, obj); JS::AddNamedObjectRoot(cx, &p->obj, "JavaScriptObjCBridge"); return true; } /** * @brief destructor for Javascript * */ static void basic_object_finalize(JSFreeOp *freeOp, JSObject *obj) { CCLOG("basic_object_finalize %p ...", obj); js_proxy_t* nproxy; js_proxy_t* jsproxy; JSContext *cx = ScriptingCore::getInstance()->getGlobalContext(); JS::RootedObject jsobj(cx, obj); jsproxy = jsb_get_js_proxy(jsobj); if (jsproxy) { nproxy = jsb_get_native_proxy(jsproxy->ptr); jsb_remove_proxy(nproxy, jsproxy); } } JS_BINDED_FUNC_IMPL(JavaScriptObjCBridge, callStaticMethod){ JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (argc >= 2) { JSStringWrapper arg0(args.get(0)); JSStringWrapper arg1(args.get(1)); CallInfo call(arg0.get(),arg1.get()); bool ok = call.execute(cx,args.array(),argc); if(!ok){ JS_ReportError(cx, "js_cocos2dx_JSObjCBridge : call result code: %d", call.getErrorCode()); return false; } args.rval().set(convertReturnValue(cx, call.getReturnValue(), call.getReturnValueType())); return ok; } return false; } static bool js_is_native_obj(JSContext *cx, uint32_t argc, jsval *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); args.rval().setBoolean(true); return true; } /** * @brief register JavascriptJavaBridge to be usable in js * */ void JavaScriptObjCBridge::_js_register(JSContext *cx, JS::HandleObject global) { JSClass jsclass = { "JavaScriptObjCBridge", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,basic_object_finalize }; JavaScriptObjCBridge::js_class = jsclass; static JSPropertySpec props[] = { JS_PSG("__nativeObj", js_is_native_obj, JSPROP_PERMANENT | JSPROP_ENUMERATE ), JS_PS_END }; static JSFunctionSpec funcs[] = { JS_BINDED_FUNC_FOR_DEF(JavaScriptObjCBridge, callStaticMethod), JS_FS_END }; JavaScriptObjCBridge::js_parent = NULL; JavaScriptObjCBridge::js_proto = JS_InitClass(cx, global, JS::NullPtr(), &JavaScriptObjCBridge::js_class , JavaScriptObjCBridge::_js_constructor, 0, props, funcs, NULL, NULL); }