// // ScriptingCore.cpp // testmonkey // // Created by Rolando Abarca on 3/14/12. // Copyright (c) 2012 Zynga Inc. All rights reserved. // #include #include #include #include #include #include #include "ScriptingCore.h" #include "cocos2d.h" #ifdef ANDROID #include #include #include #endif #ifdef ANDROID #define LOG_TAG "ScriptingCore.cpp" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) #else #define LOGD(...) js_log(__VA_ARGS__) #endif js_proxy_t *_native_js_global_ht = NULL; js_proxy_t *_js_native_global_ht = NULL; js_type_class_t *_js_global_type_ht = NULL; char *_js_log_buf = NULL; std::vector registrationList; static void executeJSFunctionFromReservedSpot(JSContext *cx, JSObject *obj, jsval &dataVal, jsval &retval) { // if(p->jsclass->JSCLASS_HAS_RESERVED_SLOTS(1)) { jsval func = JS_GetReservedSlot(obj, 0); if(func == JSVAL_VOID) { return; } jsval thisObj = JS_GetReservedSlot(obj, 1); if(thisObj == JSVAL_VOID) { JS_CallFunctionValue(cx, obj, func, 1, &dataVal, &retval); } else { assert(!JSVAL_IS_PRIMITIVE(thisObj)); JS_CallFunctionValue(cx, JSVAL_TO_OBJECT(thisObj), func, 1, &dataVal, &retval); } // } } void ScriptingCore::executeJSFunctionWithThisObj(jsval thisObj, jsval callback, jsval data) { jsval retval; if(callback != JSVAL_VOID || thisObj != JSVAL_VOID) { JS_CallFunctionValue(cx, JSVAL_TO_OBJECT(thisObj), callback, 1, &data, &retval); } } static void executeJSFunctionWithName(JSContext *cx, JSObject *obj, const char *funcName, jsval &dataVal, jsval &retval) { JSBool hasAction; jsval temp_retval; if (JS_HasProperty(cx, obj, funcName, &hasAction) && hasAction) { if(!JS_GetProperty(cx, obj, funcName, &temp_retval)) { return; } if(temp_retval == JSVAL_VOID) { return; } JS_CallFunctionName(cx, obj, funcName, 1, &dataVal, &retval); } } void js_log(const char *format, ...) { if (_js_log_buf == NULL) { _js_log_buf = (char *)calloc(sizeof(char), 257); } va_list vl; va_start(vl, format); int len = vsnprintf(_js_log_buf, 256, format, vl); va_end(vl); if (len) { #ifdef ANDROID __android_log_print(ANDROID_LOG_DEBUG, "js_log", _js_log_buf); #else fprintf(stderr, "JS: %s\n", _js_log_buf); #endif } } void registerDefaultClasses(JSContext* cx, JSObject* global) { if (!JS_InitStandardClasses(cx, global)) { js_log("error initializing the standard classes"); } // // Javascript controller (__jsc__) // JSObject *jsc = JS_NewObject(cx, NULL, NULL, NULL); jsval jscVal = OBJECT_TO_JSVAL(jsc); JS_SetProperty(cx, global, "__jsc__", &jscVal); JS_DefineFunction(cx, jsc, "garbageCollect", ScriptingCore::forceGC, 0, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE ); JS_DefineFunction(cx, jsc, "dumpRoot", ScriptingCore::dumpRoot, 0, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE ); JS_DefineFunction(cx, jsc, "addGCRootObject", ScriptingCore::addRootJS, 1, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE ); JS_DefineFunction(cx, jsc, "removeGCRootObject", ScriptingCore::removeRootJS, 1, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE ); JS_DefineFunction(cx, jsc, "executeScript", ScriptingCore::executeScript, 1, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE ); // register some global functions JS_DefineFunction(cx, global, "require", ScriptingCore::executeScript, 1, JSPROP_READONLY | JSPROP_PERMANENT); JS_DefineFunction(cx, global, "log", ScriptingCore::log, 0, JSPROP_READONLY | JSPROP_PERMANENT); JS_DefineFunction(cx, global, "executeScript", ScriptingCore::executeScript, 1, JSPROP_READONLY | JSPROP_PERMANENT); JS_DefineFunction(cx, global, "forceGC", ScriptingCore::forceGC, 0, JSPROP_READONLY | JSPROP_PERMANENT); } void sc_finalize(JSFreeOp *freeOp, JSObject *obj) { return; } static JSClass global_class = { "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, sc_finalize, JSCLASS_NO_OPTIONAL_MEMBERS }; #if !defined(JS_NewCompartmentAndGlobalObject) #if defined(JS_NewGlobalObject) # undef(JS_NewGlobalObject) #endif //#define JS_NewCompartmentAndGlobalObject(cx, clasp, ppals) JS_NewGlobalObject(cx, clasp, ppals) #endif ScriptingCore::ScriptingCore() { // set utf8 strings internally (we don't need utf16) JS_SetCStringsAreUTF8(); this->addRegisterCallback(registerDefaultClasses); } void ScriptingCore::string_report(jsval val) { if (JSVAL_IS_NULL(val)) { LOGD("val : (JSVAL_IS_NULL(val)"); // return 1; } else if ((JSVAL_IS_BOOLEAN(val)) && (JS_FALSE == (JSVAL_TO_BOOLEAN(val)))) { LOGD("val : (return value is JS_FALSE"); // return 1; } else if (JSVAL_IS_STRING(val)) { JSString *str = JS_ValueToString(this->getGlobalContext(), val); if (NULL == str) { LOGD("val : return string is NULL"); } else { LOGD("val : return string =\n%s\n", JS_EncodeString(this->getGlobalContext(), str)); } } else if (JSVAL_IS_NUMBER(val)) { double number; if (JS_FALSE == JS_ValueToNumber(this->getGlobalContext(), val, &number)) { LOGD("val : return number could not be converted"); } else { LOGD("val : return number =\n%f", number); } } } JSBool ScriptingCore::evalString(const char *string, jsval *outVal, const char *filename) { jsval rval; const char *fname = (filename ? filename : "noname"); uint32_t lineno = 1; if (outVal == NULL) { outVal = &rval; } JSBool evaluatedOK = JS_EvaluateScript(cx, global, string, strlen(string), fname, lineno, outVal); if (JS_FALSE == evaluatedOK) { LOGD("(evaluatedOK == JS_FALSE)"); } else { this->string_report(*outVal); } return evaluatedOK; } void ScriptingCore::start() { // for now just this this->createGlobalContext(); } void ScriptingCore::addRegisterCallback(sc_register_sth callback) { registrationList.push_back(callback); } void ScriptingCore::createGlobalContext() { if (this->cx && this->rt) { JS_DestroyContext(this->cx); JS_DestroyRuntime(this->rt); this->cx = NULL; this->rt = NULL; } this->rt = JS_NewRuntime(10 * 1024 * 1024); this->cx = JS_NewContext(rt, 10240); JS_SetOptions(this->cx, JSOPTION_TYPE_INFERENCE); JS_SetVersion(this->cx, JSVERSION_LATEST); JS_SetOptions(this->cx, JS_GetOptions(this->cx) & ~JSOPTION_METHODJIT); JS_SetOptions(this->cx, JS_GetOptions(this->cx) & ~JSOPTION_METHODJIT_ALWAYS); JS_SetErrorReporter(this->cx, ScriptingCore::reportError); this->global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL); for (std::vector::iterator it = registrationList.begin(); it != registrationList.end(); it++) { sc_register_sth callback = *it; callback(this->cx, this->global); } } #ifdef ANDROID static unsigned long fileutils_read_into_new_memory(const char* relativepath, unsigned char** content) { *content = NULL; AAssetManager* assetmanager = JniHelper::getAssetManager(); if (NULL == assetmanager) { LOGD("assetmanager : is NULL"); return 0; } // read asset data AAsset* asset = AAssetManager_open(assetmanager, relativepath, AASSET_MODE_UNKNOWN); if (NULL == asset) { LOGD("asset : is NULL"); return 0; } off_t size = AAsset_getLength(asset); LOGD("size = %d ", size); unsigned char* buf = (unsigned char*) malloc((sizeof(unsigned char)) * (size+1)); if (NULL == buf) { LOGD("memory allocation failed"); AAsset_close(asset); return 0; } int bytesread = AAsset_read(asset, buf, size); LOGD("bytesread = %d ", bytesread); buf[bytesread] = '\0'; AAsset_close(asset); *content = (unsigned char*) buf; return bytesread; } JSBool ScriptingCore::runScript(const char *path) { LOGD("ScriptingCore::runScript(%s)", path); if (NULL == path) { return JS_FALSE; } unsigned char* content = NULL; unsigned long contentsize = 0; contentsize = fileutils_read_into_new_memory(path, &content); if (NULL == content) { LOGD("(NULL == content)"); return JS_FALSE; } if (contentsize <= 0) { LOGD("(contentsize <= 0)"); free(content); return JS_FALSE; } jsval rval; JSBool ret = this->evalString((const char *)content, &rval, path); free(content); LOGD("... ScriptingCore::runScript(%s) done successfully.", path); return ret; } #else static size_t readFileInMemory(const char *path, unsigned char **buff) { struct stat buf; int file = open(path, O_RDONLY); long readBytes = -1; if (file) { if (fstat(file, &buf) == 0) { *buff = (unsigned char *)calloc(buf.st_size + 1, 1); if (*buff) { readBytes = read(file, *buff, buf.st_size); } } } close(file); return readBytes; } JSBool ScriptingCore::runScript(const char *path) { cocos2d::CCFileUtils *futil = cocos2d::CCFileUtils::sharedFileUtils(); #ifdef DEBUG /** * dpath should point to the parent directory of the "JS" folder. If this is * set to "" (as it is now) then it will take the scripts from the app bundle. * By setting the absolute path you can iterate the development only by * modifying those scripts and reloading from the simulator (no recompiling/ * relaunching) */ // std::string dpath("/Users/rabarca/Desktop/testjs/testjs/"); std::string dpath(""); dpath += path; const char *realPath = futil->fullPathFromRelativePath(dpath.c_str()); #else const char *realPath = NULL; futil->fullPathFromRelativePath(path); #endif if (!realPath) { return JS_FALSE; } unsigned char *content = NULL; unsigned long contentSize = 0; contentSize = readFileInMemory(realPath, &content); JSBool ret = JS_FALSE; if (content && contentSize) { jsval rval; ret = this->evalString((const char *)content, &rval, path); free(content); } return ret; } #endif ScriptingCore::~ScriptingCore() { JS_DestroyContext(cx); JS_DestroyRuntime(rt); JS_ShutDown(); if (_js_log_buf) { free(_js_log_buf); _js_log_buf = NULL; } } void ScriptingCore::reportError(JSContext *cx, const char *message, JSErrorReport *report) { js_log("%s:%u:%s\n", report->filename ? report->filename : "", (unsigned int) report->lineno, message); }; JSBool ScriptingCore::log(JSContext* cx, uint32_t argc, jsval *vp) { if (argc > 0) { JSString *string = NULL; JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S", &string); if (string) { char *cstr = JS_EncodeString(cx, string); js_log(cstr); } } return JS_TRUE; } void ScriptingCore::removeJSObjectByCCObject(void* cobj) { js_proxy_t* nproxy; js_proxy_t* jsproxy; void *ptr = cobj; JS_GET_PROXY(nproxy, ptr); if (nproxy) { JSContext *cx = ScriptingCore::getInstance()->getGlobalContext(); JS_RemoveObjectRoot(cx, &nproxy->obj); JS_GET_NATIVE_PROXY(jsproxy, nproxy->obj); JS_REMOVE_PROXY(nproxy, jsproxy); } } JSBool ScriptingCore::setReservedSpot(uint32_t i, JSObject *obj, jsval value) { JS_SetReservedSlot(obj, i, value); return JS_TRUE; } JSBool ScriptingCore::executeScript(JSContext *cx, uint32_t argc, jsval *vp) { JSBool ret = JS_FALSE; if (argc == 1) { JSString *string; if (JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S", &string) == JS_TRUE) { ret = ScriptingCore::getInstance()->runScript(JS_EncodeString(cx, string)); } } return ret; } JSBool ScriptingCore::forceGC(JSContext *cx, uint32_t argc, jsval *vp) { JSRuntime *rt = JS_GetRuntime(cx); JS_GC(rt); return JS_TRUE; } static void dumpNamedRoot(const char *name, void *addr, JSGCRootType type, void *data) { printf("Root: '%s' at %p\n", name, addr); } JSBool ScriptingCore::dumpRoot(JSContext *cx, uint32_t argc, jsval *vp) { // JS_DumpNamedRoots is only available on DEBUG versions of SpiderMonkey. // Mac and Simulator versions were compiled with DEBUG. #if DEBUG JSContext *_cx = ScriptingCore::getInstance()->getGlobalContext(); JSRuntime *rt = JS_GetRuntime(_cx); JS_DumpNamedRoots(rt, dumpNamedRoot, NULL); #endif return JS_TRUE; } JSBool ScriptingCore::addRootJS(JSContext *cx, uint32_t argc, jsval *vp) { if (argc == 1) { JSObject *o = NULL; if (JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "o", &o) == JS_TRUE) { if (JS_AddNamedObjectRoot(cx, &o, "from-js") == JS_FALSE) { LOGD("something went wrong when setting an object to the root"); } } return JS_TRUE; } return JS_FALSE; } JSBool ScriptingCore::removeRootJS(JSContext *cx, uint32_t argc, jsval *vp) { if (argc == 1) { JSObject *o = NULL; if (JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "o", &o) == JS_TRUE) { JS_RemoveObjectRoot(cx, &o); } return JS_TRUE; } return JS_FALSE; } int ScriptingCore::executeFunctionWithIntegerData(int nHandler, int data, CCNode *self) { js_proxy_t * p; JS_GET_PROXY(p, self); if (!p) return 0; jsval retval; jsval dataVal = INT_TO_JSVAL(1); js_proxy_t *proxy; JS_GET_PROXY(proxy, self); std::string funcName = ""; if(data == kCCNodeOnEnter) { executeJSFunctionWithName(this->cx, p->obj, "onEnter", dataVal, retval); } else if(data == kCCNodeOnExit) { executeJSFunctionWithName(this->cx, p->obj, "onExit", dataVal, retval); } else if(data == kCCMenuItemActivated) { dataVal = (proxy ? OBJECT_TO_JSVAL(proxy->obj) : JSVAL_NULL); executeJSFunctionFromReservedSpot(this->cx, p->obj, dataVal, retval); } return 1; } int ScriptingCore::executeFunctionWithObjectData(int nHandler, const char *name, JSObject *obj, CCNode *self) { js_proxy_t * p; JS_GET_PROXY(p, self); if (!p) return 0; jsval retval; jsval dataVal = OBJECT_TO_JSVAL(obj); executeJSFunctionWithName(this->cx, p->obj, name, dataVal, retval); return 1; } int ScriptingCore::executeFunctionWithFloatData(int nHandler, float data, CCNode *self) { js_proxy_t * p; JS_GET_PROXY(p, self); if (!p) return 0; jsval retval; jsval dataVal = DOUBLE_TO_JSVAL(data); std::string funcName = ""; executeJSFunctionWithName(this->cx, p->obj, "update", dataVal, retval); // if(data == kCCNodeOnEnter) { // executeJSFunctionWithName(this->cx, p, "onEnter", dataVal, retval); // } else if(data == kCCNodeOnExit) { // executeJSFunctionWithName(this->cx, p, "onExit", dataVal, retval); // } else if(data == kCCMenuItemActivated) { // executeJSFunctionFromReservedSpot(this->cx, p, dataVal, retval); // } return 1; } static void getTouchesFuncName(int eventType, std::string &funcName) { switch(eventType) { case CCTOUCHBEGAN: funcName = "onTouchesBegan"; break; case CCTOUCHENDED: funcName = "onTouchesEnded"; break; case CCTOUCHMOVED: funcName = "onTouchesMoved"; break; case CCTOUCHCANCELLED: funcName = "onTouchesCancelled"; break; } } static void getTouchFuncName(int eventType, std::string &funcName) { switch(eventType) { case CCTOUCHBEGAN: funcName = "onTouchBegan"; break; case CCTOUCHENDED: funcName = "onTouchEnded"; break; case CCTOUCHMOVED: funcName = "onTouchMoved"; break; case CCTOUCHCANCELLED: funcName = "onTouchCancelled"; break; } } static void rootObject(JSContext *cx, JSObject *obj) { JS_AddNamedObjectRoot(cx, &obj, "unnamed"); } static void unRootObject(JSContext *cx, JSObject *obj) { JS_RemoveObjectRoot(cx, &obj); } static void getJSTouchObject(JSContext *cx, CCTouch *x, jsval &jsret) { js_type_class_t *classType; TypeTest t; uint32_t typeId = t.s_id(); HASH_FIND_INT(_js_global_type_ht, &typeId, classType); assert(classType); JSObject *_tmp = JS_NewObject(cx, classType->jsclass, classType->proto, classType->parentProto); js_proxy_t *proxy, *nproxy; JS_NEW_PROXY(proxy, x, _tmp); void *ptr = x; JS_GET_PROXY(nproxy, ptr); JS_AddNamedObjectRoot(cx, &nproxy->obj, "CCTouch"); jsret = OBJECT_TO_JSVAL(_tmp); } static void removeJSTouchObject(JSContext *cx, CCTouch *x, jsval &jsret) { js_proxy_t* nproxy; js_proxy_t* jsproxy; void *ptr = x; JS_GET_PROXY(nproxy, ptr); if (nproxy) { JS_RemoveObjectRoot(cx, &nproxy->obj); JS_GET_NATIVE_PROXY(jsproxy, nproxy->obj); JS_REMOVE_PROXY(nproxy, jsproxy); } } int ScriptingCore::executeTouchesEvent(int nHandler, int eventType, CCSet *pTouches, CCNode *self) { std::string funcName = ""; getTouchesFuncName(eventType, funcName); JSObject *jsretArr = JS_NewArrayObject(this->cx, 0, NULL); JS_AddNamedObjectRoot(this->cx, &jsretArr, "touchArray"); int count = 0; for(CCSetIterator it = pTouches->begin(); it != pTouches->end(); ++it, ++count) { jsval jsret; getJSTouchObject(this->cx, (CCTouch *) *it, jsret); if(!JS_SetElement(this->cx, jsretArr, count, &jsret)) { break; } } executeFunctionWithObjectData(1, funcName.c_str(), jsretArr, self); JS_RemoveObjectRoot(this->cx, &jsretArr); for(CCSetIterator it = pTouches->begin(); it != pTouches->end(); ++it, ++count) { jsval jsret; removeJSTouchObject(this->cx, (CCTouch *) *it, jsret); } return 1; } int ScriptingCore::executeCustomTouchesEvent(int eventType, CCSet *pTouches, JSObject *obj) { jsval retval; std::string funcName; getTouchesFuncName(eventType, funcName); JSObject *jsretArr = JS_NewArrayObject(this->cx, 0, NULL); JS_AddNamedObjectRoot(this->cx, &jsretArr, "touchArray"); int count = 0; for(CCSetIterator it = pTouches->begin(); it != pTouches->end(); ++it, ++count) { jsval jsret; getJSTouchObject(this->cx, (CCTouch *) *it, jsret); if(!JS_SetElement(this->cx, jsretArr, count, &jsret)) { break; } } jsval jsretArrVal = OBJECT_TO_JSVAL(jsretArr); executeJSFunctionWithName(this->cx, obj, funcName.c_str(), jsretArrVal, retval); JS_RemoveObjectRoot(this->cx, &jsretArr); for(CCSetIterator it = pTouches->begin(); it != pTouches->end(); ++it, ++count) { jsval jsret; removeJSTouchObject(this->cx, (CCTouch *) *it, jsret); } return 1; } int ScriptingCore::executeCustomTouchEvent(int eventType, CCTouch *pTouch, JSObject *obj) { jsval retval; std::string funcName; getTouchFuncName(eventType, funcName); jsval jsTouch; getJSTouchObject(this->cx, pTouch, jsTouch); executeJSFunctionWithName(this->cx, obj, funcName.c_str(), jsTouch, retval); return 1; } int ScriptingCore::executeCustomTouchEvent(int eventType, CCTouch *pTouch, JSObject *obj, jsval &retval) { std::string funcName; getTouchFuncName(eventType, funcName); jsval jsTouch; getJSTouchObject(this->cx, pTouch, jsTouch); executeJSFunctionWithName(this->cx, obj, funcName.c_str(), jsTouch, retval); return 1; } int ScriptingCore::executeSchedule(int nHandler, float dt, CCNode *self) { executeFunctionWithFloatData(nHandler, dt, self); return 1; } long long jsval_to_long_long(JSContext *cx, jsval v) { JSObject *tmp = JSVAL_TO_OBJECT(v); if (JS_IsTypedArrayObject(tmp, cx) && JS_GetTypedArrayByteLength(tmp, cx) == 8) { uint32_t *data = (uint32_t *)JS_GetUint32ArrayData(tmp, cx); long long r = (long long)(*data); return r; } return 0; } std::string jsval_to_std_string(JSContext *cx, jsval v) { JSString *tmp = JS_ValueToString(cx, v); char *rawStr = JS_EncodeString(cx, tmp); std::string ret = std::string(rawStr); JS_free(cx, rawStr); return ret; } const char* jsval_to_c_string(JSContext *cx, jsval v) { JSString *tmp = JS_ValueToString(cx, v); return JS_EncodeString(cx, tmp); } CCPoint jsval_to_ccpoint(JSContext *cx, jsval v) { JSObject *tmp; jsval jsx, jsy; double x, y; JSBool ok = JS_ValueToObject(cx, v, &tmp) && JS_GetProperty(cx, tmp, "x", &jsx) && JS_GetProperty(cx, tmp, "y", &jsy) && JS_ValueToNumber(cx, jsx, &x) && JS_ValueToNumber(cx, jsy, &y); assert(ok == JS_TRUE); return cocos2d::CCPoint(x, y); } CCRect jsval_to_ccrect(JSContext *cx, jsval v) { JSObject *tmp; jsval jsx, jsy, jswidth, jsheight; double x, y, width, height; JSBool ok = JS_ValueToObject(cx, v, &tmp) && JS_GetProperty(cx, tmp, "x", &jsx) && JS_GetProperty(cx, tmp, "y", &jsy) && JS_GetProperty(cx, tmp, "width", &jswidth) && JS_GetProperty(cx, tmp, "height", &jsheight) && JS_ValueToNumber(cx, jsx, &x) && JS_ValueToNumber(cx, jsy, &y) && JS_ValueToNumber(cx, jswidth, &width) && JS_ValueToNumber(cx, jsheight, &height); assert(ok == JS_TRUE); return cocos2d::CCRect(x, y, width, height); } CCSize jsval_to_ccsize(JSContext *cx, jsval v) { JSObject *tmp; jsval jsw, jsh; double w, h; JSBool ok = JS_ValueToObject(cx, v, &tmp) && JS_GetProperty(cx, tmp, "width", &jsw) && JS_GetProperty(cx, tmp, "height", &jsh) && JS_ValueToNumber(cx, jsw, &w) && JS_ValueToNumber(cx, jsh, &h); assert(ok == JS_TRUE); return cocos2d::CCSize(w, h); } ccGridSize jsval_to_ccgridsize(JSContext *cx, jsval v) { JSObject *tmp; jsval jsx, jsy; double x, y; JSBool ok = JS_ValueToObject(cx, v, &tmp) && JS_GetProperty(cx, tmp, "x", &jsx) && JS_GetProperty(cx, tmp, "y", &jsy) && JS_ValueToNumber(cx, jsx, &x) && JS_ValueToNumber(cx, jsy, &y); assert(ok == JS_TRUE); return cocos2d::ccg(x, y); } ccColor4B jsval_to_cccolor4b(JSContext *cx, jsval v) { JSObject *tmp; jsval jsr, jsg, jsb, jsa; double r, g, b, a; JSBool ok = JS_ValueToObject(cx, v, &tmp) && JS_GetProperty(cx, tmp, "r", &jsr) && JS_GetProperty(cx, tmp, "g", &jsg) && JS_GetProperty(cx, tmp, "b", &jsb) && JS_GetProperty(cx, tmp, "a", &jsa) && JS_ValueToNumber(cx, jsr, &r) && JS_ValueToNumber(cx, jsg, &g) && JS_ValueToNumber(cx, jsb, &b) && JS_ValueToNumber(cx, jsa, &a); assert(ok == JS_TRUE); return cocos2d::ccc4(r, g, b, a); } ccColor4F jsval_to_cccolor4f(JSContext *cx, jsval v) { JSObject *tmp; jsval jsr, jsg, jsb, jsa; double r, g, b, a; JSBool ok = JS_ValueToObject(cx, v, &tmp) && JS_GetProperty(cx, tmp, "r", &jsr) && JS_GetProperty(cx, tmp, "g", &jsg) && JS_GetProperty(cx, tmp, "b", &jsb) && JS_GetProperty(cx, tmp, "a", &jsa) && JS_ValueToNumber(cx, jsr, &r) && JS_ValueToNumber(cx, jsg, &g) && JS_ValueToNumber(cx, jsb, &b) && JS_ValueToNumber(cx, jsa, &a); assert(ok == JS_TRUE); return cocos2d::ccc4f(r, g, b, a); } ccColor3B jsval_to_cccolor3b(JSContext *cx, jsval v) { JSObject *tmp; jsval jsr, jsg, jsb; double r, g, b; JSBool ok = JS_ValueToObject(cx, v, &tmp) && JS_GetProperty(cx, tmp, "r", &jsr) && JS_GetProperty(cx, tmp, "g", &jsg) && JS_GetProperty(cx, tmp, "b", &jsb) && JS_ValueToNumber(cx, jsr, &r) && JS_ValueToNumber(cx, jsg, &g) && JS_ValueToNumber(cx, jsb, &b); assert(ok == JS_TRUE); return cocos2d::ccc3(r, g, b); } CCArray* jsval_to_ccarray(JSContext* cx, jsval v) { JSObject *arr; if (JS_ValueToObject(cx, v, &arr) && JS_IsArrayObject(cx, arr)) { uint32_t len = 0; JS_GetArrayLength(cx, arr, &len); CCArray* ret = CCArray::createWithCapacity(len); for (int i=0; i < len; i++) { jsval elt; JSObject *elto; if (JS_GetElement(cx, arr, i, &elt) && JS_ValueToObject(cx, elt, &elto)) { js_proxy_t *proxy; JS_GET_NATIVE_PROXY(proxy, elto); if (proxy) { ret->addObject((CCObject *)proxy->ptr); } } } return ret; } return NULL; } // from native jsval long_long_to_jsval(JSContext* cx, long long v) { JSObject *tmp = JS_NewUint32Array(cx, 2); uint32_t *data = (uint32_t *)JS_GetArrayBufferViewData(tmp, cx); data[0] = ((uint32_t *)(&v))[0]; data[1] = ((uint32_t *)(&v))[1]; return OBJECT_TO_JSVAL(tmp); } jsval std_string_to_jsval(JSContext* cx, std::string& v) { JSString *str = JS_NewStringCopyZ(cx, v.c_str()); return STRING_TO_JSVAL(str); } jsval c_string_to_jsval(JSContext* cx, const char* v) { JSString *str = JS_NewStringCopyZ(cx, v); return STRING_TO_JSVAL(str); } jsval ccpoint_to_jsval(JSContext* cx, CCPoint& v) { JSObject *tmp = JS_NewObject(cx, NULL, NULL, NULL); if (!tmp) return JSVAL_NULL; JSBool ok = JS_DefineProperty(cx, tmp, "x", DOUBLE_TO_JSVAL(v.x), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "y", DOUBLE_TO_JSVAL(v.y), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT); if (ok) { return OBJECT_TO_JSVAL(tmp); } return JSVAL_NULL; } jsval ccrect_to_jsval(JSContext* cx, CCRect& v) { JSObject *tmp = JS_NewObject(cx, NULL, NULL, NULL); if (!tmp) return JSVAL_NULL; JSBool ok = JS_DefineProperty(cx, tmp, "x", DOUBLE_TO_JSVAL(v.origin.x), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "y", DOUBLE_TO_JSVAL(v.origin.y), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "width", DOUBLE_TO_JSVAL(v.size.width), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "height", DOUBLE_TO_JSVAL(v.size.height), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT); if (ok) { return OBJECT_TO_JSVAL(tmp); } return JSVAL_NULL; } jsval ccsize_to_jsval(JSContext* cx, CCSize& v) { JSObject *tmp = JS_NewObject(cx, NULL, NULL, NULL); if (!tmp) return JSVAL_NULL; JSBool ok = JS_DefineProperty(cx, tmp, "width", DOUBLE_TO_JSVAL(v.width), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "height", DOUBLE_TO_JSVAL(v.height), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT); if (ok) { return OBJECT_TO_JSVAL(tmp); } return JSVAL_NULL; } jsval ccgridsize_to_jsval(JSContext* cx, ccGridSize& v) { JSObject *tmp = JS_NewObject(cx, NULL, NULL, NULL); if (!tmp) return JSVAL_NULL; JSBool ok = JS_DefineProperty(cx, tmp, "x", DOUBLE_TO_JSVAL(v.x), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "y", DOUBLE_TO_JSVAL(v.y), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT); if (ok) { return OBJECT_TO_JSVAL(tmp); } return JSVAL_NULL; } jsval cccolor4b_to_jsval(JSContext* cx, ccColor4B& v) { JSObject *tmp = JS_NewObject(cx, NULL, NULL, NULL); if (!tmp) return JSVAL_NULL; JSBool ok = JS_DefineProperty(cx, tmp, "r", INT_TO_JSVAL(v.r), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "g", INT_TO_JSVAL(v.g), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "b", INT_TO_JSVAL(v.g), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "a", INT_TO_JSVAL(v.g), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT); if (ok) { return OBJECT_TO_JSVAL(tmp); } return JSVAL_NULL; } jsval cccolor4f_to_jsval(JSContext* cx, ccColor4F& v) { JSObject *tmp = JS_NewObject(cx, NULL, NULL, NULL); if (!tmp) return JSVAL_NULL; JSBool ok = JS_DefineProperty(cx, tmp, "r", DOUBLE_TO_JSVAL(v.r), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "g", DOUBLE_TO_JSVAL(v.g), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "b", DOUBLE_TO_JSVAL(v.g), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "a", DOUBLE_TO_JSVAL(v.g), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT); if (ok) { return OBJECT_TO_JSVAL(tmp); } return JSVAL_NULL; } jsval cccolor3b_to_jsval(JSContext* cx, ccColor3B& v) { JSObject *tmp = JS_NewObject(cx, NULL, NULL, NULL); if (!tmp) return JSVAL_NULL; JSBool ok = JS_DefineProperty(cx, tmp, "r", INT_TO_JSVAL(v.r), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "g", INT_TO_JSVAL(v.g), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT) && JS_DefineProperty(cx, tmp, "b", INT_TO_JSVAL(v.g), NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT); if (ok) { return OBJECT_TO_JSVAL(tmp); } return JSVAL_NULL; }