2012-08-28 10:22:36 +08:00
|
|
|
//
|
|
|
|
// ScriptingCore.cpp
|
|
|
|
// testmonkey
|
|
|
|
//
|
|
|
|
// Created by Rolando Abarca on 3/14/12.
|
|
|
|
// Copyright (c) 2012 Zynga Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <vector>
|
2012-10-19 08:44:41 +08:00
|
|
|
#include <map>
|
2012-08-28 10:22:36 +08:00
|
|
|
#include "ScriptingCore.h"
|
2012-10-19 08:44:41 +08:00
|
|
|
#include "jsdbgapi.h"
|
2012-08-28 10:22:36 +08:00
|
|
|
#include "cocos2d.h"
|
2013-10-17 11:28:57 +08:00
|
|
|
#include "local-storage/LocalStorage.h"
|
2012-09-19 09:44:21 +08:00
|
|
|
#include "cocos2d_specifics.hpp"
|
2013-01-11 18:57:33 +08:00
|
|
|
#include "js_bindings_config.h"
|
2012-10-19 08:44:41 +08:00
|
|
|
// for debug socket
|
2012-10-19 11:15:23 +08:00
|
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
|
|
|
#include <io.h>
|
2013-01-17 17:07:18 +08:00
|
|
|
#include <WS2tcpip.h>
|
2012-10-19 11:15:23 +08:00
|
|
|
#else
|
2012-10-19 08:44:41 +08:00
|
|
|
#include <sys/socket.h>
|
2013-02-18 15:12:39 +08:00
|
|
|
#include <unistd.h>
|
2012-10-19 08:44:41 +08:00
|
|
|
#include <netdb.h>
|
2012-10-19 11:15:23 +08:00
|
|
|
#endif
|
2013-06-26 14:41:19 +08:00
|
|
|
|
|
|
|
#include <thread>
|
2012-08-28 10:22:36 +08:00
|
|
|
|
|
|
|
#ifdef ANDROID
|
|
|
|
#include <android/log.h>
|
|
|
|
#include <jni/JniHelper.h>
|
2012-10-19 13:41:03 +08:00
|
|
|
#include <netinet/in.h>
|
2012-08-28 10:22:36 +08:00
|
|
|
#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
|
|
|
|
|
2013-01-12 06:01:36 +08:00
|
|
|
#include "js_bindings_config.h"
|
|
|
|
|
2013-05-14 11:48:49 +08:00
|
|
|
#if DEBUG
|
|
|
|
#define TRACE_DEBUGGER_SERVER(...) CCLOG(__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define TRACE_DEBUGGER_SERVER(...)
|
|
|
|
#endif // #if DEBUG
|
|
|
|
|
2013-05-06 11:32:23 +08:00
|
|
|
#define BYTE_CODE_FILE_EXT ".jsc"
|
2013-01-17 17:07:18 +08:00
|
|
|
|
2013-10-31 11:07:33 +08:00
|
|
|
static std::string inData;
|
|
|
|
static std::string outData;
|
|
|
|
static std::vector<std::string> g_queue;
|
2013-06-26 14:41:19 +08:00
|
|
|
static std::mutex g_qMutex;
|
|
|
|
static std::mutex g_rwMutex;
|
2013-06-21 22:29:48 +08:00
|
|
|
static int clientSocket = -1;
|
2013-11-10 11:23:34 +08:00
|
|
|
static uint32_t s_nestedLoopLevel = 0;
|
2013-01-12 06:01:36 +08:00
|
|
|
|
|
|
|
// server entry point for the bg thread
|
2013-06-26 14:41:19 +08:00
|
|
|
static void serverEntryPoint(void);
|
2013-01-12 06:01:36 +08:00
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
js_proxy_t *_native_js_global_ht = NULL;
|
|
|
|
js_proxy_t *_js_native_global_ht = NULL;
|
2013-11-11 12:54:36 +08:00
|
|
|
std::unordered_map<long, js_type_class_t*> _js_global_type_map;
|
|
|
|
|
2013-06-21 22:29:48 +08:00
|
|
|
static char *_js_log_buf = NULL;
|
2012-08-28 10:22:36 +08:00
|
|
|
|
2013-06-21 22:29:48 +08:00
|
|
|
static std::vector<sc_register_sth> registrationList;
|
2012-08-28 10:22:36 +08:00
|
|
|
|
2012-10-19 08:44:41 +08:00
|
|
|
// name ~> JSScript map
|
2013-11-22 05:43:59 +08:00
|
|
|
static std::unordered_map<std::string, JSScript*> filename_script;
|
2012-10-19 08:44:41 +08:00
|
|
|
// port ~> socket map
|
2013-11-22 05:43:59 +08:00
|
|
|
static std::unordered_map<int,int> ports_sockets;
|
2012-10-19 08:44:41 +08:00
|
|
|
// name ~> globals
|
2013-11-22 05:43:59 +08:00
|
|
|
static std::unordered_map<std::string, js::RootedObject*> globals;
|
2012-10-19 08:44:41 +08:00
|
|
|
|
2013-07-25 17:58:15 +08:00
|
|
|
static void ReportException(JSContext *cx)
|
|
|
|
{
|
|
|
|
if (JS_IsExceptionPending(cx)) {
|
|
|
|
if (!JS_ReportPendingException(cx)) {
|
|
|
|
JS_ClearPendingException(cx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-20 00:54:21 +08:00
|
|
|
static void executeJSFunctionFromReservedSpot(JSContext *cx, JSObject *obj,
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval &dataVal, jsval &retval) {
|
|
|
|
|
|
|
|
jsval func = JS_GetReservedSlot(obj, 0);
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2013-08-09 14:49:39 +08:00
|
|
|
if (func == JSVAL_VOID) { return; }
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval thisObj = JS_GetReservedSlot(obj, 1);
|
2013-02-22 00:34:51 +08:00
|
|
|
JSAutoCompartment ac(cx, obj);
|
2013-11-06 15:31:58 +08:00
|
|
|
|
2013-08-09 14:49:39 +08:00
|
|
|
if (thisObj == JSVAL_VOID) {
|
2012-08-28 10:22:36 +08:00
|
|
|
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);
|
2012-10-20 00:54:21 +08:00
|
|
|
}
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
2013-09-20 20:09:39 +08:00
|
|
|
static void getTouchesFuncName(EventTouch::EventCode eventCode, std::string &funcName)
|
2013-09-12 16:02:16 +08:00
|
|
|
{
|
|
|
|
switch(eventCode)
|
|
|
|
{
|
2013-09-20 20:09:39 +08:00
|
|
|
case EventTouch::EventCode::BEGAN:
|
2012-09-11 14:02:33 +08:00
|
|
|
funcName = "onTouchesBegan";
|
|
|
|
break;
|
2013-09-20 20:09:39 +08:00
|
|
|
case EventTouch::EventCode::ENDED:
|
2012-09-11 14:02:33 +08:00
|
|
|
funcName = "onTouchesEnded";
|
|
|
|
break;
|
2013-09-20 20:09:39 +08:00
|
|
|
case EventTouch::EventCode::MOVED:
|
2012-09-11 14:02:33 +08:00
|
|
|
funcName = "onTouchesMoved";
|
|
|
|
break;
|
2013-09-20 20:09:39 +08:00
|
|
|
case EventTouch::EventCode::CANCELLED:
|
2012-09-11 14:02:33 +08:00
|
|
|
funcName = "onTouchesCancelled";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-20 20:09:39 +08:00
|
|
|
static void getTouchFuncName(EventTouch::EventCode eventCode, std::string &funcName)
|
2013-09-12 16:02:16 +08:00
|
|
|
{
|
|
|
|
switch(eventCode) {
|
2013-09-20 20:09:39 +08:00
|
|
|
case EventTouch::EventCode::BEGAN:
|
2012-09-11 14:02:33 +08:00
|
|
|
funcName = "onTouchBegan";
|
|
|
|
break;
|
2013-09-20 20:09:39 +08:00
|
|
|
case EventTouch::EventCode::ENDED:
|
2012-09-11 14:02:33 +08:00
|
|
|
funcName = "onTouchEnded";
|
|
|
|
break;
|
2013-09-20 20:09:39 +08:00
|
|
|
case EventTouch::EventCode::MOVED:
|
2012-09-11 14:02:33 +08:00
|
|
|
funcName = "onTouchMoved";
|
|
|
|
break;
|
2013-09-20 20:09:39 +08:00
|
|
|
case EventTouch::EventCode::CANCELLED:
|
2012-09-11 14:02:33 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-06-20 14:33:59 +08:00
|
|
|
static void getJSTouchObject(JSContext *cx, Touch *x, jsval &jsret) {
|
|
|
|
js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::Touch>(cx, x);
|
fixed #1617: Some improvements for JS Bindings.
1) Changed cc.REPEAT_FOREVER = - 1 to cc.REPEAT_FOREVER = 0xffffffff
[Reason]: If cc.REPEAT_FOREVER = -1, it will be a very big double value after converting it to double by JS_ValueToNumber on android.
Then cast it to unsigned int, the value will be 0. The schedule will not be able to work.
I don't know why this occurs only on android.
[Solution]: Instead of passing -1 to it, I assign it with max value of unsigned int in c++.
2) Added two helper function, cc.ArrayGetIndexOfObject and cc.ArrayContainsObject.
3) Added JSScheduleWrapper::removeTargetForNativeNode to avoid memory leaks.
4) Improvments for JSTouchDelegate. Added four functions as follows:
// Set the touch delegate to map by using the key (pJSObj).
static void setDelegateForJSObject(JSObject* pJSObj, JSTouchDelegate* pDelegate);
// Get the touch delegate by the key (pJSObj).
static JSTouchDelegate* getDelegateForJSObject(JSObject* pJSObj);
// Remove the delegate by the key (pJSObj).
static void removeDelegateForJSObject(JSObject* pJSObj);
void unregisterTouchDelegate();
And exported cc.unregisterTouchDelegate(); to js.
Fix a memory leak for JSTouchDelegate by making it as an autorelease object.
5) Don't add js callback function to the reserved slot of object.
[Reason]: The target object may execute more than one schedule.
Therefore, previous js callback function will be replaced
by the current one. For example:
this.scheduleOnce(function() { temporary function 1 }, 0.5);
this.scheduleOnce(function() { temporary function 2 }, 0.5);
In this case, the temporary function 1 will be removed from reserved slot 0.
And temporary function 2 will be set to reserved slot 0 of this object.
If gc is triggered before the JSScheduleWrapper::scheduleFunc is invoked,
crash will happen. You could simply reproduce it by adding jsc.garbageCollect(); after scheduleOnce.
[Solution] Because one schedule corresponds to one JSScheduleWrapper, we root
the js callback function in JSScheduleWrapper::setJSCallbackFunc and unroot it
at the destructor of JSScheduleWrapper.
2012-12-18 11:56:44 +08:00
|
|
|
jsret = OBJECT_TO_JSVAL(proxy->obj);
|
2012-09-11 14:02:33 +08:00
|
|
|
}
|
|
|
|
|
2013-06-20 14:33:59 +08:00
|
|
|
static void removeJSTouchObject(JSContext *cx, Touch *x, jsval &jsret) {
|
2012-09-11 14:02:33 +08:00
|
|
|
js_proxy_t* nproxy;
|
|
|
|
js_proxy_t* jsproxy;
|
fixed #1617: Some improvements for JS Bindings.
1) Changed cc.REPEAT_FOREVER = - 1 to cc.REPEAT_FOREVER = 0xffffffff
[Reason]: If cc.REPEAT_FOREVER = -1, it will be a very big double value after converting it to double by JS_ValueToNumber on android.
Then cast it to unsigned int, the value will be 0. The schedule will not be able to work.
I don't know why this occurs only on android.
[Solution]: Instead of passing -1 to it, I assign it with max value of unsigned int in c++.
2) Added two helper function, cc.ArrayGetIndexOfObject and cc.ArrayContainsObject.
3) Added JSScheduleWrapper::removeTargetForNativeNode to avoid memory leaks.
4) Improvments for JSTouchDelegate. Added four functions as follows:
// Set the touch delegate to map by using the key (pJSObj).
static void setDelegateForJSObject(JSObject* pJSObj, JSTouchDelegate* pDelegate);
// Get the touch delegate by the key (pJSObj).
static JSTouchDelegate* getDelegateForJSObject(JSObject* pJSObj);
// Remove the delegate by the key (pJSObj).
static void removeDelegateForJSObject(JSObject* pJSObj);
void unregisterTouchDelegate();
And exported cc.unregisterTouchDelegate(); to js.
Fix a memory leak for JSTouchDelegate by making it as an autorelease object.
5) Don't add js callback function to the reserved slot of object.
[Reason]: The target object may execute more than one schedule.
Therefore, previous js callback function will be replaced
by the current one. For example:
this.scheduleOnce(function() { temporary function 1 }, 0.5);
this.scheduleOnce(function() { temporary function 2 }, 0.5);
In this case, the temporary function 1 will be removed from reserved slot 0.
And temporary function 2 will be set to reserved slot 0 of this object.
If gc is triggered before the JSScheduleWrapper::scheduleFunc is invoked,
crash will happen. You could simply reproduce it by adding jsc.garbageCollect(); after scheduleOnce.
[Solution] Because one schedule corresponds to one JSScheduleWrapper, we root
the js callback function in JSScheduleWrapper::setJSCallbackFunc and unroot it
at the destructor of JSScheduleWrapper.
2012-12-18 11:56:44 +08:00
|
|
|
void *ptr = (void*)x;
|
2013-06-04 12:53:42 +08:00
|
|
|
nproxy = jsb_get_native_proxy(ptr);
|
2012-09-11 14:02:33 +08:00
|
|
|
if (nproxy) {
|
2013-06-04 12:53:42 +08:00
|
|
|
jsproxy = jsb_get_js_proxy(nproxy->obj);
|
fixed #1617: Some improvements for JS Bindings.
1) Changed cc.REPEAT_FOREVER = - 1 to cc.REPEAT_FOREVER = 0xffffffff
[Reason]: If cc.REPEAT_FOREVER = -1, it will be a very big double value after converting it to double by JS_ValueToNumber on android.
Then cast it to unsigned int, the value will be 0. The schedule will not be able to work.
I don't know why this occurs only on android.
[Solution]: Instead of passing -1 to it, I assign it with max value of unsigned int in c++.
2) Added two helper function, cc.ArrayGetIndexOfObject and cc.ArrayContainsObject.
3) Added JSScheduleWrapper::removeTargetForNativeNode to avoid memory leaks.
4) Improvments for JSTouchDelegate. Added four functions as follows:
// Set the touch delegate to map by using the key (pJSObj).
static void setDelegateForJSObject(JSObject* pJSObj, JSTouchDelegate* pDelegate);
// Get the touch delegate by the key (pJSObj).
static JSTouchDelegate* getDelegateForJSObject(JSObject* pJSObj);
// Remove the delegate by the key (pJSObj).
static void removeDelegateForJSObject(JSObject* pJSObj);
void unregisterTouchDelegate();
And exported cc.unregisterTouchDelegate(); to js.
Fix a memory leak for JSTouchDelegate by making it as an autorelease object.
5) Don't add js callback function to the reserved slot of object.
[Reason]: The target object may execute more than one schedule.
Therefore, previous js callback function will be replaced
by the current one. For example:
this.scheduleOnce(function() { temporary function 1 }, 0.5);
this.scheduleOnce(function() { temporary function 2 }, 0.5);
In this case, the temporary function 1 will be removed from reserved slot 0.
And temporary function 2 will be set to reserved slot 0 of this object.
If gc is triggered before the JSScheduleWrapper::scheduleFunc is invoked,
crash will happen. You could simply reproduce it by adding jsc.garbageCollect(); after scheduleOnce.
[Solution] Because one schedule corresponds to one JSScheduleWrapper, we root
the js callback function in JSScheduleWrapper::setJSCallbackFunc and unroot it
at the destructor of JSScheduleWrapper.
2012-12-18 11:56:44 +08:00
|
|
|
JS_RemoveObjectRoot(cx, &jsproxy->obj);
|
2013-06-04 12:53:42 +08:00
|
|
|
jsb_remove_proxy(nproxy, jsproxy);
|
2012-09-11 14:02:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-25 20:52:16 +08:00
|
|
|
void ScriptingCore::executeJSFunctionWithThisObj(jsval thisObj,
|
|
|
|
jsval callback,
|
|
|
|
uint32_t argc/* = 0*/,
|
|
|
|
jsval* vp/* = NULL*/,
|
|
|
|
jsval* retVal/* = NULL*/)
|
|
|
|
{
|
2013-08-09 14:49:39 +08:00
|
|
|
if (callback != JSVAL_VOID || thisObj != JSVAL_VOID)
|
2013-08-09 10:58:25 +08:00
|
|
|
{
|
2013-10-31 15:13:49 +08:00
|
|
|
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
|
|
|
|
|
2013-08-09 10:58:25 +08:00
|
|
|
// Very important: The last parameter 'retVal' passed to 'JS_CallFunctionValue' should not be a NULL pointer.
|
|
|
|
// If it's a NULL pointer, crash will be triggered in 'JS_CallFunctionValue'. To find out the reason of this crash is very difficult.
|
|
|
|
// So we have to check the availability of 'retVal'.
|
|
|
|
if (retVal)
|
|
|
|
{
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_CallFunctionValue(_cx, JSVAL_TO_OBJECT(thisObj), callback, argc, vp, retVal);
|
2013-08-09 10:58:25 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
jsval jsRet;
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_CallFunctionValue(_cx, JSVAL_TO_OBJECT(thisObj), callback, argc, vp, &jsRet);
|
2013-08-09 10:58:25 +08:00
|
|
|
}
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void js_log(const char *format, ...) {
|
2013-10-08 16:21:10 +08:00
|
|
|
|
|
|
|
if (_js_log_buf == NULL)
|
|
|
|
{
|
2013-04-17 16:08:48 +08:00
|
|
|
_js_log_buf = (char *)calloc(sizeof(char), kMaxLogLen+1);
|
2013-10-08 16:21:10 +08:00
|
|
|
_js_log_buf[kMaxLogLen] = '\0';
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
va_list vl;
|
|
|
|
va_start(vl, format);
|
2013-04-17 16:08:48 +08:00
|
|
|
int len = vsnprintf(_js_log_buf, kMaxLogLen, format, vl);
|
2012-08-28 10:22:36 +08:00
|
|
|
va_end(vl);
|
2013-10-08 16:21:10 +08:00
|
|
|
if (len > 0)
|
|
|
|
{
|
2012-08-28 05:47:21 +08:00
|
|
|
CCLOG("JS: %s\n", _js_log_buf);
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-19 08:44:41 +08:00
|
|
|
#define JSB_COMPATIBLE_WITH_COCOS2D_HTML5_BASIC_TYPES 1
|
|
|
|
|
2013-01-05 15:09:19 +08:00
|
|
|
JSBool JSBCore_platform(JSContext *cx, uint32_t argc, jsval *vp)
|
|
|
|
{
|
2013-02-22 00:34:51 +08:00
|
|
|
if (argc!=0)
|
2013-01-05 15:09:19 +08:00
|
|
|
{
|
|
|
|
JS_ReportError(cx, "Invalid number of arguments in __getPlatform");
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
JSString * platform;
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-01-05 15:09:19 +08:00
|
|
|
// config.deviceType: Device Type
|
|
|
|
// 'mobile' for any kind of mobile devices, 'desktop' for PCs, 'browser' for Web Browsers
|
|
|
|
// #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
|
|
|
|
// platform = JS_InternString(_cx, "desktop");
|
|
|
|
// #else
|
|
|
|
platform = JS_InternString(cx, "mobile");
|
|
|
|
// #endif
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
jsval ret = STRING_TO_JSVAL(platform);
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
JS_SET_RVAL(cx, vp, ret);
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
return JS_TRUE;
|
2013-01-05 15:09:19 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
JSBool JSBCore_version(JSContext *cx, uint32_t argc, jsval *vp)
|
|
|
|
{
|
2013-01-05 17:41:19 +08:00
|
|
|
if (argc!=0)
|
2013-01-05 15:09:19 +08:00
|
|
|
{
|
|
|
|
JS_ReportError(cx, "Invalid number of arguments in __getVersion");
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
char version[256];
|
2013-11-11 10:40:51 +08:00
|
|
|
snprintf(version, sizeof(version)-1, "%s", cocos2dVersion());
|
2013-02-22 00:34:51 +08:00
|
|
|
JSString * js_version = JS_InternString(cx, version);
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
jsval ret = STRING_TO_JSVAL(js_version);
|
|
|
|
JS_SET_RVAL(cx, vp, ret);
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
return JS_TRUE;
|
2013-01-05 15:09:19 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
JSBool JSBCore_os(JSContext *cx, uint32_t argc, jsval *vp)
|
2012-10-19 08:44:41 +08:00
|
|
|
{
|
2013-01-05 17:41:19 +08:00
|
|
|
if (argc!=0)
|
2013-01-05 15:09:19 +08:00
|
|
|
{
|
|
|
|
JS_ReportError(cx, "Invalid number of arguments in __getOS");
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
JSString * os;
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2012-10-19 08:44:41 +08:00
|
|
|
// osx, ios, android, windows, linux, etc..
|
|
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
2013-01-05 15:09:19 +08:00
|
|
|
os = JS_InternString(cx, "ios");
|
2012-10-19 08:44:41 +08:00
|
|
|
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
2013-01-05 15:09:19 +08:00
|
|
|
os = JS_InternString(cx, "android");
|
2012-10-19 08:44:41 +08:00
|
|
|
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
2013-01-05 15:09:19 +08:00
|
|
|
os = JS_InternString(cx, "windows");
|
2012-10-19 08:44:41 +08:00
|
|
|
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE)
|
2013-01-05 15:09:19 +08:00
|
|
|
os = JS_InternString(cx, "marmalade");
|
2012-10-19 08:44:41 +08:00
|
|
|
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
|
2013-01-05 15:09:19 +08:00
|
|
|
os = JS_InternString(cx, "linux");
|
2012-10-19 08:44:41 +08:00
|
|
|
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_BADA)
|
2013-01-05 15:09:19 +08:00
|
|
|
os = JS_InternString(cx, "bada");
|
2012-10-19 08:44:41 +08:00
|
|
|
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_BLACKBERRY)
|
2013-01-05 15:09:19 +08:00
|
|
|
os = JS_InternString(cx, "blackberry");
|
2012-10-19 08:44:41 +08:00
|
|
|
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
|
2013-01-05 15:09:19 +08:00
|
|
|
os = JS_InternString(cx, "osx");
|
2012-10-19 08:44:41 +08:00
|
|
|
#else
|
2013-01-05 15:09:19 +08:00
|
|
|
os = JS_InternString(cx, "unknown");
|
2012-10-19 08:44:41 +08:00
|
|
|
#endif
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
jsval ret = STRING_TO_JSVAL(os);
|
|
|
|
JS_SET_RVAL(cx, vp, ret);
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
return JS_TRUE;
|
2013-01-05 15:09:19 +08:00
|
|
|
};
|
2012-10-19 08:44:41 +08:00
|
|
|
|
2013-03-18 18:24:37 +08:00
|
|
|
JSBool JSB_core_restartVM(JSContext *cx, uint32_t argc, jsval *vp)
|
|
|
|
{
|
|
|
|
JSB_PRECONDITION2(argc==0, cx, JS_FALSE, "Invalid number of arguments in executeScript");
|
|
|
|
ScriptingCore::getInstance()->reset();
|
|
|
|
JS_SET_RVAL(cx, vp, JSVAL_VOID);
|
|
|
|
return JS_TRUE;
|
|
|
|
};
|
2012-10-10 15:24:20 +08:00
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
void registerDefaultClasses(JSContext* cx, JSObject* global) {
|
2012-10-10 15:24:20 +08:00
|
|
|
// first, try to get the ns
|
2013-10-30 21:56:07 +08:00
|
|
|
JS::RootedValue nsval(cx);
|
2012-10-10 15:24:20 +08:00
|
|
|
JSObject *ns;
|
|
|
|
JS_GetProperty(cx, global, "cc", &nsval);
|
|
|
|
if (nsval == JSVAL_VOID) {
|
|
|
|
ns = JS_NewObject(cx, NULL, NULL, NULL);
|
|
|
|
nsval = OBJECT_TO_JSVAL(ns);
|
2013-10-30 21:56:07 +08:00
|
|
|
JS_SetProperty(cx, global, "cc", nsval);
|
2012-10-10 15:24:20 +08:00
|
|
|
} else {
|
|
|
|
JS_ValueToObject(cx, nsval, &ns);
|
|
|
|
}
|
|
|
|
|
2012-10-20 00:54:21 +08:00
|
|
|
//
|
2012-08-28 10:22:36 +08:00
|
|
|
// Javascript controller (__jsc__)
|
|
|
|
//
|
|
|
|
JSObject *jsc = JS_NewObject(cx, NULL, NULL, NULL);
|
2013-10-30 21:56:07 +08:00
|
|
|
JS::RootedValue jscVal(cx);
|
|
|
|
jscVal = OBJECT_TO_JSVAL(jsc);
|
|
|
|
JS_SetProperty(cx, global, "__jsc__", jscVal);
|
2012-08-28 10:22:36 +08:00
|
|
|
|
|
|
|
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);
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-01-05 15:09:19 +08:00
|
|
|
JS_DefineFunction(cx, global, "__getPlatform", JSBCore_platform, 0, JSPROP_READONLY | JSPROP_PERMANENT);
|
2013-02-22 00:34:51 +08:00
|
|
|
JS_DefineFunction(cx, global, "__getOS", JSBCore_os, 0, JSPROP_READONLY | JSPROP_PERMANENT);
|
|
|
|
JS_DefineFunction(cx, global, "__getVersion", JSBCore_version, 0, JSPROP_READONLY | JSPROP_PERMANENT);
|
2013-03-18 18:24:37 +08:00
|
|
|
JS_DefineFunction(cx, global, "__restartVM", JSB_core_restartVM, 0, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE );
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
2013-04-25 20:56:27 +08:00
|
|
|
static void sc_finalize(JSFreeOp *freeOp, JSObject *obj) {
|
|
|
|
CCLOGINFO("jsbindings: finalizing JS object %p (global class)", obj);
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static JSClass global_class = {
|
|
|
|
"global", JSCLASS_GLOBAL_FLAGS,
|
2013-08-07 09:56:16 +08:00
|
|
|
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
2012-08-28 10:22:36 +08:00
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, sc_finalize,
|
|
|
|
JSCLASS_NO_OPTIONAL_MEMBERS
|
|
|
|
};
|
|
|
|
|
|
|
|
ScriptingCore::ScriptingCore()
|
2013-10-11 09:52:35 +08:00
|
|
|
: _rt(nullptr)
|
|
|
|
, _cx(nullptr)
|
|
|
|
, _global(nullptr)
|
|
|
|
, _debugGlobal(nullptr)
|
2012-08-28 10:22:36 +08:00
|
|
|
{
|
|
|
|
// set utf8 strings internally (we don't need utf16)
|
2013-02-27 15:16:49 +08:00
|
|
|
// XXX: Removed in SpiderMonkey 19.0
|
|
|
|
//JS_SetCStringsAreUTF8();
|
2012-08-28 10:22:36 +08:00
|
|
|
this->addRegisterCallback(registerDefaultClasses);
|
2013-10-11 09:52:35 +08:00
|
|
|
this->_runLoop = new SimpleRunLoop();
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2013-02-22 00:34:51 +08:00
|
|
|
JSStringWrapper wrapper(str);
|
2013-11-10 11:23:34 +08:00
|
|
|
LOGD("val : return string =\n%s\n", wrapper.get());
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
} 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-19 08:44:41 +08:00
|
|
|
JSBool ScriptingCore::evalString(const char *string, jsval *outVal, const char *filename, JSContext* cx, JSObject* global)
|
2012-08-28 10:22:36 +08:00
|
|
|
{
|
2012-10-19 11:15:23 +08:00
|
|
|
if (cx == NULL)
|
2013-10-11 09:52:35 +08:00
|
|
|
cx = _cx;
|
2012-10-19 11:15:23 +08:00
|
|
|
if (global == NULL)
|
2013-10-11 09:52:35 +08:00
|
|
|
global = _global;
|
2013-11-25 15:52:59 +08:00
|
|
|
|
|
|
|
JSAutoCompartment ac(cx, global);
|
|
|
|
|
2012-10-19 11:15:23 +08:00
|
|
|
JSScript* script = JS_CompileScript(cx, global, string, strlen(string), filename, 1);
|
2013-11-25 15:52:59 +08:00
|
|
|
if (script)
|
|
|
|
{
|
2013-05-02 09:03:24 +08:00
|
|
|
JSBool evaluatedOK = JS_ExecuteScript(cx, global, script, outVal);
|
2013-11-25 15:52:59 +08:00
|
|
|
if (JS_FALSE == evaluatedOK)
|
|
|
|
{
|
2012-10-19 11:15:23 +08:00
|
|
|
fprintf(stderr, "(evaluatedOK == JS_FALSE)\n");
|
|
|
|
}
|
|
|
|
return evaluatedOK;
|
|
|
|
}
|
2013-04-25 20:56:27 +08:00
|
|
|
return JS_FALSE;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptingCore::start() {
|
|
|
|
// for now just this
|
|
|
|
this->createGlobalContext();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptingCore::addRegisterCallback(sc_register_sth callback) {
|
|
|
|
registrationList.push_back(callback);
|
|
|
|
}
|
|
|
|
|
2012-09-19 09:44:21 +08:00
|
|
|
void ScriptingCore::removeAllRoots(JSContext *cx) {
|
|
|
|
js_proxy_t *current, *tmp;
|
|
|
|
HASH_ITER(hh, _js_native_global_ht, current, tmp) {
|
|
|
|
JS_RemoveObjectRoot(cx, ¤t->obj);
|
2012-10-12 09:05:25 +08:00
|
|
|
HASH_DEL(_js_native_global_ht, current);
|
|
|
|
free(current);
|
|
|
|
}
|
|
|
|
HASH_ITER(hh, _native_js_global_ht, current, tmp) {
|
|
|
|
HASH_DEL(_native_js_global_ht, current);
|
|
|
|
free(current);
|
2012-09-19 09:44:21 +08:00
|
|
|
}
|
|
|
|
HASH_CLEAR(hh, _js_native_global_ht);
|
|
|
|
HASH_CLEAR(hh, _native_js_global_ht);
|
|
|
|
}
|
|
|
|
|
2013-04-25 20:56:27 +08:00
|
|
|
static JSPrincipals shellTrustedPrincipals = { 1 };
|
|
|
|
|
|
|
|
static JSBool
|
|
|
|
CheckObjectAccess(JSContext *cx, js::HandleObject obj, js::HandleId id, JSAccessMode mode,
|
|
|
|
js::MutableHandleValue vp)
|
|
|
|
{
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static JSSecurityCallbacks securityCallbacks = {
|
|
|
|
CheckObjectAccess,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
void ScriptingCore::createGlobalContext() {
|
2013-10-11 09:52:35 +08:00
|
|
|
if (this->_cx && this->_rt) {
|
|
|
|
ScriptingCore::removeAllRoots(this->_cx);
|
|
|
|
JS_DestroyContext(this->_cx);
|
|
|
|
JS_DestroyRuntime(this->_rt);
|
|
|
|
this->_cx = NULL;
|
|
|
|
this->_rt = NULL;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
2013-10-30 21:56:07 +08:00
|
|
|
|
|
|
|
// Start the engine. Added in SpiderMonkey v25
|
|
|
|
if (!JS_Init())
|
|
|
|
return;
|
|
|
|
|
2013-04-25 20:56:27 +08:00
|
|
|
// Removed from Spidermonkey 19.
|
2013-01-25 09:09:59 +08:00
|
|
|
//JS_SetCStringsAreUTF8();
|
2013-10-11 09:52:35 +08:00
|
|
|
this->_rt = JS_NewRuntime(8L * 1024L * 1024L, JS_USE_HELPER_THREADS);
|
|
|
|
JS_SetGCParameter(_rt, JSGC_MAX_BYTES, 0xffffffff);
|
2013-04-25 20:56:27 +08:00
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_SetTrustedPrincipals(_rt, &shellTrustedPrincipals);
|
|
|
|
JS_SetSecurityCallbacks(_rt, &securityCallbacks);
|
|
|
|
JS_SetNativeStackQuota(_rt, JSB_MAX_STACK_QUOTA);
|
2013-04-25 20:56:27 +08:00
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
this->_cx = JS_NewContext(_rt, 8192);
|
2013-10-30 21:56:07 +08:00
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_SetOptions(this->_cx, JSOPTION_TYPE_INFERENCE);
|
2013-10-30 21:56:07 +08:00
|
|
|
|
|
|
|
// JS_SetVersion(this->_cx, JSVERSION_LATEST);
|
2013-05-22 17:31:07 +08:00
|
|
|
|
|
|
|
// Only disable METHODJIT on iOS.
|
2013-10-10 15:45:24 +08:00
|
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
2013-10-30 21:56:07 +08:00
|
|
|
// JS_SetOptions(this->_cx, JS_GetOptions(this->_cx) & ~JSOPTION_METHODJIT);
|
|
|
|
// JS_SetOptions(this->_cx, JS_GetOptions(this->_cx) & ~JSOPTION_METHODJIT_ALWAYS);
|
2013-10-10 15:45:24 +08:00
|
|
|
#endif
|
2013-05-22 17:31:07 +08:00
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_SetErrorReporter(this->_cx, ScriptingCore::reportError);
|
2012-12-01 10:17:20 +08:00
|
|
|
#if defined(JS_GC_ZEAL) && defined(DEBUG)
|
2013-10-11 09:52:35 +08:00
|
|
|
//JS_SetGCZeal(this->_cx, 2, JS_DEFAULT_ZEAL_FREQ);
|
issue #1581: JSBinding bug fixes.
Some fixes of JSBinding codes:
[1] Check whether the proxy was already added in JS_NEW_PROXY
[2] In struct schedFunc_proxy_t, JSScheduleWrapper* --> CCArray*
Reason: One js function may correspond to many targets. To debug this, you could refer to JSScheduleWrapper::dump function. It will prove that i'm right. :)
[3] In ScriptingCore::cleanupSchedulesAndActions function, we must invoke unschedule for all targets and remove the proxy both in _schedFunc_target_ht and _schedTarget_native_ht, otherwise the hash tables will grow bigger and bigger, so I added a new function JSScheduleWrapper::removeAllTargetsForNatiaveNode to make this things easier.
[4] To easily find out the bugs of binding codes, I add JS_SetGCZeal in ScriptingCore::createGlobalContext, it only works in DEBUG mode.
[5] In js_cocos2dx_CCNode_getChildren, we should add the generated array to root to avoid gc happen when invoking JS_SetElement.
[6] The JSCallFuncWrapper isn't needed since an action will be run by a cc.Node and it will be released at the CCNode::cleanup.
[7] Some improvements of JSScheduleWrapper class.
[8] Added a new function JSScheduleWrapper::setTarget, it's for js_CCNode_unschedule to find out which target need to be unscheduled.
[9] Commented JS_SetReservedSlot in js_CCNode_scheduleOnce and js_CCNode_schedule.
Reason:
For js_CCNode_scheduleOnce: Don't add the callback function to the reserved slot of this js object.Since the class of js object may be inherited from cocos class(e.g. cc.Sprite). The subclass will not contain reserved slots. It will crash if invoking this.
For js_CCNode_schedule: Don't add js callback function to the reserved slot of scheduler js object. Since the scheduler is an object always rooted.
So the callback function might not be released when gc comes.I looked inside the implementation of cc.Node.schedule, and it doesn't use JS_SetReservedSlot there.
2012-11-28 22:04:55 +08:00
|
|
|
#endif
|
2013-10-11 09:52:35 +08:00
|
|
|
this->_global = NewGlobalObject(_cx);
|
2013-10-31 21:03:06 +08:00
|
|
|
|
2013-10-30 21:56:07 +08:00
|
|
|
JSAutoCompartment ac(_cx, _global);
|
|
|
|
js::SetDefaultObjectForContext(_cx, _global);
|
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
for (std::vector<sc_register_sth>::iterator it = registrationList.begin(); it != registrationList.end(); it++) {
|
|
|
|
sc_register_sth callback = *it;
|
2013-10-11 09:52:35 +08:00
|
|
|
callback(this->_cx, this->_global);
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-06 19:06:16 +08:00
|
|
|
static std::string RemoveFileExt(const std::string& filePath) {
|
2013-05-06 11:32:23 +08:00
|
|
|
size_t pos = filePath.rfind('.');
|
|
|
|
if (0 < pos) {
|
|
|
|
return filePath.substr(0, pos);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return filePath;
|
2013-04-30 03:29:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-19 08:44:41 +08:00
|
|
|
JSBool ScriptingCore::runScript(const char *path, JSObject* global, JSContext* cx)
|
2012-08-28 10:22:36 +08:00
|
|
|
{
|
2012-10-19 11:15:23 +08:00
|
|
|
if (!path) {
|
|
|
|
return false;
|
|
|
|
}
|
2013-10-07 10:50:12 +08:00
|
|
|
|
|
|
|
|
2013-07-12 06:24:23 +08:00
|
|
|
cocos2d::FileUtils *futil = cocos2d::FileUtils::getInstance();
|
2013-10-31 21:03:06 +08:00
|
|
|
|
2012-10-19 11:15:23 +08:00
|
|
|
if (global == NULL) {
|
2013-10-11 09:52:35 +08:00
|
|
|
global = _global;
|
2012-10-19 11:15:23 +08:00
|
|
|
}
|
|
|
|
if (cx == NULL) {
|
2013-10-11 09:52:35 +08:00
|
|
|
cx = _cx;
|
2012-10-19 11:15:23 +08:00
|
|
|
}
|
2013-10-30 21:56:07 +08:00
|
|
|
|
|
|
|
JSAutoCompartment ac(cx, global);
|
|
|
|
|
|
|
|
js::RootedScript script(cx);
|
2013-02-27 16:57:36 +08:00
|
|
|
js::RootedObject obj(cx, global);
|
2013-10-30 21:56:07 +08:00
|
|
|
|
2013-07-25 17:58:15 +08:00
|
|
|
// a) check jsc file first
|
2013-06-27 17:20:02 +08:00
|
|
|
std::string byteCodePath = RemoveFileExt(std::string(path)) + BYTE_CODE_FILE_EXT;
|
2013-12-13 17:53:03 +08:00
|
|
|
ssize_t length = 0;
|
2013-10-10 15:45:24 +08:00
|
|
|
unsigned char* data = futil->getFileData(byteCodePath.c_str(),
|
2013-06-27 17:20:02 +08:00
|
|
|
"rb",
|
|
|
|
&length);
|
2013-10-07 10:50:12 +08:00
|
|
|
|
2013-06-27 17:20:02 +08:00
|
|
|
if (data) {
|
|
|
|
script = JS_DecodeScript(cx, data, length, NULL, NULL);
|
2013-11-12 10:09:47 +08:00
|
|
|
free(data);
|
2013-05-06 11:32:23 +08:00
|
|
|
}
|
2013-06-27 17:20:02 +08:00
|
|
|
|
|
|
|
// b) no jsc file, check js file
|
2013-05-06 11:32:23 +08:00
|
|
|
if (!script) {
|
2013-07-25 17:58:15 +08:00
|
|
|
/* Clear any pending exception from previous failed decoding. */
|
|
|
|
ReportException(cx);
|
|
|
|
|
2013-10-31 21:03:06 +08:00
|
|
|
std::string fullPath = futil->fullPathForFilename(path);
|
|
|
|
JS::CompileOptions options(cx);
|
|
|
|
options.setUTF8(true).setFileAndLine(fullPath.c_str(), 1);
|
|
|
|
|
2013-06-27 17:20:02 +08:00
|
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
|
|
|
|
String* content = String::createWithContentsOfFile(path);
|
|
|
|
if (content) {
|
|
|
|
// Not supported in SpiderMonkey 19.0
|
|
|
|
//JSScript* script = JS_CompileScript(cx, global, (char*)content, contentSize, path, 1);
|
|
|
|
const char* contentCStr = content->getCString();
|
|
|
|
script = JS::Compile(cx, obj, options, contentCStr, strlen(contentCStr));
|
2013-05-06 11:32:23 +08:00
|
|
|
}
|
2013-06-27 17:20:02 +08:00
|
|
|
#else
|
|
|
|
script = JS::Compile(cx, obj, options, fullPath.c_str());
|
|
|
|
#endif
|
2013-04-30 03:29:56 +08:00
|
|
|
}
|
2013-06-27 17:20:02 +08:00
|
|
|
|
2012-10-19 11:15:23 +08:00
|
|
|
JSBool evaluatedOK = false;
|
|
|
|
if (script) {
|
|
|
|
jsval rval;
|
|
|
|
filename_script[path] = script;
|
2013-02-22 00:34:51 +08:00
|
|
|
JSAutoCompartment ac(cx, global);
|
2012-10-19 11:15:23 +08:00
|
|
|
evaluatedOK = JS_ExecuteScript(cx, global, script, &rval);
|
|
|
|
if (JS_FALSE == evaluatedOK) {
|
2013-07-24 06:20:22 +08:00
|
|
|
cocos2d::log("(evaluatedOK == JS_FALSE)");
|
2013-02-22 00:34:51 +08:00
|
|
|
JS_ReportPendingException(cx);
|
2012-10-19 11:15:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return evaluatedOK;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
2013-02-22 00:33:37 +08:00
|
|
|
void ScriptingCore::reset()
|
|
|
|
{
|
|
|
|
cleanup();
|
|
|
|
start();
|
|
|
|
}
|
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
ScriptingCore::~ScriptingCore()
|
2013-02-22 00:33:37 +08:00
|
|
|
{
|
|
|
|
cleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptingCore::cleanup()
|
2012-08-28 10:22:36 +08:00
|
|
|
{
|
2013-01-07 17:34:22 +08:00
|
|
|
localStorageFree();
|
2013-10-11 09:52:35 +08:00
|
|
|
removeAllRoots(_cx);
|
|
|
|
if (_cx)
|
2013-02-22 00:33:37 +08:00
|
|
|
{
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_DestroyContext(_cx);
|
|
|
|
_cx = NULL;
|
2013-02-22 00:33:37 +08:00
|
|
|
}
|
2013-10-11 09:52:35 +08:00
|
|
|
if (_rt)
|
2013-02-22 00:33:37 +08:00
|
|
|
{
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_DestroyRuntime(_rt);
|
|
|
|
_rt = NULL;
|
2013-02-22 00:33:37 +08:00
|
|
|
}
|
2012-08-28 10:22:36 +08:00
|
|
|
JS_ShutDown();
|
|
|
|
if (_js_log_buf) {
|
|
|
|
free(_js_log_buf);
|
|
|
|
_js_log_buf = NULL;
|
|
|
|
}
|
2012-12-03 22:17:53 +08:00
|
|
|
|
2013-11-11 12:54:36 +08:00
|
|
|
for (auto iter = _js_global_type_map.begin(); iter != _js_global_type_map.end(); ++iter)
|
2012-12-03 22:17:53 +08:00
|
|
|
{
|
2013-11-11 12:54:36 +08:00
|
|
|
free(iter->second->jsclass);
|
|
|
|
free(iter->second);
|
2012-12-03 22:17:53 +08:00
|
|
|
}
|
2013-11-11 12:54:36 +08:00
|
|
|
|
|
|
|
_js_global_type_map.clear();
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptingCore::reportError(JSContext *cx, const char *message, JSErrorReport *report)
|
|
|
|
{
|
2012-10-19 11:15:23 +08:00
|
|
|
js_log("%s:%u:%s\n",
|
|
|
|
report->filename ? report->filename : "<no filename=\"filename\">",
|
|
|
|
(unsigned int) report->lineno,
|
|
|
|
message);
|
2012-08-28 10:22:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
JSBool ScriptingCore::log(JSContext* cx, uint32_t argc, jsval *vp)
|
|
|
|
{
|
2012-10-19 11:15:23 +08:00
|
|
|
if (argc > 0) {
|
|
|
|
JSString *string = NULL;
|
|
|
|
JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S", &string);
|
|
|
|
if (string) {
|
2013-02-22 00:34:51 +08:00
|
|
|
JSStringWrapper wrapper(string);
|
2013-11-10 11:23:34 +08:00
|
|
|
js_log("%s", wrapper.get());
|
2012-10-19 11:15:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
2012-08-28 12:04:51 +08:00
|
|
|
|
2013-06-20 14:33:59 +08:00
|
|
|
void ScriptingCore::removeScriptObjectByObject(Object* pObj)
|
2012-09-11 14:02:33 +08:00
|
|
|
{
|
2012-08-28 12:04:51 +08:00
|
|
|
js_proxy_t* nproxy;
|
|
|
|
js_proxy_t* jsproxy;
|
2012-09-11 14:02:33 +08:00
|
|
|
void *ptr = (void*)pObj;
|
2013-06-04 12:53:42 +08:00
|
|
|
nproxy = jsb_get_native_proxy(ptr);
|
2012-08-28 12:04:51 +08:00
|
|
|
if (nproxy) {
|
|
|
|
JSContext *cx = ScriptingCore::getInstance()->getGlobalContext();
|
2013-06-04 12:53:42 +08:00
|
|
|
jsproxy = jsb_get_js_proxy(nproxy->obj);
|
2012-09-19 09:44:21 +08:00
|
|
|
JS_RemoveObjectRoot(cx, &jsproxy->obj);
|
2013-06-04 12:53:42 +08:00
|
|
|
jsb_remove_proxy(nproxy, jsproxy);
|
2012-08-28 12:04:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
JSBool ScriptingCore::setReservedSpot(uint32_t i, JSObject *obj, jsval value) {
|
2012-10-19 11:15:23 +08:00
|
|
|
JS_SetReservedSlot(obj, i, value);
|
|
|
|
return JS_TRUE;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
JSBool ScriptingCore::executeScript(JSContext *cx, uint32_t argc, jsval *vp)
|
|
|
|
{
|
2012-10-19 11:15:23 +08:00
|
|
|
if (argc >= 1) {
|
|
|
|
jsval* argv = JS_ARGV(cx, vp);
|
|
|
|
JSString* str = JS_ValueToString(cx, argv[0]);
|
2013-02-22 00:34:51 +08:00
|
|
|
JSStringWrapper path(str);
|
2012-10-19 11:15:23 +08:00
|
|
|
JSBool res = false;
|
|
|
|
if (argc == 2 && argv[1].isString()) {
|
|
|
|
JSString* globalName = JSVAL_TO_STRING(argv[1]);
|
2013-02-22 00:34:51 +08:00
|
|
|
JSStringWrapper name(globalName);
|
2013-10-07 17:19:00 +08:00
|
|
|
// js::RootedObject* rootedGlobal = globals[name];
|
|
|
|
JSObject* debugObj = ScriptingCore::getInstance()->getDebugGlobal();
|
|
|
|
if (debugObj) {
|
2013-11-10 11:23:34 +08:00
|
|
|
res = ScriptingCore::getInstance()->runScript(path.get(), debugObj);
|
2012-10-19 11:15:23 +08:00
|
|
|
} else {
|
2013-11-10 11:23:34 +08:00
|
|
|
JS_ReportError(cx, "Invalid global object: %s", name.get());
|
2012-10-19 11:15:23 +08:00
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
} else {
|
2013-10-30 21:56:07 +08:00
|
|
|
JSObject* glob = JS::CurrentGlobalOrNull(cx);
|
2013-11-10 11:23:34 +08:00
|
|
|
res = ScriptingCore::getInstance()->runScript(path.get(), glob);
|
2012-10-19 11:15:23 +08:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
JSBool ScriptingCore::forceGC(JSContext *cx, uint32_t argc, jsval *vp)
|
|
|
|
{
|
2012-10-19 11:15:23 +08:00
|
|
|
JSRuntime *rt = JS_GetRuntime(cx);
|
|
|
|
JS_GC(rt);
|
|
|
|
return JS_TRUE;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
2013-05-22 17:31:07 +08:00
|
|
|
//static void dumpNamedRoot(const char *name, void *addr, JSGCRootType type, void *data)
|
|
|
|
//{
|
|
|
|
// CCLOG("Root: '%s' at %p", name, addr);
|
|
|
|
//}
|
2012-08-28 10:22:36 +08:00
|
|
|
|
|
|
|
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
|
2012-10-19 08:44:41 +08:00
|
|
|
// JSContext *_cx = ScriptingCore::getInstance()->getGlobalContext();
|
|
|
|
// JSRuntime *rt = JS_GetRuntime(_cx);
|
|
|
|
// JS_DumpNamedRoots(rt, dumpNamedRoot, NULL);
|
2013-04-25 20:56:27 +08:00
|
|
|
// JS_DumpHeap(rt, stdout, NULL, JSTRACE_OBJECT, NULL, 2, NULL);
|
2012-08-28 10:22:36 +08:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2013-03-16 21:14:39 +08:00
|
|
|
void ScriptingCore::pauseSchedulesAndActions(js_proxy_t* p)
|
|
|
|
{
|
2013-06-20 14:33:59 +08:00
|
|
|
Array * arr = JSScheduleWrapper::getTargetForJSObject(p->obj);
|
2013-08-09 14:49:39 +08:00
|
|
|
if (! arr) return;
|
2013-03-16 21:14:39 +08:00
|
|
|
|
2013-06-20 14:33:59 +08:00
|
|
|
Node* node = (Node*)p->ptr;
|
2012-10-10 05:59:16 +08:00
|
|
|
for(unsigned int i = 0; i < arr->count(); ++i) {
|
2013-08-22 10:45:47 +08:00
|
|
|
if (arr->getObjectAtIndex(i)) {
|
|
|
|
node->getScheduler()->pauseTarget(arr->getObjectAtIndex(i));
|
2012-10-10 05:59:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-16 21:14:39 +08:00
|
|
|
void ScriptingCore::resumeSchedulesAndActions(js_proxy_t* p)
|
|
|
|
{
|
2013-06-20 14:33:59 +08:00
|
|
|
Array * arr = JSScheduleWrapper::getTargetForJSObject(p->obj);
|
2013-08-09 14:49:39 +08:00
|
|
|
if (!arr) return;
|
2013-03-16 21:14:39 +08:00
|
|
|
|
2013-06-20 14:33:59 +08:00
|
|
|
Node* node = (Node*)p->ptr;
|
2012-10-10 05:59:16 +08:00
|
|
|
for(unsigned int i = 0; i < arr->count(); ++i) {
|
2013-08-22 10:45:47 +08:00
|
|
|
if (!arr->getObjectAtIndex(i)) continue;
|
|
|
|
node->getScheduler()->resumeTarget(arr->getObjectAtIndex(i));
|
2012-10-10 05:59:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-16 21:14:39 +08:00
|
|
|
void ScriptingCore::cleanupSchedulesAndActions(js_proxy_t* p)
|
|
|
|
{
|
2013-11-10 11:23:34 +08:00
|
|
|
Array* arr = JSScheduleWrapper::getTargetForJSObject(p->obj);
|
2013-08-09 14:49:39 +08:00
|
|
|
if (arr) {
|
2013-07-12 06:24:23 +08:00
|
|
|
Scheduler* pScheduler = Director::getInstance()->getScheduler();
|
2013-06-20 14:33:59 +08:00
|
|
|
Object* pObj = NULL;
|
issue #1581: JSBinding bug fixes.
Some fixes of JSBinding codes:
[1] Check whether the proxy was already added in JS_NEW_PROXY
[2] In struct schedFunc_proxy_t, JSScheduleWrapper* --> CCArray*
Reason: One js function may correspond to many targets. To debug this, you could refer to JSScheduleWrapper::dump function. It will prove that i'm right. :)
[3] In ScriptingCore::cleanupSchedulesAndActions function, we must invoke unschedule for all targets and remove the proxy both in _schedFunc_target_ht and _schedTarget_native_ht, otherwise the hash tables will grow bigger and bigger, so I added a new function JSScheduleWrapper::removeAllTargetsForNatiaveNode to make this things easier.
[4] To easily find out the bugs of binding codes, I add JS_SetGCZeal in ScriptingCore::createGlobalContext, it only works in DEBUG mode.
[5] In js_cocos2dx_CCNode_getChildren, we should add the generated array to root to avoid gc happen when invoking JS_SetElement.
[6] The JSCallFuncWrapper isn't needed since an action will be run by a cc.Node and it will be released at the CCNode::cleanup.
[7] Some improvements of JSScheduleWrapper class.
[8] Added a new function JSScheduleWrapper::setTarget, it's for js_CCNode_unschedule to find out which target need to be unscheduled.
[9] Commented JS_SetReservedSlot in js_CCNode_scheduleOnce and js_CCNode_schedule.
Reason:
For js_CCNode_scheduleOnce: Don't add the callback function to the reserved slot of this js object.Since the class of js object may be inherited from cocos class(e.g. cc.Sprite). The subclass will not contain reserved slots. It will crash if invoking this.
For js_CCNode_schedule: Don't add js callback function to the reserved slot of scheduler js object. Since the scheduler is an object always rooted.
So the callback function might not be released when gc comes.I looked inside the implementation of cc.Node.schedule, and it doesn't use JS_SetReservedSlot there.
2012-11-28 22:04:55 +08:00
|
|
|
CCARRAY_FOREACH(arr, pObj)
|
|
|
|
{
|
|
|
|
pScheduler->unscheduleAllForTarget(pObj);
|
|
|
|
}
|
|
|
|
|
2013-03-16 21:14:39 +08:00
|
|
|
JSScheduleWrapper::removeAllTargetsForJSObject(p->obj);
|
2012-10-23 05:51:35 +08:00
|
|
|
}
|
|
|
|
}
|
2012-10-10 05:59:16 +08:00
|
|
|
|
2013-07-22 17:12:53 +08:00
|
|
|
int ScriptingCore::handleNodeEvent(void* data)
|
2012-09-11 14:02:33 +08:00
|
|
|
{
|
2013-07-22 17:12:53 +08:00
|
|
|
if (NULL == data)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
BasicScriptData* basicScriptData = static_cast<BasicScriptData*>(data);
|
|
|
|
if (NULL == basicScriptData->nativeObject || NULL == basicScriptData->value)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
Node* node = static_cast<Node*>(basicScriptData->nativeObject);
|
|
|
|
int action = *((int*)(basicScriptData->value));
|
|
|
|
|
|
|
|
js_proxy_t * p = jsb_get_native_proxy(node);
|
2012-08-28 10:22:36 +08:00
|
|
|
if (!p) return 0;
|
2012-09-11 14:02:33 +08:00
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval retval;
|
|
|
|
jsval dataVal = INT_TO_JSVAL(1);
|
2012-09-11 14:02:33 +08:00
|
|
|
|
2013-08-09 14:49:39 +08:00
|
|
|
if (action == kNodeOnEnter)
|
2012-09-11 14:02:33 +08:00
|
|
|
{
|
2013-03-14 18:14:16 +08:00
|
|
|
executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), "onEnter", 1, &dataVal, &retval);
|
2013-03-16 21:14:39 +08:00
|
|
|
resumeSchedulesAndActions(p);
|
2012-10-20 00:54:21 +08:00
|
|
|
}
|
2013-08-09 14:49:39 +08:00
|
|
|
else if (action == kNodeOnExit)
|
2012-09-11 14:02:33 +08:00
|
|
|
{
|
2013-03-14 18:14:16 +08:00
|
|
|
executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), "onExit", 1, &dataVal, &retval);
|
2013-03-16 21:14:39 +08:00
|
|
|
pauseSchedulesAndActions(p);
|
2012-09-11 14:02:33 +08:00
|
|
|
}
|
2013-08-09 14:49:39 +08:00
|
|
|
else if (action == kNodeOnEnterTransitionDidFinish)
|
2012-09-11 14:02:33 +08:00
|
|
|
{
|
2013-03-14 18:14:16 +08:00
|
|
|
executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), "onEnterTransitionDidFinish", 1, &dataVal, &retval);
|
2012-08-29 02:25:48 +08:00
|
|
|
}
|
2013-08-09 14:49:39 +08:00
|
|
|
else if (action == kNodeOnExitTransitionDidStart)
|
2012-09-11 14:02:33 +08:00
|
|
|
{
|
2013-03-14 18:14:16 +08:00
|
|
|
executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), "onExitTransitionDidStart", 1, &dataVal, &retval);
|
2012-09-11 14:02:33 +08:00
|
|
|
}
|
2013-08-09 14:49:39 +08:00
|
|
|
else if (action == kNodeOnCleanup) {
|
2013-03-16 21:14:39 +08:00
|
|
|
cleanupSchedulesAndActions(p);
|
2012-10-23 05:51:35 +08:00
|
|
|
}
|
2012-08-28 10:22:36 +08:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-07-22 17:12:53 +08:00
|
|
|
int ScriptingCore::handleMenuClickedEvent(void* data)
|
2012-09-11 14:02:33 +08:00
|
|
|
{
|
2013-07-22 17:12:53 +08:00
|
|
|
if (NULL == data)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
BasicScriptData* basicScriptData = static_cast<BasicScriptData*>(data);
|
|
|
|
if (NULL == basicScriptData->nativeObject)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
MenuItem* menuItem = static_cast<MenuItem*>(basicScriptData->nativeObject);
|
|
|
|
|
|
|
|
js_proxy_t * p = jsb_get_native_proxy(menuItem);
|
2012-08-28 10:22:36 +08:00
|
|
|
if (!p) return 0;
|
2012-09-11 14:02:33 +08:00
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval retval;
|
2012-09-11 14:02:33 +08:00
|
|
|
jsval dataVal;
|
2013-07-22 17:12:53 +08:00
|
|
|
js_proxy_t *proxy = jsb_get_native_proxy(menuItem);
|
2012-09-11 14:02:33 +08:00
|
|
|
dataVal = (proxy ? OBJECT_TO_JSVAL(proxy->obj) : JSVAL_NULL);
|
2012-08-28 10:22:36 +08:00
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
executeJSFunctionFromReservedSpot(this->_cx, p->obj, dataVal, retval);
|
2012-08-28 10:22:36 +08:00
|
|
|
|
2012-09-11 14:02:33 +08:00
|
|
|
return 1;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
2013-07-22 17:12:53 +08:00
|
|
|
int ScriptingCore::handleTouchesEvent(void* data)
|
2012-09-11 14:02:33 +08:00
|
|
|
{
|
2013-07-22 17:12:53 +08:00
|
|
|
if (NULL == data)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
TouchesScriptData* touchesScriptData = static_cast<TouchesScriptData*>(data);
|
2013-09-12 16:02:16 +08:00
|
|
|
if (NULL == touchesScriptData->nativeObject || touchesScriptData->touches.empty())
|
2013-07-22 17:12:53 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
Layer* pLayer = static_cast<Layer*>(touchesScriptData->nativeObject);
|
2013-09-20 20:09:39 +08:00
|
|
|
EventTouch::EventCode eventType = touchesScriptData->actionType;
|
2013-09-12 16:02:16 +08:00
|
|
|
const std::vector<Touch*>& touches = touchesScriptData->touches;
|
2013-07-22 17:12:53 +08:00
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
std::string funcName = "";
|
|
|
|
getTouchesFuncName(eventType, funcName);
|
2012-09-11 14:02:33 +08:00
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
JSObject *jsretArr = JS_NewArrayObject(this->_cx, 0, NULL);
|
2012-09-11 14:02:33 +08:00
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_AddNamedObjectRoot(this->_cx, &jsretArr, "touchArray");
|
2012-08-28 10:22:36 +08:00
|
|
|
int count = 0;
|
2013-09-12 16:02:16 +08:00
|
|
|
|
|
|
|
for (auto& touch : touches)
|
|
|
|
{
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval jsret;
|
2013-10-11 09:52:35 +08:00
|
|
|
getJSTouchObject(this->_cx, touch, jsret);
|
|
|
|
if (!JS_SetElement(this->_cx, jsretArr, count, &jsret))
|
2013-09-12 16:02:16 +08:00
|
|
|
{
|
2012-08-28 10:22:36 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-09-12 16:02:16 +08:00
|
|
|
++count;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
2012-09-11 14:02:33 +08:00
|
|
|
|
|
|
|
executeFunctionWithObjectData(pLayer, funcName.c_str(), jsretArr);
|
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_RemoveObjectRoot(this->_cx, &jsretArr);
|
2012-09-11 14:02:33 +08:00
|
|
|
|
2013-09-12 16:02:16 +08:00
|
|
|
for (auto& touch : touches)
|
|
|
|
{
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval jsret;
|
2013-10-11 09:52:35 +08:00
|
|
|
removeJSTouchObject(this->_cx, touch, jsret);
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
2012-09-11 14:02:33 +08:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-07-22 17:12:53 +08:00
|
|
|
int ScriptingCore::handleTouchEvent(void* data)
|
2012-09-11 14:02:33 +08:00
|
|
|
{
|
2013-07-22 17:12:53 +08:00
|
|
|
if (NULL == data)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
TouchScriptData* touchScriptData = static_cast<TouchScriptData*>(data);
|
|
|
|
if (NULL == touchScriptData->nativeObject || NULL == touchScriptData->touch)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
Layer* pLayer = static_cast<Layer*>(touchScriptData->nativeObject);
|
2013-09-20 20:09:39 +08:00
|
|
|
EventTouch::EventCode eventType = touchScriptData->actionType;
|
2013-07-22 17:12:53 +08:00
|
|
|
Touch *pTouch = touchScriptData->touch;
|
|
|
|
|
|
|
|
|
2012-10-23 02:02:54 +08:00
|
|
|
std::string funcName = "";
|
|
|
|
getTouchFuncName(eventType, funcName);
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2012-10-23 02:02:54 +08:00
|
|
|
jsval jsret;
|
2012-10-29 15:10:13 +08:00
|
|
|
getJSTouchObject(this->getGlobalContext(), pTouch, jsret);
|
2012-10-23 02:02:54 +08:00
|
|
|
JSObject *jsObj = JSVAL_TO_OBJECT(jsret);
|
2012-11-07 03:01:46 +08:00
|
|
|
bool retval = executeFunctionWithObjectData(pLayer, funcName.c_str(), jsObj);
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2012-10-29 15:10:13 +08:00
|
|
|
removeJSTouchObject(this->getGlobalContext(), pTouch, jsret);
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2012-11-07 03:01:46 +08:00
|
|
|
return retval;
|
2012-11-06 14:18:13 +08:00
|
|
|
}
|
|
|
|
|
2013-06-20 14:33:59 +08:00
|
|
|
bool ScriptingCore::executeFunctionWithObjectData(Node *self, const char *name, JSObject *obj) {
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2013-06-04 12:53:42 +08:00
|
|
|
js_proxy_t * p = jsb_get_native_proxy(self);
|
2012-11-22 10:24:26 +08:00
|
|
|
if (!p) return false;
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2012-09-11 14:02:33 +08:00
|
|
|
jsval retval;
|
|
|
|
jsval dataVal = OBJECT_TO_JSVAL(obj);
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2013-03-14 18:14:16 +08:00
|
|
|
executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), name, 1, &dataVal, &retval);
|
2013-08-09 14:49:39 +08:00
|
|
|
if (JSVAL_IS_NULL(retval)) {
|
2012-11-22 10:24:26 +08:00
|
|
|
return false;
|
|
|
|
}
|
2013-08-09 14:49:39 +08:00
|
|
|
else if (JSVAL_IS_BOOLEAN(retval)) {
|
2012-11-22 10:24:26 +08:00
|
|
|
return JSVAL_TO_BOOLEAN(retval);
|
|
|
|
}
|
|
|
|
return false;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
|
|
|
|
2013-03-14 18:14:16 +08:00
|
|
|
JSBool ScriptingCore::executeFunctionWithOwner(jsval owner, const char *name, uint32_t argc /* = 0 */, jsval *vp /* = NULL */, jsval* retVal /* = NULL */)
|
|
|
|
{
|
|
|
|
JSBool bRet = JS_FALSE;
|
|
|
|
JSBool hasAction;
|
2013-10-11 09:52:35 +08:00
|
|
|
JSContext* cx = this->_cx;
|
2013-10-30 21:56:07 +08:00
|
|
|
JS::RootedValue temp_retval(cx);
|
2013-03-14 18:14:16 +08:00
|
|
|
JSObject* obj = JSVAL_TO_OBJECT(owner);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2013-10-07 10:50:12 +08:00
|
|
|
JSAutoCompartment ac(cx, obj);
|
|
|
|
|
2013-03-14 18:14:16 +08:00
|
|
|
if (JS_HasProperty(cx, obj, name, &hasAction) && hasAction) {
|
2013-08-09 14:49:39 +08:00
|
|
|
if (!JS_GetProperty(cx, obj, name, &temp_retval)) {
|
2013-03-14 18:14:16 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-08-09 14:49:39 +08:00
|
|
|
if (temp_retval == JSVAL_VOID) {
|
2013-03-14 18:14:16 +08:00
|
|
|
break;
|
2013-10-07 10:50:12 +08:00
|
|
|
}
|
2013-03-14 18:14:16 +08:00
|
|
|
|
|
|
|
if (retVal) {
|
|
|
|
bRet = JS_CallFunctionName(cx, obj, name, argc, vp, retVal);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
jsval jsret;
|
|
|
|
bRet = JS_CallFunctionName(cx, obj, name, argc, vp, &jsret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}while(0);
|
|
|
|
return bRet;
|
2012-09-19 09:21:07 +08:00
|
|
|
}
|
|
|
|
|
2013-07-22 17:12:53 +08:00
|
|
|
int ScriptingCore::handleAccelerometerEvent(void* data)
|
|
|
|
{
|
|
|
|
if (NULL == data)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
BasicScriptData* basicScriptData = static_cast<BasicScriptData*>(data);
|
|
|
|
if (NULL == basicScriptData->nativeObject || NULL == basicScriptData->value)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
Acceleration* accelerationValue = static_cast<Acceleration*>(basicScriptData->value);
|
|
|
|
Layer* layer = static_cast<Layer*>(basicScriptData->nativeObject);
|
|
|
|
|
|
|
|
jsval value = ccacceleration_to_jsval(this->getGlobalContext(), *accelerationValue);
|
2012-11-14 06:58:11 +08:00
|
|
|
JS_AddValueRoot(this->getGlobalContext(), &value);
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2013-07-22 17:12:53 +08:00
|
|
|
executeFunctionWithObjectData(layer, "onAccelerometer", JSVAL_TO_OBJECT(value));
|
2013-02-22 00:33:37 +08:00
|
|
|
|
2012-11-14 06:58:11 +08:00
|
|
|
JS_RemoveValueRoot(this->getGlobalContext(), &value);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-07-22 17:12:53 +08:00
|
|
|
int ScriptingCore::handleKeypadEvent(void* data)
|
2012-12-04 10:52:49 +08:00
|
|
|
{
|
2013-07-22 17:12:53 +08:00
|
|
|
if (NULL == data)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
KeypadScriptData* keypadScriptData = static_cast<KeypadScriptData*>(data);
|
|
|
|
if (NULL == keypadScriptData->nativeObject)
|
|
|
|
return 0;
|
|
|
|
|
2013-09-20 20:09:39 +08:00
|
|
|
EventKeyboard::KeyCode action = keypadScriptData->actionType;
|
2013-07-22 17:12:53 +08:00
|
|
|
|
|
|
|
js_proxy_t * p = jsb_get_native_proxy(keypadScriptData->nativeObject);
|
2013-04-17 10:40:46 +08:00
|
|
|
|
2013-09-12 16:02:16 +08:00
|
|
|
if (p)
|
|
|
|
{
|
2013-09-17 23:21:45 +08:00
|
|
|
JSBool ret = JS_FALSE;
|
|
|
|
switch(action)
|
|
|
|
{
|
2013-09-20 20:09:39 +08:00
|
|
|
case EventKeyboard::KeyCode::KEY_BACKSPACE:
|
2013-09-17 23:21:45 +08:00
|
|
|
ret = executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), "onBackClicked");
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
ret = executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), "backClicked");
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
CCLOG("backClicked will be deprecated, please use onBackClicked instead.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2013-09-20 20:09:39 +08:00
|
|
|
case EventKeyboard::KeyCode::KEY_MENU:
|
2013-09-17 23:21:45 +08:00
|
|
|
ret = executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), "onMenuClicked");
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
ret = executeFunctionWithOwner(OBJECT_TO_JSVAL(p->obj), "menuClicked");
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
CCLOG("menuClicked will be deprecated, please use onMenuClicked instead.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-04-17 10:40:46 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2012-12-04 10:52:49 +08:00
|
|
|
}
|
|
|
|
|
2012-11-14 06:58:11 +08:00
|
|
|
|
2013-09-20 20:09:39 +08:00
|
|
|
int ScriptingCore::executeCustomTouchesEvent(EventTouch::EventCode eventType,
|
2013-09-12 16:02:16 +08:00
|
|
|
const std::vector<Touch*>& touches, JSObject *obj)
|
2012-08-28 10:22:36 +08:00
|
|
|
{
|
|
|
|
jsval retval;
|
|
|
|
std::string funcName;
|
|
|
|
getTouchesFuncName(eventType, funcName);
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
JSObject *jsretArr = JS_NewArrayObject(this->_cx, 0, NULL);
|
|
|
|
JS_AddNamedObjectRoot(this->_cx, &jsretArr, "touchArray");
|
2012-08-28 10:22:36 +08:00
|
|
|
int count = 0;
|
2013-09-12 16:02:16 +08:00
|
|
|
for (auto& touch : touches)
|
|
|
|
{
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval jsret;
|
2013-10-11 09:52:35 +08:00
|
|
|
getJSTouchObject(this->_cx, touch, jsret);
|
|
|
|
if (!JS_SetElement(this->_cx, jsretArr, count, &jsret)) {
|
2012-08-28 10:22:36 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-09-12 16:02:16 +08:00
|
|
|
++count;
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval jsretArrVal = OBJECT_TO_JSVAL(jsretArr);
|
2013-03-14 18:14:16 +08:00
|
|
|
executeFunctionWithOwner(OBJECT_TO_JSVAL(obj), funcName.c_str(), 1, &jsretArrVal, &retval);
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_RemoveObjectRoot(this->_cx, &jsretArr);
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2013-09-12 16:02:16 +08:00
|
|
|
for (auto& touch : touches)
|
|
|
|
{
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval jsret;
|
2013-10-11 09:52:35 +08:00
|
|
|
removeJSTouchObject(this->_cx, touch, jsret);
|
2012-08-28 10:22:36 +08:00
|
|
|
}
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-20 20:09:39 +08:00
|
|
|
int ScriptingCore::executeCustomTouchEvent(EventTouch::EventCode eventType,
|
2013-09-12 16:02:16 +08:00
|
|
|
Touch *pTouch, JSObject *obj)
|
|
|
|
{
|
2013-10-31 15:13:49 +08:00
|
|
|
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
|
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval retval;
|
|
|
|
std::string funcName;
|
|
|
|
getTouchFuncName(eventType, funcName);
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval jsTouch;
|
2013-10-11 09:52:35 +08:00
|
|
|
getJSTouchObject(this->_cx, pTouch, jsTouch);
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2013-03-14 18:14:16 +08:00
|
|
|
executeFunctionWithOwner(OBJECT_TO_JSVAL(obj), funcName.c_str(), 1, &jsTouch, &retval);
|
fixed #1617: Some improvements for JS Bindings.
1) Changed cc.REPEAT_FOREVER = - 1 to cc.REPEAT_FOREVER = 0xffffffff
[Reason]: If cc.REPEAT_FOREVER = -1, it will be a very big double value after converting it to double by JS_ValueToNumber on android.
Then cast it to unsigned int, the value will be 0. The schedule will not be able to work.
I don't know why this occurs only on android.
[Solution]: Instead of passing -1 to it, I assign it with max value of unsigned int in c++.
2) Added two helper function, cc.ArrayGetIndexOfObject and cc.ArrayContainsObject.
3) Added JSScheduleWrapper::removeTargetForNativeNode to avoid memory leaks.
4) Improvments for JSTouchDelegate. Added four functions as follows:
// Set the touch delegate to map by using the key (pJSObj).
static void setDelegateForJSObject(JSObject* pJSObj, JSTouchDelegate* pDelegate);
// Get the touch delegate by the key (pJSObj).
static JSTouchDelegate* getDelegateForJSObject(JSObject* pJSObj);
// Remove the delegate by the key (pJSObj).
static void removeDelegateForJSObject(JSObject* pJSObj);
void unregisterTouchDelegate();
And exported cc.unregisterTouchDelegate(); to js.
Fix a memory leak for JSTouchDelegate by making it as an autorelease object.
5) Don't add js callback function to the reserved slot of object.
[Reason]: The target object may execute more than one schedule.
Therefore, previous js callback function will be replaced
by the current one. For example:
this.scheduleOnce(function() { temporary function 1 }, 0.5);
this.scheduleOnce(function() { temporary function 2 }, 0.5);
In this case, the temporary function 1 will be removed from reserved slot 0.
And temporary function 2 will be set to reserved slot 0 of this object.
If gc is triggered before the JSScheduleWrapper::scheduleFunc is invoked,
crash will happen. You could simply reproduce it by adding jsc.garbageCollect(); after scheduleOnce.
[Solution] Because one schedule corresponds to one JSScheduleWrapper, we root
the js callback function in JSScheduleWrapper::setJSCallbackFunc and unroot it
at the destructor of JSScheduleWrapper.
2012-12-18 11:56:44 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
// Remove touch object from global hash table and unroot it.
|
2013-10-11 09:52:35 +08:00
|
|
|
removeJSTouchObject(this->_cx, pTouch, jsTouch);
|
2012-08-28 10:22:36 +08:00
|
|
|
return 1;
|
2012-10-20 00:54:21 +08:00
|
|
|
|
|
|
|
}
|
2012-08-28 10:22:36 +08:00
|
|
|
|
|
|
|
|
2013-09-20 20:09:39 +08:00
|
|
|
int ScriptingCore::executeCustomTouchEvent(EventTouch::EventCode eventType,
|
2013-06-20 14:33:59 +08:00
|
|
|
Touch *pTouch, JSObject *obj,
|
2013-09-12 16:02:16 +08:00
|
|
|
jsval &retval)
|
|
|
|
{
|
2013-10-31 15:13:49 +08:00
|
|
|
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
|
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
std::string funcName;
|
|
|
|
getTouchFuncName(eventType, funcName);
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
jsval jsTouch;
|
2013-10-11 09:52:35 +08:00
|
|
|
getJSTouchObject(this->_cx, pTouch, jsTouch);
|
2012-08-28 10:22:36 +08:00
|
|
|
|
2013-03-14 18:14:16 +08:00
|
|
|
executeFunctionWithOwner(OBJECT_TO_JSVAL(obj), funcName.c_str(), 1, &jsTouch, &retval);
|
fixed #1617: Some improvements for JS Bindings.
1) Changed cc.REPEAT_FOREVER = - 1 to cc.REPEAT_FOREVER = 0xffffffff
[Reason]: If cc.REPEAT_FOREVER = -1, it will be a very big double value after converting it to double by JS_ValueToNumber on android.
Then cast it to unsigned int, the value will be 0. The schedule will not be able to work.
I don't know why this occurs only on android.
[Solution]: Instead of passing -1 to it, I assign it with max value of unsigned int in c++.
2) Added two helper function, cc.ArrayGetIndexOfObject and cc.ArrayContainsObject.
3) Added JSScheduleWrapper::removeTargetForNativeNode to avoid memory leaks.
4) Improvments for JSTouchDelegate. Added four functions as follows:
// Set the touch delegate to map by using the key (pJSObj).
static void setDelegateForJSObject(JSObject* pJSObj, JSTouchDelegate* pDelegate);
// Get the touch delegate by the key (pJSObj).
static JSTouchDelegate* getDelegateForJSObject(JSObject* pJSObj);
// Remove the delegate by the key (pJSObj).
static void removeDelegateForJSObject(JSObject* pJSObj);
void unregisterTouchDelegate();
And exported cc.unregisterTouchDelegate(); to js.
Fix a memory leak for JSTouchDelegate by making it as an autorelease object.
5) Don't add js callback function to the reserved slot of object.
[Reason]: The target object may execute more than one schedule.
Therefore, previous js callback function will be replaced
by the current one. For example:
this.scheduleOnce(function() { temporary function 1 }, 0.5);
this.scheduleOnce(function() { temporary function 2 }, 0.5);
In this case, the temporary function 1 will be removed from reserved slot 0.
And temporary function 2 will be set to reserved slot 0 of this object.
If gc is triggered before the JSScheduleWrapper::scheduleFunc is invoked,
crash will happen. You could simply reproduce it by adding jsc.garbageCollect(); after scheduleOnce.
[Solution] Because one schedule corresponds to one JSScheduleWrapper, we root
the js callback function in JSScheduleWrapper::setJSCallbackFunc and unroot it
at the destructor of JSScheduleWrapper.
2012-12-18 11:56:44 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
// Remove touch object from global hash table and unroot it.
|
2013-10-11 09:52:35 +08:00
|
|
|
removeJSTouchObject(this->_cx, pTouch, jsTouch);
|
fixed #1617: Some improvements for JS Bindings.
1) Changed cc.REPEAT_FOREVER = - 1 to cc.REPEAT_FOREVER = 0xffffffff
[Reason]: If cc.REPEAT_FOREVER = -1, it will be a very big double value after converting it to double by JS_ValueToNumber on android.
Then cast it to unsigned int, the value will be 0. The schedule will not be able to work.
I don't know why this occurs only on android.
[Solution]: Instead of passing -1 to it, I assign it with max value of unsigned int in c++.
2) Added two helper function, cc.ArrayGetIndexOfObject and cc.ArrayContainsObject.
3) Added JSScheduleWrapper::removeTargetForNativeNode to avoid memory leaks.
4) Improvments for JSTouchDelegate. Added four functions as follows:
// Set the touch delegate to map by using the key (pJSObj).
static void setDelegateForJSObject(JSObject* pJSObj, JSTouchDelegate* pDelegate);
// Get the touch delegate by the key (pJSObj).
static JSTouchDelegate* getDelegateForJSObject(JSObject* pJSObj);
// Remove the delegate by the key (pJSObj).
static void removeDelegateForJSObject(JSObject* pJSObj);
void unregisterTouchDelegate();
And exported cc.unregisterTouchDelegate(); to js.
Fix a memory leak for JSTouchDelegate by making it as an autorelease object.
5) Don't add js callback function to the reserved slot of object.
[Reason]: The target object may execute more than one schedule.
Therefore, previous js callback function will be replaced
by the current one. For example:
this.scheduleOnce(function() { temporary function 1 }, 0.5);
this.scheduleOnce(function() { temporary function 2 }, 0.5);
In this case, the temporary function 1 will be removed from reserved slot 0.
And temporary function 2 will be set to reserved slot 0 of this object.
If gc is triggered before the JSScheduleWrapper::scheduleFunc is invoked,
crash will happen. You could simply reproduce it by adding jsc.garbageCollect(); after scheduleOnce.
[Solution] Because one schedule corresponds to one JSScheduleWrapper, we root
the js callback function in JSScheduleWrapper::setJSCallbackFunc and unroot it
at the destructor of JSScheduleWrapper.
2012-12-18 11:56:44 +08:00
|
|
|
|
2012-08-28 10:22:36 +08:00
|
|
|
return 1;
|
2012-10-20 00:54:21 +08:00
|
|
|
|
2012-10-19 08:44:41 +08:00
|
|
|
}
|
|
|
|
|
2013-07-22 17:12:53 +08:00
|
|
|
int ScriptingCore::sendEvent(ScriptEvent* evt)
|
|
|
|
{
|
|
|
|
if (NULL == evt)
|
|
|
|
return 0;
|
2013-10-07 10:50:12 +08:00
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
JSAutoCompartment ac(_cx, _global);
|
2013-07-22 17:12:53 +08:00
|
|
|
|
|
|
|
switch (evt->type)
|
|
|
|
{
|
|
|
|
case kNodeEvent:
|
|
|
|
{
|
|
|
|
return handleNodeEvent(evt->data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kMenuClickedEvent:
|
|
|
|
{
|
|
|
|
return handleMenuClickedEvent(evt->data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kTouchEvent:
|
|
|
|
{
|
|
|
|
return handleTouchEvent(evt->data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kTouchesEvent:
|
|
|
|
{
|
|
|
|
return handleTouchesEvent(evt->data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kKeypadEvent:
|
|
|
|
{
|
|
|
|
return handleKeypadEvent(evt->data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kAccelerometerEvent:
|
|
|
|
{
|
|
|
|
return handleAccelerometerEvent(evt->data);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-16 03:14:57 +08:00
|
|
|
#pragma mark - Debug
|
|
|
|
|
2013-10-09 13:55:23 +08:00
|
|
|
void SimpleRunLoop::update(float dt)
|
|
|
|
{
|
|
|
|
g_qMutex.lock();
|
|
|
|
size_t size = g_queue.size();
|
|
|
|
g_qMutex.unlock();
|
|
|
|
|
|
|
|
while (size > 0)
|
|
|
|
{
|
|
|
|
g_qMutex.lock();
|
2013-10-31 11:07:33 +08:00
|
|
|
auto first = g_queue.begin();
|
|
|
|
std::string str = *first;
|
2013-06-21 22:29:48 +08:00
|
|
|
g_queue.erase(first);
|
2013-10-09 13:55:23 +08:00
|
|
|
size = g_queue.size();
|
|
|
|
g_qMutex.unlock();
|
|
|
|
|
|
|
|
ScriptingCore::getInstance()->debugProcessInput(str);
|
2013-02-22 00:34:51 +08:00
|
|
|
}
|
2013-01-17 06:02:05 +08:00
|
|
|
}
|
|
|
|
|
2013-10-31 11:07:33 +08:00
|
|
|
void ScriptingCore::debugProcessInput(const std::string& str)
|
2013-10-11 21:52:55 +08:00
|
|
|
{
|
2013-10-11 09:52:35 +08:00
|
|
|
JSAutoCompartment ac(_cx, _debugGlobal);
|
2013-10-07 10:50:12 +08:00
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
JSString* jsstr = JS_NewStringCopyZ(_cx, str.c_str());
|
2013-10-11 21:52:55 +08:00
|
|
|
jsval argv = STRING_TO_JSVAL(jsstr);
|
2013-02-22 00:34:51 +08:00
|
|
|
jsval outval;
|
2013-10-07 10:50:12 +08:00
|
|
|
|
2013-10-11 21:52:55 +08:00
|
|
|
JS_CallFunctionName(_cx, _debugGlobal, "processInput", 1, &argv, &outval);
|
2013-01-12 06:01:36 +08:00
|
|
|
}
|
|
|
|
|
2013-10-09 13:55:23 +08:00
|
|
|
static bool NS_ProcessNextEvent()
|
|
|
|
{
|
|
|
|
g_qMutex.lock();
|
|
|
|
size_t size = g_queue.size();
|
|
|
|
g_qMutex.unlock();
|
|
|
|
|
|
|
|
while (size > 0)
|
|
|
|
{
|
|
|
|
g_qMutex.lock();
|
2013-10-31 11:07:33 +08:00
|
|
|
auto first = g_queue.begin();
|
|
|
|
std::string str = *first;
|
2013-10-09 13:55:23 +08:00
|
|
|
g_queue.erase(first);
|
|
|
|
size = g_queue.size();
|
|
|
|
g_qMutex.unlock();
|
|
|
|
|
|
|
|
ScriptingCore::getInstance()->debugProcessInput(str);
|
|
|
|
}
|
|
|
|
// std::this_thread::yield();
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool JSBDebug_enterNestedEventLoop(JSContext* cx, unsigned argc, jsval* vp)
|
|
|
|
{
|
|
|
|
enum {
|
|
|
|
NS_OK = 0,
|
|
|
|
NS_ERROR_UNEXPECTED
|
|
|
|
};
|
|
|
|
|
|
|
|
#define NS_SUCCEEDED(v) ((v) == NS_OK)
|
|
|
|
|
|
|
|
int rv = NS_OK;
|
|
|
|
|
|
|
|
uint32_t nestLevel = ++s_nestedLoopLevel;
|
|
|
|
|
|
|
|
while (NS_SUCCEEDED(rv) && s_nestedLoopLevel >= nestLevel) {
|
|
|
|
if (!NS_ProcessNextEvent())
|
|
|
|
rv = NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCASSERT(s_nestedLoopLevel <= nestLevel,
|
|
|
|
"nested event didn't unwind properly");
|
|
|
|
|
|
|
|
JS_SET_RVAL(cx, vp, UINT_TO_JSVAL(s_nestedLoopLevel));
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool JSBDebug_exitNestedEventLoop(JSContext* cx, unsigned argc, jsval* vp)
|
|
|
|
{
|
|
|
|
if (s_nestedLoopLevel > 0) {
|
|
|
|
--s_nestedLoopLevel;
|
|
|
|
} else {
|
|
|
|
JS_SET_RVAL(cx, vp, UINT_TO_JSVAL(0));
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JS_SET_RVAL(cx, vp, UINT_TO_JSVAL(s_nestedLoopLevel));
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool JSBDebug_getEventLoopNestLevel(JSContext* cx, unsigned argc, jsval* vp)
|
|
|
|
{
|
|
|
|
JS_SET_RVAL(cx, vp, UINT_TO_JSVAL(s_nestedLoopLevel));
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2013-10-10 17:17:48 +08:00
|
|
|
//#pragma mark - Debugger
|
|
|
|
|
|
|
|
static void _clientSocketWriteAndClearString(std::string& s)
|
|
|
|
{
|
|
|
|
::send(clientSocket, s.c_str(), s.length(), 0);
|
|
|
|
s.clear();
|
|
|
|
}
|
|
|
|
|
2013-10-31 11:07:33 +08:00
|
|
|
static void processInput(const std::string& data) {
|
2013-10-10 17:17:48 +08:00
|
|
|
std::lock_guard<std::mutex> lk(g_qMutex);
|
|
|
|
g_queue.push_back(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clearBuffers() {
|
|
|
|
std::lock_guard<std::mutex> lk(g_rwMutex);
|
|
|
|
// only process input if there's something and we're not locked
|
|
|
|
if (inData.length() > 0) {
|
|
|
|
processInput(inData);
|
|
|
|
inData.clear();
|
|
|
|
}
|
|
|
|
if (outData.length() > 0) {
|
|
|
|
_clientSocketWriteAndClearString(outData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void serverEntryPoint(void)
|
|
|
|
{
|
|
|
|
// start a server, accept the connection and keep reading data from it
|
|
|
|
struct addrinfo hints, *result = nullptr, *rp = nullptr;
|
|
|
|
int s = 0;
|
|
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
|
|
|
hints.ai_family = AF_INET; // IPv4
|
|
|
|
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
|
|
|
|
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
|
|
|
|
|
2013-10-31 11:07:33 +08:00
|
|
|
std::stringstream portstr;
|
2013-10-10 17:17:48 +08:00
|
|
|
portstr << JSB_DEBUGGER_PORT;
|
|
|
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
|
|
|
WSADATA wsaData;
|
|
|
|
err = WSAStartup(MAKEWORD(2, 2),&wsaData);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ((err = getaddrinfo(NULL, portstr.str().c_str(), &hints, &result)) != 0) {
|
|
|
|
LOGD("getaddrinfo error : %s\n", gai_strerror(err));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
|
|
|
if ((s = socket(rp->ai_family, rp->ai_socktype, 0)) < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int optval = 1;
|
|
|
|
if ((setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof(optval))) < 0) {
|
|
|
|
close(s);
|
|
|
|
TRACE_DEBUGGER_SERVER("debug server : error setting socket option SO_REUSEADDR");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
|
|
|
if ((setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval))) < 0) {
|
|
|
|
close(s);
|
|
|
|
TRACE_DEBUGGER_SERVER("debug server : error setting socket option SO_NOSIGPIPE");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif //(CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
|
|
|
|
|
|
|
if ((::bind(s, rp->ai_addr, rp->ai_addrlen)) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
close(s);
|
|
|
|
s = -1;
|
|
|
|
}
|
|
|
|
if (s < 0 || rp == NULL) {
|
|
|
|
TRACE_DEBUGGER_SERVER("debug server : error creating/binding socket");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(result);
|
|
|
|
|
|
|
|
listen(s, 1);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
clientSocket = accept(s, NULL, NULL);
|
|
|
|
|
|
|
|
if (clientSocket < 0)
|
|
|
|
{
|
|
|
|
TRACE_DEBUGGER_SERVER("debug server : error on accept");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// read/write data
|
|
|
|
TRACE_DEBUGGER_SERVER("debug server : client connected");
|
|
|
|
|
|
|
|
inData = "connected";
|
|
|
|
// process any input, send any output
|
|
|
|
clearBuffers();
|
|
|
|
|
|
|
|
char buf[1024] = {0};
|
|
|
|
int readBytes = 0;
|
|
|
|
while ((readBytes = ::recv(clientSocket, buf, sizeof(buf), 0)) > 0)
|
|
|
|
{
|
|
|
|
buf[readBytes] = '\0';
|
|
|
|
// TRACE_DEBUGGER_SERVER("debug server : received command >%s", buf);
|
|
|
|
|
|
|
|
// no other thread is using this
|
|
|
|
inData.append(buf);
|
|
|
|
// process any input, send any output
|
|
|
|
clearBuffers();
|
|
|
|
} // while(read)
|
|
|
|
|
|
|
|
close(clientSocket);
|
|
|
|
}
|
|
|
|
} // while(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
JSBool JSBDebug_BufferWrite(JSContext* cx, unsigned argc, jsval* vp)
|
|
|
|
{
|
|
|
|
if (argc == 1) {
|
|
|
|
jsval* argv = JS_ARGV(cx, vp);
|
|
|
|
JSStringWrapper strWrapper(argv[0]);
|
|
|
|
// this is safe because we're already inside a lock (from clearBuffers)
|
|
|
|
outData.append(strWrapper.get());
|
|
|
|
_clientSocketWriteAndClearString(outData);
|
|
|
|
}
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2013-10-10 16:06:05 +08:00
|
|
|
void ScriptingCore::enableDebugger()
|
|
|
|
{
|
2013-10-11 09:52:35 +08:00
|
|
|
if (_debugGlobal == NULL)
|
2013-10-10 16:06:05 +08:00
|
|
|
{
|
2013-10-11 09:52:35 +08:00
|
|
|
JSAutoCompartment ac0(_cx, _global);
|
2013-10-30 21:56:07 +08:00
|
|
|
|
|
|
|
JS_SetDebugMode(_cx, JS_TRUE);
|
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
_debugGlobal = NewGlobalObject(_cx, true);
|
|
|
|
JS_WrapObject(_cx, &_debugGlobal);
|
|
|
|
JSAutoCompartment ac(_cx, _debugGlobal);
|
2013-02-22 00:34:51 +08:00
|
|
|
// these are used in the debug program
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_DefineFunction(_cx, _debugGlobal, "log", ScriptingCore::log, 0, JSPROP_READONLY | JSPROP_PERMANENT);
|
|
|
|
JS_DefineFunction(_cx, _debugGlobal, "_bufferWrite", JSBDebug_BufferWrite, 1, JSPROP_READONLY | JSPROP_PERMANENT);
|
|
|
|
JS_DefineFunction(_cx, _debugGlobal, "_enterNestedEventLoop", JSBDebug_enterNestedEventLoop, 0, JSPROP_READONLY | JSPROP_PERMANENT);
|
|
|
|
JS_DefineFunction(_cx, _debugGlobal, "_exitNestedEventLoop", JSBDebug_exitNestedEventLoop, 0, JSPROP_READONLY | JSPROP_PERMANENT);
|
|
|
|
JS_DefineFunction(_cx, _debugGlobal, "_getEventLoopNestLevel", JSBDebug_getEventLoopNestLevel, 0, JSPROP_READONLY | JSPROP_PERMANENT);
|
2013-10-07 17:19:00 +08:00
|
|
|
|
|
|
|
|
2013-10-11 09:52:35 +08:00
|
|
|
runScript("jsb_debugger.js", _debugGlobal);
|
2013-10-05 10:56:52 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
// prepare the debugger
|
2013-10-11 09:52:35 +08:00
|
|
|
jsval argv = OBJECT_TO_JSVAL(_global);
|
2013-02-22 00:34:51 +08:00
|
|
|
jsval outval;
|
2013-10-11 09:52:35 +08:00
|
|
|
JSBool ok = JS_CallFunctionName(_cx, _debugGlobal, "_prepareDebugger", 1, &argv, &outval);
|
2013-02-22 00:34:51 +08:00
|
|
|
if (!ok) {
|
2013-10-11 09:52:35 +08:00
|
|
|
JS_ReportPendingException(_cx);
|
2013-02-22 00:34:51 +08:00
|
|
|
}
|
2013-10-07 10:50:12 +08:00
|
|
|
|
2013-02-22 00:34:51 +08:00
|
|
|
// start bg thread
|
2013-06-26 14:41:19 +08:00
|
|
|
auto t = std::thread(&serverEntryPoint);
|
|
|
|
t.detach();
|
2013-02-22 00:34:51 +08:00
|
|
|
|
2013-07-12 06:24:23 +08:00
|
|
|
Scheduler* scheduler = Director::getInstance()->getScheduler();
|
2013-10-11 09:52:35 +08:00
|
|
|
scheduler->scheduleUpdateForTarget(this->_runLoop, 0, false);
|
2013-02-22 00:34:51 +08:00
|
|
|
}
|
2012-11-16 03:14:57 +08:00
|
|
|
}
|
2012-10-19 08:44:41 +08:00
|
|
|
|
2012-11-16 03:14:57 +08:00
|
|
|
JSObject* NewGlobalObject(JSContext* cx, bool debug)
|
2012-10-19 08:44:41 +08:00
|
|
|
{
|
2013-10-30 21:56:07 +08:00
|
|
|
JS::CompartmentOptions options;
|
|
|
|
options.setVersion(JSVERSION_LATEST);
|
|
|
|
|
|
|
|
JS::RootedObject glob(cx, JS_NewGlobalObject(cx, &global_class, NULL, JS::DontFireOnNewGlobalHook, options));
|
2013-10-10 15:45:24 +08:00
|
|
|
if (!glob) {
|
2013-02-22 00:34:51 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
JSAutoCompartment ac(cx, glob);
|
|
|
|
JSBool ok = JS_TRUE;
|
|
|
|
ok = JS_InitStandardClasses(cx, glob);
|
|
|
|
if (ok)
|
2013-10-10 15:45:24 +08:00
|
|
|
JS_InitReflect(cx, glob);
|
2013-02-22 00:34:51 +08:00
|
|
|
if (ok && debug)
|
|
|
|
ok = JS_DefineDebuggerObject(cx, glob);
|
|
|
|
if (!ok)
|
|
|
|
return NULL;
|
2012-10-19 08:44:41 +08:00
|
|
|
|
2013-10-30 21:56:07 +08:00
|
|
|
JS_FireOnNewGlobalObject(cx, glob);
|
|
|
|
|
2012-10-19 11:15:23 +08:00
|
|
|
return glob;
|
2012-10-19 08:44:41 +08:00
|
|
|
}
|
|
|
|
|
2013-01-12 06:01:36 +08:00
|
|
|
JSBool jsb_set_reserved_slot(JSObject *obj, uint32_t idx, jsval value)
|
2012-10-19 08:44:41 +08:00
|
|
|
{
|
2013-01-12 06:01:36 +08:00
|
|
|
JSClass *klass = JS_GetClass(obj);
|
|
|
|
unsigned int slots = JSCLASS_RESERVED_SLOTS(klass);
|
2013-08-09 14:49:39 +08:00
|
|
|
if ( idx >= slots )
|
2013-01-12 06:01:36 +08:00
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
JS_SetReservedSlot(obj, idx, value);
|
|
|
|
|
2012-10-19 11:15:23 +08:00
|
|
|
return JS_TRUE;
|
2012-10-19 08:44:41 +08:00
|
|
|
}
|
|
|
|
|
2013-01-12 06:01:36 +08:00
|
|
|
JSBool jsb_get_reserved_slot(JSObject *obj, uint32_t idx, jsval& ret)
|
2012-10-19 08:44:41 +08:00
|
|
|
{
|
2013-01-12 06:01:36 +08:00
|
|
|
JSClass *klass = JS_GetClass(obj);
|
|
|
|
unsigned int slots = JSCLASS_RESERVED_SLOTS(klass);
|
2013-08-09 14:49:39 +08:00
|
|
|
if ( idx >= slots )
|
2013-01-12 06:01:36 +08:00
|
|
|
return JS_FALSE;
|
|
|
|
|
|
|
|
ret = JS_GetReservedSlot(obj, idx);
|
|
|
|
|
2012-10-19 11:15:23 +08:00
|
|
|
return JS_TRUE;
|
2012-10-19 08:44:41 +08:00
|
|
|
}
|
|
|
|
|
2013-06-03 22:31:24 +08:00
|
|
|
js_proxy_t* jsb_new_proxy(void* nativeObj, JSObject* jsObj)
|
|
|
|
{
|
2013-12-06 16:46:19 +08:00
|
|
|
js_proxy_t* p = nullptr;
|
2013-06-03 22:31:24 +08:00
|
|
|
JS_NEW_PROXY(p, nativeObj, jsObj);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
js_proxy_t* jsb_get_native_proxy(void* nativeObj)
|
|
|
|
{
|
2013-12-06 16:46:19 +08:00
|
|
|
js_proxy_t* p = nullptr;
|
2013-06-03 22:31:24 +08:00
|
|
|
JS_GET_PROXY(p, nativeObj);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
js_proxy_t* jsb_get_js_proxy(JSObject* jsObj)
|
|
|
|
{
|
2013-12-06 16:46:19 +08:00
|
|
|
js_proxy_t* p = nullptr;
|
2013-06-03 22:31:24 +08:00
|
|
|
JS_GET_NATIVE_PROXY(p, jsObj);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void jsb_remove_proxy(js_proxy_t* nativeProxy, js_proxy_t* jsProxy)
|
|
|
|
{
|
|
|
|
JS_REMOVE_PROXY(nativeProxy, jsProxy);
|
|
|
|
}
|
|
|
|
|
2013-11-10 11:23:34 +08:00
|
|
|
|