mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2026-01-10 09:56:54 +00:00
初始化
This commit is contained in:
38
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Base.h
Normal file
38
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Base.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <JavaScriptCore/JavaScript.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "HelperMacros.h"
|
||||
282
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Class.cpp
Normal file
282
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Class.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "Class.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
#include "Object.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
#include "State.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
#define JS_FN(name, func, attr) {name, func, attr}
|
||||
#define JS_FS_END JS_FN(0, 0, 0)
|
||||
#define JS_PSGS(name, getter, setter, attr) {name, getter, setter, attr}
|
||||
#define JS_PS_END JS_PSGS(0, 0, 0, 0)
|
||||
|
||||
namespace {
|
||||
// std::unordered_map<std::string, Class *> __clsMap;
|
||||
JSContextRef __cx = nullptr;
|
||||
std::vector<Class*> __allClasses;
|
||||
|
||||
void defaultFinalizeCallback(JSObjectRef _obj)
|
||||
{
|
||||
void* nativeThisObject = JSObjectGetPrivate(_obj);
|
||||
if (nativeThisObject != nullptr)
|
||||
{
|
||||
State state(nativeThisObject);
|
||||
Object* _thisObject = state.thisObject();
|
||||
if (_thisObject) _thisObject->_cleanup(nativeThisObject);
|
||||
JSObjectSetPrivate(_obj, nullptr);
|
||||
SAFE_DEC_REF(_thisObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Class::Class()
|
||||
: _parent(nullptr)
|
||||
, _proto(nullptr)
|
||||
, _parentProto(nullptr)
|
||||
, _ctor(nullptr)
|
||||
, _jsCls(nullptr)
|
||||
, _finalizeOp(nullptr)
|
||||
{
|
||||
_jsClsDef = kJSClassDefinitionEmpty;
|
||||
__allClasses.push_back(this);
|
||||
}
|
||||
|
||||
Class::~Class()
|
||||
{
|
||||
}
|
||||
|
||||
Class* Class::create(const std::string& className, Object* obj, Object* parentProto, JSObjectCallAsConstructorCallback ctor)
|
||||
{
|
||||
Class* cls = new Class();
|
||||
if (cls != nullptr && !cls->init(className, obj, parentProto, ctor))
|
||||
{
|
||||
delete cls;
|
||||
cls = nullptr;
|
||||
}
|
||||
return cls;
|
||||
}
|
||||
|
||||
bool Class::init(const std::string &clsName, Object* parent, Object *parentProto, JSObjectCallAsConstructorCallback ctor)
|
||||
{
|
||||
_name = clsName;
|
||||
_parent = parent;
|
||||
SAFE_INC_REF(_parent);
|
||||
_parentProto = parentProto;
|
||||
SAFE_INC_REF(_parentProto);
|
||||
_ctor = ctor;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::install()
|
||||
{
|
||||
// assert(__clsMap.find(_name) == __clsMap.end());
|
||||
//
|
||||
// __clsMap.emplace(_name, this);
|
||||
|
||||
_jsClsDef.version = 0;
|
||||
_jsClsDef.attributes = kJSClassAttributeNone;
|
||||
_jsClsDef.className = _name.c_str();
|
||||
if (_parentProto != nullptr)
|
||||
{
|
||||
_jsClsDef.parentClass = _parentProto->_getClass()->_jsCls;
|
||||
}
|
||||
|
||||
_funcs.push_back(JS_FS_END);
|
||||
// _properties.push_back(JS_PS_END);
|
||||
|
||||
// _jsClsDef.staticValues = _properties.data();
|
||||
_jsClsDef.staticFunctions = _funcs.data();
|
||||
|
||||
// _jsClsDef.getProperty = _getPropertyCallback;
|
||||
// _jsClsDef.setProperty = _setPropertyCallback;
|
||||
// _jsClsDef.hasProperty = _hasPropertyCallback;
|
||||
|
||||
if (_finalizeOp != nullptr)
|
||||
_jsClsDef.finalize = _finalizeOp;
|
||||
else
|
||||
_jsClsDef.finalize = defaultFinalizeCallback;
|
||||
|
||||
_jsCls = JSClassCreate(&_jsClsDef);
|
||||
|
||||
JSObjectRef jsCtor = JSObjectMakeConstructor(__cx, _jsCls, _ctor);
|
||||
HandleObject ctorObj(Object::_createJSObject(nullptr, jsCtor));
|
||||
|
||||
Value functionCtor;
|
||||
ScriptEngine::getInstance()->getGlobalObject()->getProperty("Function", &functionCtor);
|
||||
ctorObj->setProperty("constructor", functionCtor);
|
||||
|
||||
JSValueRef exception = nullptr;
|
||||
for (const auto& staticfunc : _staticFuncs)
|
||||
{
|
||||
JSStringRef name = JSStringCreateWithUTF8CString(staticfunc.name);
|
||||
JSObjectRef func = JSObjectMakeFunctionWithCallback(__cx, nullptr, staticfunc.callAsFunction);
|
||||
|
||||
exception = nullptr;
|
||||
JSObjectSetProperty(__cx, jsCtor, name, func, kJSPropertyAttributeNone, &exception);
|
||||
if (exception != nullptr)
|
||||
{
|
||||
ScriptEngine::getInstance()->_clearException(exception);
|
||||
}
|
||||
JSStringRelease(name);
|
||||
}
|
||||
|
||||
JSValueRef prototypeObj = nullptr;
|
||||
JSStringRef prototypeName = JSStringCreateWithUTF8CString("prototype");
|
||||
bool exist = JSObjectHasProperty(__cx, jsCtor, prototypeName);
|
||||
if (exist)
|
||||
{
|
||||
exception = nullptr;
|
||||
prototypeObj = JSObjectGetProperty(__cx, jsCtor, prototypeName, &exception);
|
||||
if (exception != nullptr)
|
||||
{
|
||||
ScriptEngine::getInstance()->_clearException(exception);
|
||||
}
|
||||
}
|
||||
JSStringRelease(prototypeName);
|
||||
assert(prototypeObj != nullptr);
|
||||
|
||||
exception = nullptr;
|
||||
JSObjectRef protoJSObj = JSValueToObject(__cx, prototypeObj, &exception);
|
||||
if (exception != nullptr)
|
||||
{
|
||||
ScriptEngine::getInstance()->_clearException(exception);
|
||||
}
|
||||
|
||||
//NOTE: I's weird that proto object has a private data which is an invalid adress.
|
||||
// We have to reset its private data to the max value of unsigned long.
|
||||
// Therefore, in SE_BIND_FUNC of HelperMacro.h, we could distinguish whether it's a
|
||||
// proto object. Don't set it to nullptr since static method will get private data
|
||||
// with nullptr. This line is needed by Web Inspector because it needs to extend
|
||||
// all global JS values including global proto object. However, proto objects will
|
||||
// not have a private data which will cause crash while debugging in Safari since
|
||||
// se::State::nativeThisObject() will return nullptr.
|
||||
JSObjectSetPrivate(protoJSObj, (void*)std::numeric_limits<unsigned long>::max());
|
||||
|
||||
_proto = Object::_createJSObject(this, protoJSObj);
|
||||
_proto->root();
|
||||
|
||||
// reset constructor
|
||||
_proto->setProperty("constructor", Value(ctorObj));
|
||||
|
||||
// Set instance properties
|
||||
for (const auto& property : _properties)
|
||||
{
|
||||
internal::defineProperty(_proto, property.name, property.getter, property.setter);
|
||||
}
|
||||
|
||||
// Set class properties
|
||||
for (const auto& property : _staticProperties)
|
||||
{
|
||||
internal::defineProperty(ctorObj.get(), property.name, property.getter, property.setter);
|
||||
}
|
||||
|
||||
_parent->setProperty(_name.c_str(), Value(ctorObj));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineFunction(const char *name, JSObjectCallAsFunctionCallback func)
|
||||
{
|
||||
JSStaticFunction cb = JS_FN(name, func, kJSPropertyAttributeNone);
|
||||
_funcs.push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineProperty(const char *name, JSObjectCallAsFunctionCallback getter, JSObjectCallAsFunctionCallback setter)
|
||||
{
|
||||
JSPropertySpec property = JS_PSGS(name, getter, setter, kJSPropertyAttributeNone);
|
||||
_properties.push_back(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticFunction(const char *name, JSObjectCallAsFunctionCallback func)
|
||||
{
|
||||
JSStaticFunction cb = JS_FN(name, func, kJSPropertyAttributeNone);
|
||||
_staticFuncs.push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticProperty(const char *name, JSObjectCallAsFunctionCallback getter, JSObjectCallAsFunctionCallback setter)
|
||||
{
|
||||
JSPropertySpec property = JS_PSGS(name, getter, setter, kJSPropertyAttributeNone);
|
||||
_staticProperties.push_back(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineFinalizeFunction(JSObjectFinalizeCallback func)
|
||||
{
|
||||
_finalizeOp = func;
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObjectRef Class::_createJSObjectWithClass(Class* cls)
|
||||
{
|
||||
return JSObjectMake(__cx, cls->_jsCls, nullptr);
|
||||
}
|
||||
|
||||
void Class::setContext(JSContextRef cx)
|
||||
{
|
||||
__cx = cx;
|
||||
}
|
||||
|
||||
Object *Class::getProto()
|
||||
{
|
||||
return _proto;
|
||||
}
|
||||
|
||||
void Class::destroy()
|
||||
{
|
||||
SAFE_DEC_REF(_parent);
|
||||
SAFE_DEC_REF(_proto);
|
||||
SAFE_DEC_REF(_parentProto);
|
||||
|
||||
JSClassRelease(_jsCls);
|
||||
}
|
||||
|
||||
void Class::cleanup()
|
||||
{
|
||||
for (auto cls : __allClasses)
|
||||
{
|
||||
cls->destroy();
|
||||
}
|
||||
|
||||
ScriptEngine::getInstance()->addAfterCleanupHook([](){
|
||||
for (auto cls : __allClasses)
|
||||
{
|
||||
delete cls;
|
||||
}
|
||||
__allClasses.clear();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
158
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Class.hpp
Normal file
158
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Class.hpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* se::Class represents a definition of how to create a native binding object.
|
||||
*/
|
||||
class Class final
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a class used for creating relevant native binding objects.
|
||||
* @param[in] className A null-terminated UTF8 string containing the class's name.
|
||||
* @param[in] obj The object that current class proto object attaches to. Should not be nullptr.
|
||||
* @param[in] parentProto The parent proto object that current class inherits from. Passing nullptr means a new class has no parent.
|
||||
* @param[in] ctor A callback to invoke when your constructor is used in a 'new' expression. Pass nullptr to use the default object constructor.
|
||||
* @return A class instance used for creating relevant native binding objects.
|
||||
* @note Don't need to delete the pointer return by this method, it's managed internally.
|
||||
*/
|
||||
static Class* create(const std::string& className, Object* obj, Object* parentProto, JSObjectCallAsConstructorCallback ctor);
|
||||
|
||||
/**
|
||||
* @brief Defines a member function with a callback. Each objects created by class will have this function property.
|
||||
* @param[in] name A null-terminated UTF8 string containing the function name.
|
||||
* @param[in] func A callback to invoke when the property is called as a function.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineFunction(const char *name, JSObjectCallAsFunctionCallback func);
|
||||
|
||||
/**
|
||||
* @brief Defines a property with accessor callbacks. Each objects created by class will have this property.
|
||||
* @param[in] name A null-terminated UTF8 string containing the property name.
|
||||
* @param[in] getter A callback to invoke when the property is read.
|
||||
* @param[in] setter A callback to invoke when the property is set.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineProperty(const char *name, JSObjectCallAsFunctionCallback getter, JSObjectCallAsFunctionCallback setter);
|
||||
|
||||
/**
|
||||
* @brief Defines a static function with a callback. Only JavaScript constructor object will have this function.
|
||||
* @param[in] name A null-terminated UTF8 string containing the function name.
|
||||
* @param[in] func A callback to invoke when the constructor's property is called as a function.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineStaticFunction(const char *name, JSObjectCallAsFunctionCallback func);
|
||||
|
||||
/**
|
||||
* @brief Defines a static property with accessor callbacks. Only JavaScript constructor object will have this property.
|
||||
* @param[in] name A null-terminated UTF8 string containing the property name.
|
||||
* @param[in] getter A callback to invoke when the constructor's property is read.
|
||||
* @param[in] setter A callback to invoke when the constructor's property is set.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineStaticProperty(const char *name, JSObjectCallAsFunctionCallback getter, JSObjectCallAsFunctionCallback setter);
|
||||
|
||||
/**
|
||||
* @brief Defines the finalize function with a callback.
|
||||
* @param[in] func The callback to invoke when a JavaScript object is garbage collected.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineFinalizeFunction(JSObjectFinalizeCallback func);
|
||||
|
||||
/**
|
||||
* @brief Installs class to JavaScript VM.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note After this method, an object could be created by `var foo = new Foo();`.
|
||||
*/
|
||||
bool install();
|
||||
|
||||
/**
|
||||
* @brief Gets the proto object of this class.
|
||||
* @return The proto object of this class.
|
||||
* @note Don't need to be released in user code.
|
||||
*/
|
||||
Object* getProto();
|
||||
|
||||
/**
|
||||
* @brief Gets the class name.
|
||||
* @return The class name.
|
||||
*/
|
||||
const char* getName() const { return _name.c_str(); }
|
||||
|
||||
private:
|
||||
Class();
|
||||
~Class();
|
||||
|
||||
bool init(const std::string& clsName, Object* obj, Object* parentProto, JSObjectCallAsConstructorCallback ctor);
|
||||
void destroy();
|
||||
|
||||
static JSObjectRef _createJSObjectWithClass(Class* cls);
|
||||
|
||||
static void setContext(JSContextRef cx);
|
||||
static void cleanup();
|
||||
|
||||
struct JSPropertySpec
|
||||
{
|
||||
const char* name;
|
||||
JSObjectCallAsFunctionCallback getter;
|
||||
JSObjectCallAsFunctionCallback setter;
|
||||
JSPropertyAttributes attributes;
|
||||
};
|
||||
|
||||
std::string _name;
|
||||
Object* _parent;
|
||||
Object* _proto;
|
||||
Object* _parentProto;
|
||||
|
||||
JSObjectCallAsConstructorCallback _ctor;
|
||||
|
||||
JSClassRef _jsCls;
|
||||
JSClassDefinition _jsClsDef;
|
||||
|
||||
std::vector<JSStaticFunction> _funcs;
|
||||
std::vector<JSStaticFunction> _staticFuncs;
|
||||
std::vector<JSPropertySpec> _properties;
|
||||
std::vector<JSPropertySpec> _staticProperties;
|
||||
JSObjectFinalizeCallback _finalizeOp;
|
||||
|
||||
friend class ScriptEngine;
|
||||
friend class Object;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <JavaScriptCore/JavaScriptCore.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
NSString *JSValueToNSString( JSContextRef ctx, JSValueRef v );
|
||||
JSValueRef NSStringToJSValue( JSContextRef ctx, NSString *string );
|
||||
double JSValueToNumberFast( JSContextRef ctx, JSValueRef v );
|
||||
void JSValueUnprotectSafe( JSContextRef ctx, JSValueRef v );
|
||||
JSValueRef NSObjectToJSValue( JSContextRef ctx, NSObject *obj );
|
||||
NSObject *JSValueToNSObject( JSContextRef ctx, JSValueRef value );
|
||||
|
||||
static inline void *JSValueGetPrivate(JSValueRef v) {
|
||||
// On 64bit systems we can not safely call JSObjectGetPrivate with any
|
||||
// JSValueRef. Doing so with immediate values (numbers, null, bool,
|
||||
// undefined) will crash the app. So we check for these first.
|
||||
|
||||
#if __LP64__
|
||||
return !((int64_t)v & 0xffff000000000002ll)
|
||||
? JSObjectGetPrivate((JSObjectRef)v)
|
||||
: NULL;
|
||||
#else
|
||||
return JSObjectGetPrivate((JSObjectRef)v);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
173
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/EJConvert.m
Normal file
173
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/EJConvert.m
Normal file
@@ -0,0 +1,173 @@
|
||||
#import "EJConvert.h"
|
||||
|
||||
NSString *JSValueToNSString( JSContextRef ctx, JSValueRef v ) {
|
||||
JSStringRef jsString = JSValueToStringCopy( ctx, v, NULL );
|
||||
if( !jsString ) return nil;
|
||||
|
||||
NSString *string = (NSString *)JSStringCopyCFString( kCFAllocatorDefault, jsString );
|
||||
[string autorelease];
|
||||
JSStringRelease( jsString );
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
JSValueRef NSStringToJSValue( JSContextRef ctx, NSString *string ) {
|
||||
JSStringRef jstr = JSStringCreateWithCFString((CFStringRef)string);
|
||||
JSValueRef ret = JSValueMakeString(ctx, jstr);
|
||||
JSStringRelease(jstr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// JSValueToNumberFast blindly assumes that the given JSValueRef is a
|
||||
// a number. Everything else will be silently converted to 0.
|
||||
// This functions comes in a 64bit and 32bit flavor, since the NaN-Boxing
|
||||
// in JSC works a bit differently on each platforms. For an explanation
|
||||
// of the taggging refer to JSC/runtime/JSCJSValue.h
|
||||
// The 32bit version just calls the normal JSValueToNumber() function
|
||||
// and is thus a lot slower.
|
||||
|
||||
double JSValueToNumberFast(JSContextRef ctx, JSValueRef v) {
|
||||
#if __LP64__ // arm64 version
|
||||
union {
|
||||
int64_t asInt64;
|
||||
double asDouble;
|
||||
struct { int32_t asInt; int32_t tag; } asBits;
|
||||
} taggedValue = { .asInt64 = (int64_t)v };
|
||||
|
||||
#define DoubleEncodeOffset 0x1000000000000ll
|
||||
#define TagTypeNumber 0xffff0000
|
||||
#define ValueTrue 0x7
|
||||
|
||||
if( (taggedValue.asBits.tag & TagTypeNumber) == TagTypeNumber ) {
|
||||
return taggedValue.asBits.asInt;
|
||||
}
|
||||
else if( taggedValue.asBits.tag & TagTypeNumber ) {
|
||||
taggedValue.asInt64 -= DoubleEncodeOffset;
|
||||
return taggedValue.asDouble;
|
||||
}
|
||||
else if( taggedValue.asBits.asInt == ValueTrue ) {
|
||||
return 1.0;
|
||||
}
|
||||
else {
|
||||
return 0; // false, undefined, null, object
|
||||
}
|
||||
#else // armv7 version
|
||||
return JSValueToNumber(ctx, v, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void JSValueUnprotectSafe( JSContextRef ctx, JSValueRef v ) {
|
||||
if( ctx && v ) {
|
||||
JSValueUnprotect(ctx, v);
|
||||
}
|
||||
}
|
||||
|
||||
JSValueRef NSObjectToJSValue( JSContextRef ctx, NSObject *obj ) {
|
||||
JSValueRef ret = NULL;
|
||||
|
||||
// String
|
||||
if( [obj isKindOfClass:NSString.class] ) {
|
||||
ret = NSStringToJSValue(ctx, (NSString *)obj);
|
||||
}
|
||||
|
||||
// Number or Bool
|
||||
else if( [obj isKindOfClass:NSNumber.class] ) {
|
||||
NSNumber *number = (NSNumber *)obj;
|
||||
if( strcmp(number.objCType, @encode(BOOL)) == 0 ) {
|
||||
ret = JSValueMakeBoolean(ctx, number.boolValue);
|
||||
}
|
||||
else {
|
||||
ret = JSValueMakeNumber(ctx, number.doubleValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Date
|
||||
else if( [obj isKindOfClass:NSDate.class] ) {
|
||||
NSDate *date = (NSDate *)obj;
|
||||
JSValueRef timestamp = JSValueMakeNumber(ctx, date.timeIntervalSince1970 * 1000.0);
|
||||
ret = JSObjectMakeDate(ctx, 1, ×tamp, NULL);
|
||||
}
|
||||
|
||||
// Array
|
||||
else if( [obj isKindOfClass:NSArray.class] ) {
|
||||
NSArray *array = (NSArray *)obj;
|
||||
JSValueRef *args = malloc(array.count * sizeof(JSValueRef));
|
||||
for( int i = 0; i < array.count; i++ ) {
|
||||
args[i] = NSObjectToJSValue(ctx, array[i] );
|
||||
}
|
||||
ret = JSObjectMakeArray(ctx, array.count, args, NULL);
|
||||
free(args);
|
||||
}
|
||||
|
||||
// Dictionary
|
||||
else if( [obj isKindOfClass:NSDictionary.class] ) {
|
||||
NSDictionary *dict = (NSDictionary *)obj;
|
||||
ret = JSObjectMake(ctx, NULL, NULL);
|
||||
for( NSString *key in dict ) {
|
||||
JSStringRef jsKey = JSStringCreateWithUTF8CString(key.UTF8String);
|
||||
JSValueRef value = NSObjectToJSValue(ctx, dict[key]);
|
||||
JSObjectSetProperty(ctx, (JSObjectRef)ret, jsKey, value, NULL, NULL);
|
||||
JSStringRelease(jsKey);
|
||||
}
|
||||
}
|
||||
|
||||
return ret ? ret : JSValueMakeNull(ctx);
|
||||
}
|
||||
|
||||
NSObject *JSValueToNSObject( JSContextRef ctx, JSValueRef value ) {
|
||||
JSType type = JSValueGetType(ctx, value);
|
||||
|
||||
switch( type ) {
|
||||
case kJSTypeString: return JSValueToNSString(ctx, value);
|
||||
case kJSTypeBoolean: return [NSNumber numberWithBool:JSValueToBoolean(ctx, value)];
|
||||
case kJSTypeNumber: return [NSNumber numberWithDouble:JSValueToNumberFast(ctx, value)];
|
||||
case kJSTypeNull: return nil;
|
||||
case kJSTypeUndefined: return nil;
|
||||
case kJSTypeObject: break;
|
||||
}
|
||||
|
||||
if( type == kJSTypeObject ) {
|
||||
JSObjectRef jsObj = (JSObjectRef)value;
|
||||
|
||||
// Get the Array constructor to check if this Object is an Array
|
||||
JSStringRef arrayName = JSStringCreateWithUTF8CString("Array");
|
||||
JSObjectRef arrayConstructor = (JSObjectRef)JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), arrayName, NULL);
|
||||
JSStringRelease(arrayName);
|
||||
|
||||
if( JSValueIsInstanceOfConstructor(ctx, jsObj, arrayConstructor, NULL) ) {
|
||||
// Array
|
||||
JSStringRef lengthName = JSStringCreateWithUTF8CString("length");
|
||||
int count = JSValueToNumberFast(ctx, JSObjectGetProperty(ctx, jsObj, lengthName, NULL));
|
||||
JSStringRelease(lengthName);
|
||||
|
||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity:count];
|
||||
for( int i = 0; i < count; i++ ) {
|
||||
NSObject *obj = JSValueToNSObject(ctx, JSObjectGetPropertyAtIndex(ctx, jsObj, i, NULL));
|
||||
[array addObject:(obj ? obj : NSNull.null)];
|
||||
}
|
||||
return array;
|
||||
}
|
||||
else {
|
||||
// Plain Object
|
||||
JSPropertyNameArrayRef properties = JSObjectCopyPropertyNames(ctx, jsObj);
|
||||
size_t count = JSPropertyNameArrayGetCount(properties);
|
||||
|
||||
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:count];
|
||||
for( size_t i = 0; i < count; i++ ) {
|
||||
JSStringRef jsName = JSPropertyNameArrayGetNameAtIndex(properties, i);
|
||||
NSObject *obj = JSValueToNSObject(ctx, JSObjectGetProperty(ctx, jsObj, jsName, NULL));
|
||||
|
||||
NSString *name = (NSString *)JSStringCopyCFString( kCFAllocatorDefault, jsName );
|
||||
dict[name] = obj ? obj : NSNull.null;
|
||||
[name release];
|
||||
}
|
||||
|
||||
JSPropertyNameArrayRelease(properties);
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <JavaScriptCore/JavaScriptCore.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@enum JSType
|
||||
@abstract A constant identifying the Typed Array type of a JSValue.
|
||||
@constant kEJJSTypedArrayTypeNone Not a Typed Array.
|
||||
@constant kEJJSTypedArrayTypeInt8Array Int8Array
|
||||
@constant kEJJSTypedArrayTypeInt16Array Int16Array
|
||||
@constant kEJJSTypedArrayTypeInt32Array Int32Array
|
||||
@constant kEJJSTypedArrayTypeUint8Array Int8Array
|
||||
@constant kEJJSTypedArrayTypeUint8ClampedArray Int8ClampedArray
|
||||
@constant kEJJSTypedArrayTypeUint16Array Uint16Array
|
||||
@constant kEJJSTypedArrayTypeUint32Array Uint32Array
|
||||
@constant kEJJSTypedArrayTypeFloat32Array Float32Array
|
||||
@constant kEJJSTypedArrayTypeFloat64Array Float64Array
|
||||
@constant kEJJSTypedArrayTypeArrayBuffer ArrayBuffer
|
||||
*/
|
||||
typedef enum {
|
||||
kEJJSTypedArrayTypeNone = 0,
|
||||
kEJJSTypedArrayTypeInt8Array = 1,
|
||||
kEJJSTypedArrayTypeInt16Array = 2,
|
||||
kEJJSTypedArrayTypeInt32Array = 3,
|
||||
kEJJSTypedArrayTypeUint8Array = 4,
|
||||
kEJJSTypedArrayTypeUint8ClampedArray = 5,
|
||||
kEJJSTypedArrayTypeUint16Array = 6,
|
||||
kEJJSTypedArrayTypeUint32Array = 7,
|
||||
kEJJSTypedArrayTypeFloat32Array = 8,
|
||||
kEJJSTypedArrayTypeFloat64Array = 9,
|
||||
kEJJSTypedArrayTypeArrayBuffer = 10
|
||||
} EJJSTypedArrayType;
|
||||
|
||||
/*!
|
||||
@function
|
||||
@abstract Setup the JSContext for use of the Typed Array functions.
|
||||
@param ctx The execution context to use
|
||||
*/
|
||||
void EJJSContextPrepareTypedArrayAPI(JSContextRef ctx);
|
||||
|
||||
/*!
|
||||
@function
|
||||
@abstract Returns a JavaScript value's Typed Array type
|
||||
@param ctx The execution context to use.
|
||||
@param value The JSObject whose Typed Array type you want to obtain.
|
||||
@result A value of type EJJSTypedArrayType that identifies value's Typed Array type
|
||||
*/
|
||||
EJJSTypedArrayType EJJSObjectGetTypedArrayType(JSContextRef ctx, JSObjectRef object);
|
||||
|
||||
/*!
|
||||
@function
|
||||
@abstract Creates an empty JavaScript Typed Array with the given number of elements
|
||||
@param ctx The execution context to use.
|
||||
@param arrayType A value of type EJJSTypedArrayType identifying the type of array you want to create
|
||||
@param numElements The number of elements for the array.
|
||||
@result A JSObjectRef that is a Typed Array or NULL if there was an error
|
||||
*/
|
||||
JSObjectRef EJJSObjectMakeTypedArray(JSContextRef ctx, EJJSTypedArrayType arrayType, size_t numElements);
|
||||
|
||||
/*!
|
||||
@function
|
||||
@abstract Returns a copy of the Typed Array's data
|
||||
@param ctx The execution context to use.
|
||||
@param value The JSObject whose Typed Array data you want to obtain.
|
||||
@result A copy of the Typed Array's data or NULL if the JSObject is not a Typed Array
|
||||
*/
|
||||
NSMutableData *EJJSObjectGetTypedArrayData(JSContextRef ctx, JSObjectRef object);
|
||||
|
||||
/*!
|
||||
@function
|
||||
@abstract Replaces a Typed Array's data
|
||||
@param ctx The execution context to use.
|
||||
@param value The JSObject whose Typed Array data you want to replace
|
||||
*/
|
||||
void EJJSObjectSetTypedArrayData(JSContextRef ctx, JSObjectRef object, NSData *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,394 @@
|
||||
#import "EJConvertTypedArray.h"
|
||||
#import "EJConvert.h"
|
||||
|
||||
#if defined __ARM_NEON__
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
// These functions deal with getting and setting a JavaScript Typed Array's data.
|
||||
// The process in which this is done is extremely backwards, prohibitively
|
||||
// memory inefficient and painfully slow.
|
||||
|
||||
// JavaScriptCore still doesn't have an API to deal with Typed Arrays directly
|
||||
// and nobody seems to care. See this WebKit bug for the details:
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=120112
|
||||
|
||||
// The naive method of getting the array's data would be to read the "length"
|
||||
// property and call JSObjectGetPropertyAtIndex() for each element. This function
|
||||
// returns a JSValue and since the array's data is just a raw memory pointer
|
||||
// internally, this JSValue has be constructed first by JSC. Afterwards this
|
||||
// JSValue has to be converted back into an int and stored in a native buffer.
|
||||
// This ordeal takes about 1200ms per megabyte on an iPhone5S.
|
||||
|
||||
// So, instead of extracting each byte on its own, we create Int32 view on the
|
||||
// Array, so that we can extract 4 bytes at a time. Now, the fastest method to
|
||||
// get to the values is to call a function with .apply(null, theInt32Array).
|
||||
// This causes the typed array to be converted into plain JSValues - we can then
|
||||
// iterate all function arguments and convert each JSValue into a Int32.
|
||||
|
||||
// There's one big caveat, though: a function can only be called with 64k
|
||||
// arguments at once, or it will blow the stack. So we have to divide the Int32
|
||||
// Array into smaller subarrays and convert the data in chunks.
|
||||
|
||||
// Setting the Array's data works about the same. We can create a plain JS
|
||||
// Array in reasonable time by treating the data as 32bit ints. This plain
|
||||
// Array can then be used to call .set() on an Int32 View of the Typed Array.
|
||||
|
||||
// To be able to quickly determine the type of a typed array, we install an
|
||||
// enum with the type id ("__ejTypedArrayType") onto each array constructor's
|
||||
// prototype.
|
||||
|
||||
// This all brings the time down to about 7ms per megabyte for reading and about
|
||||
// 20ms per megabyte for writing. Even though this 7ms/MB number may not sound
|
||||
// all too bad, keep in mind that it's still 7ms slower than what we would have
|
||||
// with a better API.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Array Types to constructor names. We need to obtain the Constructors from
|
||||
// the JSGlobalObject when creating Typed Arrays and when attaching our methods
|
||||
// to them.
|
||||
|
||||
const static char *ConstructorNames[] = {
|
||||
[kEJJSTypedArrayTypeNone] = NULL,
|
||||
[kEJJSTypedArrayTypeInt8Array] = "Int8Array",
|
||||
[kEJJSTypedArrayTypeInt16Array] = "Int16Array",
|
||||
[kEJJSTypedArrayTypeInt32Array] = "Int32Array",
|
||||
[kEJJSTypedArrayTypeUint8Array] = "Uint8Array",
|
||||
[kEJJSTypedArrayTypeUint8ClampedArray] = "Uint8ClampedArray",
|
||||
[kEJJSTypedArrayTypeUint16Array] = "Uint16Array",
|
||||
[kEJJSTypedArrayTypeUint32Array] = "Uint32Array",
|
||||
[kEJJSTypedArrayTypeFloat32Array] = "Float32Array",
|
||||
[kEJJSTypedArrayTypeFloat64Array] = "Float64Array",
|
||||
[kEJJSTypedArrayTypeArrayBuffer] = "ArrayBuffer"
|
||||
};
|
||||
|
||||
|
||||
// Data from Typed Arrays smaller than CopyInChunksThreshold will be extracted
|
||||
// one byte at a time.
|
||||
|
||||
const static int CopyInChunksThreshold = 32;
|
||||
|
||||
|
||||
// For large arrays, copy the data in Chunks of 16k elements, so that we don't
|
||||
// blow the stack.
|
||||
|
||||
const static int CopyChunkSize = 0x4000;
|
||||
|
||||
|
||||
|
||||
// Hobo version to get an Int32 from a JSValueRef. This is even faster than
|
||||
// JSValueToNumberFast() because it blindly assumes the value is an Int32.
|
||||
// A number stored as double will produce a bogus Int value. JSC stores all
|
||||
// values that are outside of the Int32 range as double.
|
||||
|
||||
// This function is only fast on 64bit systems. On 32bit systems we can't
|
||||
// easily access the integer value.
|
||||
// See JavaScriptCore/runtime/JSCJSValue.h for an explanation of the NaN-
|
||||
// boxing used in JSC.
|
||||
|
||||
// Use with extreme caution!
|
||||
|
||||
static inline int32_t GetInt32(JSContextRef ctx, JSValueRef v) {
|
||||
#if __LP64__
|
||||
return (int32_t)(0x00000000ffffffffll & (uint64_t)v);
|
||||
#else
|
||||
return JSValueToNumber(ctx, v, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Hobo version to create an Int32 fast. Falls back to JSValueMakeNumber()
|
||||
// on 32bit systems.
|
||||
|
||||
static inline JSValueRef MakeInt32(JSContextRef ctx, int32_t number) {
|
||||
#if __LP64__
|
||||
return (0xffff000000000000ll | (uint64_t)number);
|
||||
#else
|
||||
return JSValueMakeNumber(ctx, number);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Shorthand to get a property by name
|
||||
|
||||
static JSValueRef GetPropertyNamed(JSContextRef ctx, JSObjectRef object, const char *name) {
|
||||
JSStringRef jsPropertyName = JSStringCreateWithUTF8CString(name);
|
||||
JSValueRef value = JSObjectGetProperty(ctx, object, jsPropertyName, NULL);
|
||||
JSStringRelease(jsPropertyName);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
// Shorthand to get the Constructor for the given EJJSTypedArrayType
|
||||
|
||||
static JSObjectRef GetConstructor(JSContextRef ctx, EJJSTypedArrayType type) {
|
||||
if( type <= kEJJSTypedArrayTypeNone || type > kEJJSTypedArrayTypeArrayBuffer ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *constructorName = ConstructorNames[type];
|
||||
JSObjectRef global = JSContextGetGlobalObject(ctx);
|
||||
return (JSObjectRef)GetPropertyNamed(ctx, global, constructorName);
|
||||
}
|
||||
|
||||
|
||||
// Create a typed array view from another typed array or arraybuffer
|
||||
|
||||
static JSObjectRef GetView(JSContextRef ctx, JSObjectRef object, EJJSTypedArrayType type, size_t count) {
|
||||
EJJSTypedArrayType currentType = EJJSObjectGetTypedArrayType(ctx, object);
|
||||
if( currentType == kEJJSTypedArrayTypeNone ) {
|
||||
return NULL;
|
||||
}
|
||||
else if( currentType == type ) {
|
||||
return object;
|
||||
}
|
||||
|
||||
JSValueRef args[3];
|
||||
if( currentType == kEJJSTypedArrayTypeArrayBuffer ) {
|
||||
args[0] = object;
|
||||
args[1] = MakeInt32(ctx, 0);
|
||||
args[2] = MakeInt32(ctx, (int)count);
|
||||
}
|
||||
else {
|
||||
args[0] = GetPropertyNamed(ctx, object, "buffer");
|
||||
args[1] = GetPropertyNamed(ctx, object, "byteOffset");
|
||||
args[2] = MakeInt32(ctx, (int)count);
|
||||
}
|
||||
JSObjectRef constructor = GetConstructor(ctx, type);
|
||||
return JSObjectCallAsConstructor(ctx, constructor, 3, args, NULL);
|
||||
}
|
||||
|
||||
|
||||
// The callback called from JSObjectGetTypedArrayData with chunks of data. This writes
|
||||
// into the state's data.
|
||||
|
||||
typedef struct {
|
||||
int32_t *currentDataPtr;
|
||||
JSObjectRef jsGetCallback;
|
||||
JSObjectRef jsGetCallbackApply;
|
||||
} AppendDataCallbackState;
|
||||
|
||||
static JSValueRef AppendDataCallback(
|
||||
JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
|
||||
size_t argc, const JSValueRef argv[], JSValueRef* exception
|
||||
) {
|
||||
AppendDataCallbackState *state = JSObjectGetPrivate(thisObject);
|
||||
int32_t *dst = state->currentDataPtr;
|
||||
int remainderStart = 0;
|
||||
|
||||
// On 64bit systems where ARM_NEON instructions are available we can use
|
||||
// some SIMD intrinsics to extract the lower 32bit of each JSValueRef.
|
||||
// Hopefully the JSValueRef encodes an Int32 so the lower 32bit corresponds
|
||||
// to that Int32 exactly.
|
||||
|
||||
#if __LP64__ && defined __ARM_NEON__
|
||||
// Iterate over the arguments in packs of 4.
|
||||
// Load arguments as 2 lanes of 4 int32 values and store the first
|
||||
// lane (the lower 32bit) into the dst buffer.
|
||||
|
||||
int argPacks4 = (int)argc/4;
|
||||
int32_t *src = (int32_t*)argv;
|
||||
|
||||
for(int i = 0; i < argPacks4; i++ ) {
|
||||
const int32x4x2_t lanes32 = vld2q_s32(src);
|
||||
vst1q_s32(dst, lanes32.val[0]);
|
||||
src += 8;
|
||||
dst += 4;
|
||||
}
|
||||
remainderStart = argPacks4 * 4;
|
||||
#endif
|
||||
|
||||
for( int i = remainderStart; i < argc; i++ ) {
|
||||
*(dst++) = GetInt32(ctx, argv[i]);
|
||||
}
|
||||
|
||||
state->currentDataPtr += argc;
|
||||
return MakeInt32(ctx, (int)argc);
|
||||
}
|
||||
|
||||
|
||||
// To store the AppendDataCallbackState and access it _per context_, we create
|
||||
// a plain JSObject where we can store a pointer to the state in the private
|
||||
// data.
|
||||
|
||||
static void FinalizeAppendDataCallbackState(JSObjectRef object) {
|
||||
AppendDataCallbackState *state = JSObjectGetPrivate(object);
|
||||
free(state);
|
||||
}
|
||||
|
||||
static JSObjectRef CreateAppendDataCallbackState(JSContextRef ctx) {
|
||||
// Create a JS function that calls the AppendDataCallback
|
||||
JSObjectRef jsAppendDataCallback = JSObjectMakeFunctionWithCallback(ctx, NULL, AppendDataCallback);
|
||||
JSValueProtect(ctx, jsAppendDataCallback);
|
||||
|
||||
// Allocate and create the state and attach the AppendDataCallback
|
||||
// function and its .apply property.
|
||||
AppendDataCallbackState *state = malloc(sizeof(AppendDataCallbackState));
|
||||
state->currentDataPtr = NULL;
|
||||
state->jsGetCallback = jsAppendDataCallback;
|
||||
state->jsGetCallbackApply = (JSObjectRef)GetPropertyNamed(ctx, jsAppendDataCallback, "apply");
|
||||
|
||||
|
||||
// Create the empty js object that holds the state in its Private data
|
||||
JSClassDefinition internalStateClassDef = kJSClassDefinitionEmpty;
|
||||
internalStateClassDef.finalize = FinalizeAppendDataCallbackState;
|
||||
|
||||
JSClassRef internalStateClass = JSClassCreate(&internalStateClassDef);
|
||||
JSObjectRef internalStateObject = JSObjectMake(ctx, internalStateClass, state);
|
||||
JSClassRelease(internalStateClass);
|
||||
return internalStateObject;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void EJJSContextPrepareTypedArrayAPI(JSContextRef ctx) {
|
||||
// The __ejTypedArrayType property is read only, not enumerable
|
||||
JSPropertyAttributes attributes =
|
||||
kJSPropertyAttributeReadOnly |
|
||||
kJSPropertyAttributeDontEnum |
|
||||
kJSPropertyAttributeDontDelete;
|
||||
|
||||
// Install the __ejTypedArrayType property on each Typed Array prototype
|
||||
JSStringRef jsTypeName = JSStringCreateWithUTF8CString("__ejTypedArrayType");
|
||||
|
||||
for( int type = kEJJSTypedArrayTypeInt8Array; type <= kEJJSTypedArrayTypeArrayBuffer; type++ ) {
|
||||
JSObjectRef jsConstructor = GetConstructor(ctx, type);
|
||||
JSObjectRef jsPrototype = (JSObjectRef)GetPropertyNamed(ctx, jsConstructor, "prototype");
|
||||
|
||||
JSValueRef jsType = MakeInt32(ctx, type);
|
||||
JSObjectSetProperty(ctx, jsPrototype, jsTypeName, jsType, attributes, NULL);
|
||||
}
|
||||
|
||||
JSStringRelease(jsTypeName);
|
||||
|
||||
|
||||
// Create the state object for the AppendData Callback and attach it to the global
|
||||
// object
|
||||
JSObjectRef jsCallbackStateObject = CreateAppendDataCallbackState(ctx);
|
||||
|
||||
JSStringRef jsInternalStateName = JSStringCreateWithUTF8CString("__ejTypedArrayState");
|
||||
JSObjectRef global = JSContextGetGlobalObject(ctx);
|
||||
JSObjectSetProperty(ctx, global, jsInternalStateName, jsCallbackStateObject, attributes, NULL);
|
||||
JSStringRelease(jsInternalStateName);
|
||||
}
|
||||
|
||||
|
||||
EJJSTypedArrayType EJJSObjectGetTypedArrayType(JSContextRef ctx, JSObjectRef object) {
|
||||
JSValueRef jsType = GetPropertyNamed(ctx, object, "__ejTypedArrayType");
|
||||
return jsType ? GetInt32(ctx, jsType) : kEJJSTypedArrayTypeNone;
|
||||
}
|
||||
|
||||
JSObjectRef EJJSObjectMakeTypedArray(JSContextRef ctx, EJJSTypedArrayType arrayType, size_t numElements) {
|
||||
JSObjectRef jsConstructor = GetConstructor(ctx, arrayType);
|
||||
if( !jsConstructor ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSValueRef jsNumElements = MakeInt32(ctx, (int)numElements);
|
||||
return JSObjectCallAsConstructor(ctx, jsConstructor, 1, (JSValueRef[]){jsNumElements}, NULL);
|
||||
}
|
||||
|
||||
|
||||
NSMutableData *EJJSObjectGetTypedArrayData(JSContextRef ctx, JSObjectRef object) {
|
||||
size_t length = GetInt32(ctx, GetPropertyNamed(ctx, object, "byteLength"));
|
||||
if( !length ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t int32Count = length / 4;
|
||||
size_t uint8Count = length % 4;
|
||||
|
||||
// For very small typed arrays it's faster to read all bytes individually
|
||||
// instead of doing the callback dance.
|
||||
if( length < CopyInChunksThreshold ) {
|
||||
int32Count = 0;
|
||||
uint8Count = length;
|
||||
}
|
||||
|
||||
NSMutableData *data = [NSMutableData dataWithLength:length];
|
||||
|
||||
// Read data in large chunks of 32bit values
|
||||
if( int32Count ) {
|
||||
JSObjectRef int32View = GetView(ctx, object, kEJJSTypedArrayTypeInt32Array, int32Count);
|
||||
if( !int32View ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSObjectRef jsState = (JSObjectRef)GetPropertyNamed(ctx, JSContextGetGlobalObject(ctx), "__ejTypedArrayState");
|
||||
AppendDataCallbackState *state = JSObjectGetPrivate(jsState);
|
||||
state->currentDataPtr = data.mutableBytes;
|
||||
|
||||
|
||||
// If the whole data is smaller than the chunk size, we only have to call our callback once, otherwise
|
||||
// we have to create subarrays and call the callback with these.
|
||||
if( int32Count < CopyChunkSize ) {
|
||||
JSValueRef getArgs[] = {jsState, int32View};
|
||||
JSObjectCallAsFunction(ctx, state->jsGetCallbackApply, state->jsGetCallback, 2, getArgs, NULL);
|
||||
}
|
||||
else {
|
||||
JSObjectRef subarrayFunc = (JSObjectRef)GetPropertyNamed(ctx, int32View, "subarray");
|
||||
|
||||
for( int i = 0; i < int32Count; i+= CopyChunkSize) {
|
||||
JSValueRef subarrayArgs[] = {MakeInt32(ctx, i), MakeInt32(ctx, i + CopyChunkSize)};
|
||||
JSObjectRef jsSubarray = (JSObjectRef)JSObjectCallAsFunction(ctx, subarrayFunc, int32View, 2, subarrayArgs, NULL);
|
||||
|
||||
JSValueRef getArgs[] = {jsState, jsSubarray};
|
||||
JSObjectCallAsFunction(ctx, state->jsGetCallbackApply, state->jsGetCallback, 2, getArgs, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read remaining bytes directly
|
||||
if( uint8Count ) {
|
||||
uint8_t *values8 = data.mutableBytes;
|
||||
JSObjectRef uint8View = GetView(ctx, object, kEJJSTypedArrayTypeUint8Array, length);
|
||||
for( int i = 0; i < uint8Count; i++ ) {
|
||||
int index = (int)int32Count * 4 + i;
|
||||
values8[index] = GetInt32(ctx, JSObjectGetPropertyAtIndex(ctx, uint8View, index, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
void EJJSObjectSetTypedArrayData(JSContextRef ctx, JSObjectRef object, NSData *data) {
|
||||
size_t int32Count = data.length / 4;
|
||||
size_t uint8Count = data.length % 4;
|
||||
|
||||
if( int32Count ) {
|
||||
JSObjectRef int32View = GetView(ctx, object, kEJJSTypedArrayTypeInt32Array, int32Count);
|
||||
if( !int32View ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct the JSValues and create the plain JSArray
|
||||
JSValueRef *jsValues = malloc(int32Count * sizeof(JSValueRef));
|
||||
|
||||
const int32_t *values32 = data.bytes;
|
||||
for(int i = 0; i < int32Count; i++) {
|
||||
jsValues[i] = MakeInt32(ctx, values32[i]);
|
||||
}
|
||||
JSObjectRef jsArray = JSObjectMakeArray(ctx, int32Count, jsValues, NULL);
|
||||
|
||||
free(jsValues);
|
||||
|
||||
// Call the .set() function on the Typed Array
|
||||
JSObjectRef setFunction = (JSObjectRef)GetPropertyNamed(ctx, int32View, "set");
|
||||
JSObjectCallAsFunction(ctx, setFunction, int32View, 1, (JSValueRef[]){jsArray}, NULL);
|
||||
}
|
||||
|
||||
// Set remaining bytes directly
|
||||
if( uint8Count ) {
|
||||
const uint8_t *values8 = data.bytes;
|
||||
JSObjectRef uint8View = GetView(ctx, object, kEJJSTypedArrayTypeUint8Array, data.length);
|
||||
for( int i = 0; i < uint8Count; i++ ) {
|
||||
int index = (int)int32Count * 4 + i;
|
||||
JSObjectSetPropertyAtIndex(ctx, uint8View, index, MakeInt32(ctx, values8[index]), NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define SE_UNUSED __attribute__ ((unused))
|
||||
#else
|
||||
#define SE_UNUSED
|
||||
#endif
|
||||
|
||||
#define SAFE_INC_REF(obj) if (obj != nullptr) obj->incRef()
|
||||
#define SAFE_DEC_REF(obj) if ((obj) != nullptr) { (obj)->decRef(); (obj) = nullptr; }
|
||||
|
||||
#define _SE(name) name##Registry
|
||||
|
||||
#define SE_DECLARE_FUNC(funcName) \
|
||||
JSValueRef funcName##Registry(JSContextRef _cx, JSObjectRef _function, JSObjectRef _thisObject, size_t argc, const JSValueRef _argv[], JSValueRef* _exception)
|
||||
|
||||
|
||||
#define SE_BIND_FUNC(funcName) \
|
||||
JSValueRef funcName##Registry(JSContextRef _cx, JSObjectRef _function, JSObjectRef _thisObject, size_t _argc, const JSValueRef _argv[], JSValueRef* _exception) \
|
||||
{ \
|
||||
unsigned short argc = (unsigned short) _argc; \
|
||||
JSValueRef _jsRet = JSValueMakeUndefined(_cx); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_thisObject); \
|
||||
if (nativeThisObject != (void*)std::numeric_limits<unsigned long>::max()) \
|
||||
{ \
|
||||
bool ret = true; \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, &args); \
|
||||
se::State state(nativeThisObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
se::internal::seToJsValue(_cx, state.rval(), &_jsRet); \
|
||||
} \
|
||||
return _jsRet; \
|
||||
}
|
||||
|
||||
#define SE_BIND_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(JSObjectRef _obj) \
|
||||
{ \
|
||||
auto se = se::ScriptEngine::getInstance(); \
|
||||
se->_setGarbageCollecting(true); \
|
||||
void* nativeThisObject = JSObjectGetPrivate(_obj); \
|
||||
if (nativeThisObject != nullptr) \
|
||||
{ \
|
||||
bool ret = false; \
|
||||
se::State state(nativeThisObject); \
|
||||
se::Object* _thisObject = state.thisObject(); \
|
||||
if (_thisObject) _thisObject->_cleanup(nativeThisObject); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
JSObjectSetPrivate(_obj, nullptr); \
|
||||
SAFE_DEC_REF(_thisObject); \
|
||||
} \
|
||||
se->_setGarbageCollecting(false); \
|
||||
}
|
||||
|
||||
#define SE_DECLARE_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(JSObjectRef _obj);
|
||||
|
||||
|
||||
// NOTE: se::Object::createObjectWithClass(cls) will return a se::Object pointer which is watched by garbage collector.
|
||||
// If there is a '_ctor' function of current class, '_property.toObject->call(...)' will be invoked which is an operation that may
|
||||
// make garbage collector to mark the created JS object as a garbage and set it to an invalid state.
|
||||
// If this happens, crash will be triggered. So please take care of the value returned from se::Object::createObjectWithClass.
|
||||
// HOW TO FIX: Use a rooted se::Value to save the se::Object poiner returned by se::Object::createObjectWithClass.
|
||||
#define SE_BIND_CTOR(funcName, cls, finalizeCb) \
|
||||
JSObjectRef funcName##Registry(JSContextRef _cx, JSObjectRef _constructor, size_t argc, const JSValueRef _argv[], JSValueRef* _exception) \
|
||||
{ \
|
||||
bool ret = true; \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, &args); \
|
||||
se::Value thisVal(se::Object::createObjectWithClass(cls), true); \
|
||||
se::Object* thisObject = thisVal.toObject(); \
|
||||
JSValueRef _jsRet = JSValueMakeUndefined(_cx); \
|
||||
se::State state(thisObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (ret) \
|
||||
{ \
|
||||
_jsRet = thisObject->_getJSObject(); \
|
||||
se::Value _property; \
|
||||
bool _found = false; \
|
||||
_found = thisObject->getProperty("_ctor", &_property); \
|
||||
if (_found) _property.toObject()->call(args, thisObject); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
return JSValueToObject(_cx, _jsRet, nullptr); \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_SUB_CLS_CTOR(funcName, cls, finalizeCb) \
|
||||
JSValueRef funcName##Registry(JSContextRef _cx, JSObjectRef _function, JSObjectRef _thisObject, size_t argc, const JSValueRef _argv[], JSValueRef* _exception) \
|
||||
{ \
|
||||
bool ret = true; \
|
||||
JSValueRef _jsRet = JSValueMakeUndefined(_cx); \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, &args); \
|
||||
se::Object* thisObject = se::Object::_createJSObject(cls, _thisObject); \
|
||||
thisObject->_setFinalizeCallback(_SE(finalizeCb)); \
|
||||
se::State state(thisObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (ret) \
|
||||
{ \
|
||||
se::Value _property; \
|
||||
bool _found = false; \
|
||||
_found = thisObject->getProperty("_ctor", &_property); \
|
||||
if (_found) _property.toObject()->call(args, thisObject); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
return _jsRet; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_PROP_GET(funcName) \
|
||||
JSValueRef funcName##Registry(JSContextRef _cx, JSObjectRef _function, JSObjectRef _thisObject, size_t argc, const JSValueRef _argv[], JSValueRef* _exception) \
|
||||
{ \
|
||||
assert(argc == 0); \
|
||||
JSValueRef _jsRet = JSValueMakeUndefined(_cx); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_thisObject); \
|
||||
if (nativeThisObject != (void*)std::numeric_limits<unsigned long>::max()) \
|
||||
{ \
|
||||
se::State state(nativeThisObject); \
|
||||
if (funcName(state)) \
|
||||
{ \
|
||||
se::internal::seToJsValue(_cx, state.rval(), &_jsRet); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
} \
|
||||
return _jsRet; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_PROP_SET(funcName) \
|
||||
JSValueRef funcName##Registry(JSContextRef _cx, JSObjectRef _function, JSObjectRef _thisObject, size_t argc, const JSValueRef _argv[], JSValueRef* _exception) \
|
||||
{ \
|
||||
assert(argc == 1); \
|
||||
JSValueRef _jsRet = JSValueMakeUndefined(_cx); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_thisObject); \
|
||||
if (nativeThisObject != (void*)std::numeric_limits<unsigned long>::max()) \
|
||||
{ \
|
||||
bool ret = true; \
|
||||
se::Value data; \
|
||||
se::internal::jsToSeValue(_cx, _argv[0], &data); \
|
||||
se::ValueArray args; \
|
||||
args.push_back(std::move(data)); \
|
||||
se::State state(nativeThisObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
} \
|
||||
return _jsRet; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define SE_TYPE_NAME(t) typeid(t).name()
|
||||
|
||||
#define SE_QUOTEME_(x) #x
|
||||
#define SE_QUOTEME(x) SE_QUOTEME_(x)
|
||||
|
||||
//IDEA: implement this macro
|
||||
#define SE_REPORT_ERROR(fmt, ...) SE_LOGE("[ERROR] (" __FILE__ ", " SE_QUOTEME(__LINE__) "): " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#if COCOS2D_DEBUG > 0
|
||||
|
||||
#define SE_ASSERT(cond, fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(cond)) \
|
||||
{ \
|
||||
SE_LOGE("ASSERT (" __FILE__ ", " SE_QUOTEME(__LINE__) "): " fmt "\n", ##__VA_ARGS__); \
|
||||
assert(false); \
|
||||
} \
|
||||
} while(false)
|
||||
|
||||
#else
|
||||
|
||||
#define SE_ASSERT(cond, fmt, ...)
|
||||
|
||||
#endif // #if COCOS2D_DEBUG > 0
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
424
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Object.hpp
Normal file
424
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Object.hpp
Normal file
@@ -0,0 +1,424 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
#include "Base.h"
|
||||
#include "../Value.hpp"
|
||||
#include "../RefCounter.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Class;
|
||||
|
||||
/**
|
||||
* se::Object represents JavaScript Object.
|
||||
*/
|
||||
class Object final : public RefCounter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a JavaScript Object like `{} or new Object()`.
|
||||
* @return A JavaScript Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createPlainObject();
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Array Object like `[] or new Array()`.
|
||||
* @param[in] length The initical length of array.
|
||||
* @return A JavaScript Array Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createArrayObject(size_t length);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Typed Array Object with uint8 format from an existing pointer.
|
||||
* @param[in] bytes A pointer to the byte buffer to be used as the backing store of the Typed Array object.
|
||||
* @param[in] byteLength The number of bytes pointed to by the parameter bytes.
|
||||
* @return A JavaScript Typed Array Object whose backing store is the same as the one pointed data, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
* @deprecated This method is deprecated, please use `se::Object::createTypedArray` instead.
|
||||
*/
|
||||
SE_DEPRECATED_ATTRIBUTE static Object* createUint8TypedArray(uint8_t* bytes, size_t byteLength);
|
||||
|
||||
enum class TypedArrayType
|
||||
{
|
||||
NONE,
|
||||
INT8,
|
||||
INT16,
|
||||
INT32,
|
||||
UINT8,
|
||||
UINT8_CLAMPED,
|
||||
UINT16,
|
||||
UINT32,
|
||||
FLOAT32,
|
||||
FLOAT64
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Typed Array Object with specified format from an existing pointer,
|
||||
if provide a null pointer,then will create a empty JavaScript Typed Array Object.
|
||||
* @param[in] type The format of typed array.
|
||||
* @param[in] data A pointer to the byte buffer to be used as the backing store of the Typed Array object.
|
||||
* @param[in] byteLength The number of bytes pointed to by the parameter bytes.
|
||||
* @return A JavaScript Typed Array Object whose backing store is the same as the one pointed data, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createTypedArray(TypedArrayType type, void* data, size_t byteLength);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Array Buffer object from an existing pointer.
|
||||
* @param[in] bytes A pointer to the byte buffer to be used as the backing store of the Typed Array object.
|
||||
* @param[in] byteLength The number of bytes pointed to by the parameter bytes.
|
||||
* @return A Array Buffer Object whose backing store is the same as the one pointed to data, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createArrayBufferObject(void* bytes, size_t byteLength);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Object from a JSON formatted string.
|
||||
* @param[in] jsonStr The utf-8 string containing the JSON string to be parsed.
|
||||
* @return A JavaScript Object containing the parsed value, or nullptr if the input is invalid.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createJSONObject(const std::string& jsonStr);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Native Binding Object from an existing se::Class instance.
|
||||
* @param[in] cls The se::Class instance which stores native callback informations.
|
||||
* @return A JavaScript Native Binding Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* createObjectWithClass(Class* cls);
|
||||
|
||||
/**
|
||||
* @brief Gets a se::Object from an existing native object pointer.
|
||||
* @param[in] ptr The native object pointer associated with the se::Object
|
||||
* @return A JavaScript Native Binding Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object* getObjectWithPtr(void* ptr);
|
||||
|
||||
/**
|
||||
* @brief Gets a property from an object.
|
||||
* @param[in] name A utf-8 string containing the property's name.
|
||||
* @param[out] value The property's value if object has the property, otherwise the undefined value.
|
||||
* @return true if object has the property, otherwise false.
|
||||
*/
|
||||
bool getProperty(const char* name, Value* value);
|
||||
|
||||
/**
|
||||
* @brief Sets a property to an object.
|
||||
* @param[in] name A utf-8 string containing the property's name.
|
||||
* @param[in] value A value to be used as the property's value.
|
||||
* @return true if the property is set successfully, otherwise false.
|
||||
*/
|
||||
bool setProperty(const char* name, const Value& value);
|
||||
|
||||
/**
|
||||
* @brief Delete a property of an object.
|
||||
* @param[in] name A utf-8 string containing the property's name.
|
||||
* @return true if the property is deleted successfully, otherwise false.
|
||||
*/
|
||||
bool deleteProperty(const char *name);
|
||||
|
||||
/**
|
||||
* @brief Defines a property with native accessor callbacks for an object.
|
||||
* @param[in] name A utf-8 string containing the property's name.
|
||||
* @param[in] getter The native callback for getter.
|
||||
* @param[in] setter The native callback for setter.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineProperty(const char *name, JSObjectCallAsFunctionCallback getter, JSObjectCallAsFunctionCallback setter);
|
||||
|
||||
/**
|
||||
* @brief Defines a function with a native callback for an object.
|
||||
* @param[in] funcName A utf-8 string containing the function name.
|
||||
* @param[in] func The native callback triggered by JavaScript code.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineFunction(const char *funcName, JSObjectCallAsFunctionCallback func);
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object can be called as a function.
|
||||
* @return true if object can be called as a function, otherwise false.
|
||||
*/
|
||||
bool isFunction() const;
|
||||
|
||||
/**
|
||||
* @brief Calls an object as a function.
|
||||
* @param[in] args A se::Value array of arguments to pass to the function. Pass se::EmptyValueArray if argumentCount is 0.
|
||||
* @param[in] thisObject The object to use as "this," or NULL to use the global object as "this."
|
||||
* @param[out] rval The se::Value that results from calling object as a function, passing nullptr if return value is ignored.
|
||||
* @return true if object is a function and there isn't any errors, otherwise false.
|
||||
*/
|
||||
bool call(const ValueArray& args, Object* thisObject, Value* rval = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object is an array.
|
||||
* @return true if object is an array, otherwise false.
|
||||
*/
|
||||
bool isArray() const;
|
||||
|
||||
/**
|
||||
* @brief Gets array length of an array object.
|
||||
* @param[out] length The array length to be stored. It's set to 0 if there is an error.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getArrayLength(uint32_t* length) const;
|
||||
|
||||
/**
|
||||
* @brief Gets an element from an array object by numeric index.
|
||||
* @param[in] index An integer value for index.
|
||||
* @param[out] data The se::Value to be stored for the element in certain index.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getArrayElement(uint32_t index, Value* data) const;
|
||||
|
||||
/**
|
||||
* @brief Sets an element to an array object by numeric index.
|
||||
* @param[in] index An integer value for index.
|
||||
* @param[in] data The se::Value to be set to array with certain index.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool setArrayElement(uint32_t index, const Value& data);
|
||||
|
||||
/** @brief Tests whether an object is a typed array.
|
||||
* @return true if object is a typed array, otherwise false.
|
||||
*/
|
||||
bool isTypedArray() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the type of a typed array object.
|
||||
* @return The type of a typed array object.
|
||||
*/
|
||||
TypedArrayType getTypedArrayType() const;
|
||||
|
||||
/**
|
||||
* @brief Gets backing store of a typed array object.
|
||||
* @param[out] ptr A temporary pointer to the backing store of a JavaScript Typed Array object.
|
||||
* @param[out] length The byte length of a JavaScript Typed Array object.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getTypedArrayData(uint8_t** ptr, size_t* length) const;
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object is an array buffer object.
|
||||
* @return true if object is an array buffer object, otherwise false.
|
||||
*/
|
||||
bool isArrayBuffer() const;
|
||||
|
||||
/**
|
||||
* @brief Gets buffer data of an array buffer object.
|
||||
* @param[out] ptr A pointer to the data buffer that serves as the backing store for a JavaScript Typed Array object.
|
||||
* @param[out] length The number of bytes in a JavaScript data object.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getArrayBufferData(uint8_t** ptr, size_t* length) const;
|
||||
|
||||
/**
|
||||
* @brief Gets all property names of an object.
|
||||
* @param[out] allKeys A string vector to store all property names.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getAllKeys(std::vector<std::string>* allKeys) const;
|
||||
|
||||
/**
|
||||
* @brief Sets a pointer to private data on an object.
|
||||
* @param[in] data A void* to set as the object's private data.
|
||||
* @note This method will associate private data with se::Object by std::unordered_map::emplace.
|
||||
* It's used for search a se::Object via a void* private data.
|
||||
*/
|
||||
void setPrivateData(void* data);
|
||||
|
||||
/**
|
||||
* @brief Gets an object's private data.
|
||||
* @return A void* that is the object's private data, if the object has private data, otherwise nullptr.
|
||||
*/
|
||||
void* getPrivateData() const;
|
||||
|
||||
/**
|
||||
* @brief Clears private data of an object.
|
||||
* @param clearMapping Whether to clear the mapping of native object & se::Object.
|
||||
*/
|
||||
void clearPrivateData(bool clearMapping = true);
|
||||
|
||||
/**
|
||||
* @brief Roots an object from garbage collection.
|
||||
* @note Use this method when you want to store an object in a global or on the heap, where the garbage collector will not be able to discover your reference to it.
|
||||
* An object may be rooted multiple times and must be unrooted an equal number of times before becoming eligible for garbage collection.
|
||||
*/
|
||||
void root();
|
||||
|
||||
/**
|
||||
* @brief Unroots an object from garbage collection.
|
||||
* @note An object may be rooted multiple times and must be unrooted an equal number of times before becoming eligible for garbage collection.
|
||||
*/
|
||||
void unroot();
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object is rooted.
|
||||
* @return true if it has been already rooted, otherwise false.
|
||||
*/
|
||||
bool isRooted() const;
|
||||
|
||||
/**
|
||||
* @brief Tests whether two objects are strict equal, as compared by the JS === operator.
|
||||
* @param[in] o The object to be tested with this object.
|
||||
* @return true if the two values are strict equal, otherwise false.
|
||||
*/
|
||||
bool strictEquals(Object* o) const;
|
||||
|
||||
/**
|
||||
* @brief Attaches an object to current object.
|
||||
* @param[in] obj The object to be attached.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note This method will set `obj` as a property of current object, therefore the lifecycle of child object will depend on current object,
|
||||
* which means `obj` may be garbage collected only after current object is garbage collected.
|
||||
* It's normally used in binding a native callback method. For example:
|
||||
|
||||
```javascript
|
||||
var self = this;
|
||||
someObject.setCallback(function(){}, self);
|
||||
```
|
||||
|
||||
```c++
|
||||
static bool SomeObject_setCallback(se::State& s)
|
||||
{
|
||||
SomeObject* cobj = (SomeObject*)s.nativeThisObject();
|
||||
const auto& args = s.args();
|
||||
size_t argc = args.size();
|
||||
if (argc == 2) {
|
||||
std::function<void()> arg0;
|
||||
do {
|
||||
if (args[0].isObject() && args[0].toObject()->isFunction())
|
||||
{
|
||||
se::Value jsThis(args[1]);
|
||||
se::Value jsFunc(args[0]);
|
||||
|
||||
jsThis.toObject()->attachObject(jsFunc.toObject());
|
||||
|
||||
auto lambda = [=]() -> void {
|
||||
...
|
||||
// Call jsFunc stuff...
|
||||
...
|
||||
};
|
||||
arg0 = lambda;
|
||||
}
|
||||
else
|
||||
{
|
||||
arg0 = nullptr;
|
||||
}
|
||||
} while(false);
|
||||
SE_PRECONDITION2(ok, false, "Error processing arguments");
|
||||
cobj->setCallback(arg0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
```
|
||||
*/
|
||||
bool attachObject(Object* obj);
|
||||
|
||||
/**
|
||||
* @brief Detaches an object from current object.
|
||||
* @param[in] obj The object to be detached.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note The attached object will not be released if current object is not garbage collected.
|
||||
*/
|
||||
bool detachObject(Object* obj);
|
||||
|
||||
/**
|
||||
* @brief Returns the string for describing current object.
|
||||
* @return The string for describing current object.
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
// Private API used in wrapper
|
||||
static Object* _createJSObject(Class* cls, JSObjectRef obj);
|
||||
JSObjectRef _getJSObject() const;
|
||||
Class* _getClass() const;
|
||||
void _setFinalizeCallback(JSObjectFinalizeCallback finalizeCb);
|
||||
|
||||
void _cleanup(void* nativeObject = nullptr);
|
||||
bool _isNativeFunction() const;
|
||||
//
|
||||
|
||||
private:
|
||||
static void setContext(JSContextRef cx);
|
||||
static void cleanup();
|
||||
|
||||
Object();
|
||||
virtual ~Object();
|
||||
|
||||
bool init(Class* cls, JSObjectRef obj);
|
||||
|
||||
enum class Type : char
|
||||
{
|
||||
UNKNOWN,
|
||||
PLAIN,
|
||||
ARRAY,
|
||||
ARRAY_BUFFER,
|
||||
TYPED_ARRAY_INT8,
|
||||
TYPED_ARRAY_INT16,
|
||||
TYPED_ARRAY_INT32,
|
||||
TYPED_ARRAY_UINT8,
|
||||
TYPED_ARRAY_UINT8_CLAMPED,
|
||||
TYPED_ARRAY_UINT16,
|
||||
TYPED_ARRAY_UINT32,
|
||||
TYPED_ARRAY_FLOAT32,
|
||||
TYPED_ARRAY_FLOAT64,
|
||||
FUNCTION
|
||||
};
|
||||
|
||||
Class* _cls;
|
||||
JSObjectRef _obj;
|
||||
void* _privateData;
|
||||
JSObjectFinalizeCallback _finalizeCb;
|
||||
|
||||
uint32_t _rootCount;
|
||||
uint32_t _currentVMId;
|
||||
#if SE_DEBUG > 0
|
||||
public:
|
||||
uint32_t _id;
|
||||
private:
|
||||
#endif
|
||||
bool _isCleanup;
|
||||
|
||||
mutable Type _type;
|
||||
|
||||
friend class ScriptEngine;
|
||||
friend class AutoHandleScope;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
1136
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Object.mm
Normal file
1136
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Object.mm
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
namespace se {
|
||||
|
||||
bool isSupportTypedArrayAPI();
|
||||
bool isSupportArrayTestAPI();
|
||||
|
||||
} // namespace se
|
||||
@@ -0,0 +1,81 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "PlatformUtils.h"
|
||||
#include "../config.hpp"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIKit.h>
|
||||
#elif TARGET_OS_MAC
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
|
||||
namespace se {
|
||||
|
||||
bool isSupportTypedArrayAPI()
|
||||
{
|
||||
static bool isSupported = false;
|
||||
static bool isInited = false;
|
||||
if (!isInited)
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
float version = [[UIDevice currentDevice].systemVersion floatValue];
|
||||
isSupported = (version >= 10.0f);
|
||||
#elif TARGET_OS_MAC
|
||||
NSOperatingSystemVersion minimumSupportedOSVersion = { .majorVersion = 10, .minorVersion = 12, .patchVersion = 0 };
|
||||
isSupported = [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:minimumSupportedOSVersion] ? true : false;
|
||||
#else
|
||||
SE_LOGE("isSupportTypedArrayAPI: Unknown system!");
|
||||
#endif
|
||||
isInited = true;
|
||||
}
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
bool isSupportArrayTestAPI()
|
||||
{
|
||||
static bool isSupported = false;
|
||||
static bool isInited = false;
|
||||
if (!isInited)
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
float version = [[UIDevice currentDevice].systemVersion floatValue];
|
||||
isSupported = (version >= 9.0f);
|
||||
#elif TARGET_OS_MAC
|
||||
NSOperatingSystemVersion minimumSupportedOSVersion = { .majorVersion = 10, .minorVersion = 11, .patchVersion = 0 };
|
||||
isSupported = [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:minimumSupportedOSVersion] ? true : false;
|
||||
#else
|
||||
SE_LOGE("isSupportArrayTestAPI: Unknown system!");
|
||||
#endif
|
||||
isInited = true;
|
||||
}
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
@@ -0,0 +1,331 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
#include "Base.h"
|
||||
#include <thread>
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
class Class;
|
||||
class Value;
|
||||
|
||||
extern Class* __jsb_CCPrivateData_class;
|
||||
|
||||
/**
|
||||
* A stack-allocated class that governs a number of local handles.
|
||||
* It's only implemented for v8 wrapper now.
|
||||
* Other script engine wrappers have empty implementation for this class.
|
||||
* It's used at the beginning of executing any wrapper API.
|
||||
*/
|
||||
class AutoHandleScope
|
||||
{
|
||||
public:
|
||||
AutoHandleScope();
|
||||
~AutoHandleScope();
|
||||
};
|
||||
|
||||
/**
|
||||
* ScriptEngine is a sington which represents a context of JavaScript VM.
|
||||
*/
|
||||
class ScriptEngine final
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Gets or creates the instance of script engine.
|
||||
* @return The script engine instance.
|
||||
*/
|
||||
static ScriptEngine* getInstance();
|
||||
|
||||
/**
|
||||
* @brief Destroys the instance of script engine.
|
||||
*/
|
||||
static void destroyInstance();
|
||||
|
||||
/**
|
||||
* @brief Gets the global object of JavaScript VM.
|
||||
* @return The se::Object stores the global JavaScript object.
|
||||
*/
|
||||
Object* getGlobalObject();
|
||||
|
||||
typedef bool (*RegisterCallback)(Object*);
|
||||
|
||||
/**
|
||||
* @brief Adds a callback for registering a native binding module.
|
||||
* @param[in] cb A callback for registering a native binding module.
|
||||
* @note This method just add a callback to a vector, callbacks is invoked in `start` method.
|
||||
*/
|
||||
void addRegisterCallback(RegisterCallback cb);
|
||||
|
||||
/**
|
||||
* @brief Starts the script engine.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note This method will invoke all callbacks of native binding modules by the order of registration.
|
||||
*/
|
||||
bool start();
|
||||
|
||||
/**
|
||||
* @brief Initializes script engine.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note This method will create JavaScript context and global object.
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function before initializing script engine.
|
||||
* @param[in] hook A hook function to be invoked before initializing script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addBeforeInitHook(const std::function<void()>& hook);
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function after initializing script engine.
|
||||
* @param[in] hook A hook function to be invoked before initializing script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addAfterInitHook(const std::function<void()>& hook);
|
||||
|
||||
/**
|
||||
* @brief Cleanups script engine.
|
||||
* @note This method will removes all objects in JavaScript VM even whose are rooted, then shutdown JavaScript VMf.
|
||||
*/
|
||||
void cleanup();
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function before cleanuping script engine.
|
||||
* @param[in] hook A hook function to be invoked before cleanuping script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addBeforeCleanupHook(const std::function<void()>& hook);
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function after cleanuping script engine.
|
||||
* @param[in] hook A hook function to be invoked after cleanuping script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addAfterCleanupHook(const std::function<void()>& hook);
|
||||
|
||||
/**
|
||||
* @brief Executes a utf-8 string buffer which contains JavaScript code.
|
||||
* @param[in] scriptStr A utf-8 string buffer, if it isn't null-terminated, parameter `length` should be assigned and > 0.
|
||||
* @param[in] length The length of parameter `scriptStr`, it will be set to string length internally if passing < 0 and parameter `scriptStr` is null-terminated.
|
||||
* @param[in] rval The se::Value that results from evaluating script. Passing nullptr if you don't care about the result.
|
||||
* @param[in] fileName A string containing a URL for the script's source file. This is used by debuggers and when reporting exceptions. Pass NULL if you do not care to include source file information.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool evalString(const char* scriptStr, ssize_t length = -1, Value* rval = nullptr, const char* fileName = nullptr);
|
||||
|
||||
/**
|
||||
* Delegate class for file operation
|
||||
*/
|
||||
class FileOperationDelegate
|
||||
{
|
||||
public:
|
||||
FileOperationDelegate()
|
||||
: onGetDataFromFile(nullptr)
|
||||
, onGetStringFromFile(nullptr)
|
||||
, onCheckFileExist(nullptr)
|
||||
, onGetFullPath(nullptr)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Tests whether delegate is valid.
|
||||
*/
|
||||
bool isValid() const {
|
||||
return onGetDataFromFile != nullptr
|
||||
&& onGetStringFromFile != nullptr
|
||||
&& onCheckFileExist != nullptr
|
||||
&& onGetFullPath != nullptr; }
|
||||
|
||||
// path, buffer, buffer size
|
||||
std::function<void(const std::string&, const std::function<void(const uint8_t*, size_t)>& )> onGetDataFromFile;
|
||||
// path, return file string content.
|
||||
std::function<std::string(const std::string&)> onGetStringFromFile;
|
||||
// path
|
||||
std::function<bool(const std::string&)> onCheckFileExist;
|
||||
// path, return full path
|
||||
std::function<std::string(const std::string&)> onGetFullPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Sets the delegate for file operation.
|
||||
* @param delegate[in] The delegate instance for file operation.
|
||||
*/
|
||||
void setFileOperationDelegate(const FileOperationDelegate& delegate);
|
||||
|
||||
/**
|
||||
* @brief Gets the delegate for file operation.
|
||||
* @return The delegate for file operation
|
||||
*/
|
||||
const FileOperationDelegate& getFileOperationDelegate() const;
|
||||
|
||||
/**
|
||||
* @brief Executes a file which contains JavaScript code.
|
||||
* @param[in] path Script file path.
|
||||
* @param[in] rval The se::Value that results from evaluating script. Passing nullptr if you don't care about the result.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool runScript(const std::string& path, Value* rval = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Tests whether script engine is doing garbage collection.
|
||||
* @return true if it's in garbage collection, otherwise false.
|
||||
*/
|
||||
bool isGarbageCollecting();
|
||||
|
||||
/**
|
||||
* @brief Performs a JavaScript garbage collection.
|
||||
*/
|
||||
void garbageCollect();
|
||||
|
||||
/**
|
||||
* @brief Tests whether script engine is being cleaned up.
|
||||
* @return true if it's in cleaning up, otherwise false.
|
||||
*/
|
||||
bool isInCleanup() { return _isInCleanup; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether script engine is valid.
|
||||
* @return true if it's valid, otherwise false.
|
||||
*/
|
||||
bool isValid() { return _isValid; }
|
||||
|
||||
/**
|
||||
* @brief Clears all exceptions.
|
||||
*/
|
||||
void clearException();
|
||||
|
||||
using ExceptionCallback = std::function<void(const char*, const char*, const char*)>; // location, message, stack
|
||||
|
||||
/**
|
||||
* @brief Sets the callback function while an exception is fired.
|
||||
* @param[in] cb The callback function to notify that an exception is fired.
|
||||
*/
|
||||
void setExceptionCallback(const ExceptionCallback& cb);
|
||||
|
||||
/**
|
||||
* @brief Sets the callback function while an exception is fired in JS.
|
||||
* @param[in] cb The callback function to notify that an exception is fired.
|
||||
*/
|
||||
void setJSExceptionCallback(const ExceptionCallback& cb);
|
||||
/**
|
||||
* @brief Gets the start time of script engine.
|
||||
* @return The start time of script engine.
|
||||
*/
|
||||
const std::chrono::steady_clock::time_point& getStartTime() const { return _startTime; }
|
||||
|
||||
/**
|
||||
* @brief Enables JavaScript debugger
|
||||
* @param[in] serverAddr The address of debugger server.
|
||||
* @param[in] port The port of debugger server will use.
|
||||
* @param[in] isWait Whether wait debugger attach when loading.
|
||||
*/
|
||||
void enableDebugger(const std::string& serverAddr, uint32_t port, bool isWait = false);
|
||||
|
||||
/**
|
||||
* @brief Tests whether JavaScript debugger is enabled
|
||||
* @return true if JavaScript debugger is enabled, otherwise false.
|
||||
*/
|
||||
bool isDebuggerEnabled() const;
|
||||
|
||||
/**
|
||||
* @brief Main loop update trigger, it's need to invoked in main thread every frame.
|
||||
*/
|
||||
void mainLoopUpdate();
|
||||
|
||||
/**
|
||||
* @brief Gets script virtual machine instance ID. Default value is 1, increase by 1 if `init` is invoked.
|
||||
*/
|
||||
uint32_t getVMId() const { return _vmId; }
|
||||
|
||||
// Private API used in wrapper
|
||||
void _clearException(JSValueRef exception);
|
||||
JSContextRef _getContext() const { return _cx; }
|
||||
void _setGarbageCollecting(bool isGarbageCollecting);
|
||||
//
|
||||
private:
|
||||
|
||||
ScriptEngine();
|
||||
~ScriptEngine();
|
||||
|
||||
struct ExceptionInfo
|
||||
{
|
||||
std::string location;
|
||||
std::string message;
|
||||
std::string stack;
|
||||
|
||||
// For compatibility
|
||||
std::string filePath;
|
||||
uint32_t lineno;
|
||||
|
||||
ExceptionInfo()
|
||||
: lineno(0)
|
||||
{}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !message.empty();
|
||||
}
|
||||
};
|
||||
ExceptionInfo _formatException(JSValueRef exception);
|
||||
|
||||
void callExceptionCallback(const char*, const char*, const char*);
|
||||
|
||||
std::chrono::steady_clock::time_point _startTime;
|
||||
std::vector<RegisterCallback> _registerCallbackArray;
|
||||
std::vector<std::function<void()>> _beforeInitHookArray;
|
||||
std::vector<std::function<void()>> _afterInitHookArray;
|
||||
|
||||
std::vector<std::function<void()>> _beforeCleanupHookArray;
|
||||
std::vector<std::function<void()>> _afterCleanupHookArray;
|
||||
|
||||
JSGlobalContextRef _cx;
|
||||
|
||||
Object* _globalObj;
|
||||
FileOperationDelegate _fileOperationDelegate;
|
||||
ExceptionCallback _nativeExceptionCallback = nullptr;
|
||||
ExceptionCallback _jsExceptionCallback = nullptr;
|
||||
|
||||
uint32_t _vmId;
|
||||
|
||||
std::thread::id _engineThreadId;
|
||||
|
||||
bool _isGarbageCollecting;
|
||||
bool _isValid;
|
||||
bool _isInCleanup;
|
||||
bool _isErrorHandleWorking;
|
||||
bool _isDebuggerEnabled;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
|
||||
@@ -0,0 +1,694 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "ScriptEngine.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
#include "Object.hpp"
|
||||
#include "Class.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "../State.hpp"
|
||||
#include "../MappingUtils.hpp"
|
||||
#include "PlatformUtils.h"
|
||||
|
||||
#import "EJConvertTypedArray.h"
|
||||
|
||||
#if SE_DEBUG > 0
|
||||
extern "C" JS_EXPORT void JSSynchronousGarbageCollectForDebugging(JSContextRef);
|
||||
#endif
|
||||
|
||||
namespace se {
|
||||
|
||||
AutoHandleScope::AutoHandleScope()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AutoHandleScope::~AutoHandleScope()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Class* __jsb_CCPrivateData_class = nullptr;
|
||||
//
|
||||
namespace {
|
||||
|
||||
ScriptEngine* __instance = nullptr;
|
||||
|
||||
JSValueRef __forceGC(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
|
||||
size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
|
||||
{
|
||||
if (__instance != nullptr)
|
||||
{
|
||||
__instance->garbageCollect();
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
JSValueRef __log(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
|
||||
size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
|
||||
{
|
||||
if (argumentCount > 0)
|
||||
{
|
||||
std::string ret;
|
||||
internal::forceConvertJsValueToStdString(ctx, arguments[0], &ret);
|
||||
SE_LOGD("JS: %s\n", ret.c_str());
|
||||
}
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
JSObjectRef privateDataContructor(JSContextRef ctx, JSObjectRef constructor, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void privateDataFinalize(JSObjectRef obj)
|
||||
{
|
||||
internal::PrivateData* p = (internal::PrivateData*)JSObjectGetPrivate(obj);
|
||||
JSObjectSetPrivate(obj, p->data);
|
||||
if (p->finalizeCb != nullptr)
|
||||
p->finalizeCb(obj);
|
||||
free(p);
|
||||
}
|
||||
|
||||
Value __consoleVal;
|
||||
Value __oldConsoleLog;
|
||||
Value __oldConsoleDebug;
|
||||
Value __oldConsoleInfo;
|
||||
Value __oldConsoleWarn;
|
||||
Value __oldConsoleError;
|
||||
Value __oldConsoleAssert;
|
||||
|
||||
bool JSB_console_format_log(State& s, const char* prefix, int msgIndex = 0)
|
||||
{
|
||||
if (msgIndex < 0)
|
||||
return false;
|
||||
|
||||
const auto& args = s.args();
|
||||
int argc = (int)args.size();
|
||||
if ((argc - msgIndex) == 1)
|
||||
{
|
||||
std::string msg = args[msgIndex].toStringForce();
|
||||
SE_LOGD("JS: %s%s\n", prefix, msg.c_str());
|
||||
}
|
||||
else if (argc > 1)
|
||||
{
|
||||
std::string msg = args[msgIndex].toStringForce();
|
||||
size_t pos;
|
||||
for (int i = (msgIndex+1); i < argc; ++i)
|
||||
{
|
||||
pos = msg.find("%");
|
||||
if (pos != std::string::npos && pos != (msg.length()-1) && (msg[pos+1] == 'd' || msg[pos+1] == 's' || msg[pos+1] == 'f'))
|
||||
{
|
||||
msg.replace(pos, 2, args[i].toStringForce());
|
||||
}
|
||||
else
|
||||
{
|
||||
msg += " " + args[i].toStringForce();
|
||||
}
|
||||
}
|
||||
|
||||
SE_LOGD("JS: %s%s\n", prefix, msg.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// exist potential crash in iOS, when invoke log to javascript
|
||||
void JSB_invoke_js_log(Value& v, State& s)
|
||||
{
|
||||
#if SE_LOG_TO_JS_ENV
|
||||
v.toObject()->call(s.args(), __consoleVal.toObject());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool JSB_console_log(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "");
|
||||
JSB_invoke_js_log(__oldConsoleLog, s);
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_log)
|
||||
|
||||
bool JSB_console_debug(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[DEBUG]: ");
|
||||
JSB_invoke_js_log(__oldConsoleDebug, s);
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_debug)
|
||||
|
||||
bool JSB_console_info(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[INFO]: ");
|
||||
JSB_invoke_js_log(__oldConsoleInfo, s);
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_info)
|
||||
|
||||
bool JSB_console_warn(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[WARN]: ");
|
||||
JSB_invoke_js_log(__oldConsoleWarn, s);
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_warn)
|
||||
|
||||
bool JSB_console_error(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[ERROR]: ");
|
||||
JSB_invoke_js_log(__oldConsoleError, s);
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_error)
|
||||
|
||||
bool JSB_console_assert(State& s)
|
||||
{
|
||||
const auto& args = s.args();
|
||||
if (!args.empty())
|
||||
{
|
||||
if (args[0].isBoolean() && !args[0].toBoolean())
|
||||
{
|
||||
JSB_console_format_log(s, "[ASSERT]: ", 1);
|
||||
JSB_invoke_js_log(__oldConsoleAssert, s);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_assert)
|
||||
}
|
||||
|
||||
ScriptEngine *ScriptEngine::getInstance()
|
||||
{
|
||||
if (__instance == nullptr)
|
||||
{
|
||||
__instance = new ScriptEngine();
|
||||
}
|
||||
|
||||
return __instance;
|
||||
}
|
||||
|
||||
void ScriptEngine::destroyInstance()
|
||||
{
|
||||
delete __instance;
|
||||
__instance = nullptr;
|
||||
}
|
||||
|
||||
ScriptEngine::ScriptEngine()
|
||||
: _cx(nullptr)
|
||||
, _globalObj(nullptr)
|
||||
, _vmId(0)
|
||||
, _isGarbageCollecting(false)
|
||||
, _isValid(false)
|
||||
, _isInCleanup(false)
|
||||
, _isErrorHandleWorking(false)
|
||||
, _isDebuggerEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool ScriptEngine::init()
|
||||
{
|
||||
cleanup();
|
||||
SE_LOGD("Initializing JavaScriptCore \n");
|
||||
++_vmId;
|
||||
|
||||
_engineThreadId = std::this_thread::get_id();
|
||||
|
||||
for (const auto& hook : _beforeInitHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_beforeInitHookArray.clear();
|
||||
|
||||
_cx = JSGlobalContextCreate(nullptr);
|
||||
|
||||
if (nullptr == _cx)
|
||||
return false;
|
||||
|
||||
JSStringRef ctxName = JSStringCreateWithUTF8CString("Cocos2d-x JSB");
|
||||
JSGlobalContextSetName(_cx, ctxName);
|
||||
JSStringRelease(ctxName);
|
||||
|
||||
NativePtrToObjectMap::init();
|
||||
NonRefNativePtrCreatedByCtorMap::init();
|
||||
|
||||
internal::setContext(_cx);
|
||||
Class::setContext(_cx);
|
||||
Object::setContext(_cx);
|
||||
|
||||
JSObjectRef globalObj = JSContextGetGlobalObject(_cx);
|
||||
|
||||
if (nullptr == globalObj)
|
||||
return false;
|
||||
|
||||
_globalObj = Object::_createJSObject(nullptr, globalObj);
|
||||
_globalObj->root();
|
||||
_globalObj->setProperty("window", Value(_globalObj));
|
||||
|
||||
if (!isSupportTypedArrayAPI())
|
||||
EJJSContextPrepareTypedArrayAPI(_cx);
|
||||
|
||||
if (_globalObj->getProperty("console", &__consoleVal) && __consoleVal.isObject())
|
||||
{
|
||||
#if SE_LOG_TO_JS_ENV
|
||||
__consoleVal.toObject()->getProperty("log", &__oldConsoleLog);
|
||||
__consoleVal.toObject()->getProperty("debug", &__oldConsoleDebug);
|
||||
__consoleVal.toObject()->getProperty("info", &__oldConsoleInfo);
|
||||
__consoleVal.toObject()->getProperty("warn", &__oldConsoleWarn);
|
||||
__consoleVal.toObject()->getProperty("error", &__oldConsoleError);
|
||||
__consoleVal.toObject()->getProperty("assert", &__oldConsoleAssert);
|
||||
#endif
|
||||
__consoleVal.toObject()->defineFunction("log", _SE(JSB_console_log));
|
||||
__consoleVal.toObject()->defineFunction("debug", _SE(JSB_console_debug));
|
||||
__consoleVal.toObject()->defineFunction("info", _SE(JSB_console_info));
|
||||
__consoleVal.toObject()->defineFunction("warn", _SE(JSB_console_warn));
|
||||
__consoleVal.toObject()->defineFunction("error", _SE(JSB_console_error));
|
||||
__consoleVal.toObject()->defineFunction("assert", _SE(JSB_console_assert));
|
||||
}
|
||||
|
||||
_globalObj->setProperty("scriptEngineType", Value("JavaScriptCore"));
|
||||
|
||||
JSStringRef propertyName = JSStringCreateWithUTF8CString("log");
|
||||
JSObjectSetProperty(_cx, globalObj, propertyName, JSObjectMakeFunctionWithCallback(_cx, propertyName, __log), kJSPropertyAttributeReadOnly, nullptr);
|
||||
JSStringRelease(propertyName);
|
||||
|
||||
propertyName = JSStringCreateWithUTF8CString("forceGC");
|
||||
JSObjectSetProperty(_cx, globalObj, propertyName, JSObjectMakeFunctionWithCallback(_cx, propertyName, __forceGC), kJSPropertyAttributeReadOnly, nullptr);
|
||||
JSStringRelease(propertyName);
|
||||
|
||||
__jsb_CCPrivateData_class = Class::create("__PrivateData", _globalObj, nullptr, privateDataContructor);
|
||||
__jsb_CCPrivateData_class->defineFinalizeFunction(privateDataFinalize);
|
||||
__jsb_CCPrivateData_class->install();
|
||||
|
||||
_isValid = true;
|
||||
|
||||
for (const auto& hook : _afterInitHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_afterInitHookArray.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ScriptEngine::~ScriptEngine()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void ScriptEngine::cleanup()
|
||||
{
|
||||
if (!_isValid)
|
||||
return;
|
||||
|
||||
SE_LOGD("ScriptEngine::cleanup begin ...\n");
|
||||
_isInCleanup = true;
|
||||
for (const auto& hook : _beforeCleanupHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_beforeCleanupHookArray.clear();
|
||||
|
||||
SAFE_DEC_REF(_globalObj);
|
||||
Object::cleanup();
|
||||
garbageCollect();
|
||||
|
||||
__consoleVal.setUndefined();
|
||||
__oldConsoleLog.setUndefined();
|
||||
__oldConsoleDebug.setUndefined();
|
||||
__oldConsoleInfo.setUndefined();
|
||||
__oldConsoleWarn.setUndefined();
|
||||
__oldConsoleError.setUndefined();
|
||||
__oldConsoleAssert.setUndefined();
|
||||
|
||||
JSGlobalContextRelease(_cx);
|
||||
|
||||
Class::cleanup();
|
||||
|
||||
_cx = nullptr;
|
||||
_globalObj = nullptr;
|
||||
_isValid = false;
|
||||
|
||||
_registerCallbackArray.clear();
|
||||
|
||||
for (const auto& hook : _afterCleanupHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_afterCleanupHookArray.clear();
|
||||
_isInCleanup = false;
|
||||
|
||||
NativePtrToObjectMap::destroy();
|
||||
NonRefNativePtrCreatedByCtorMap::destroy();
|
||||
SE_LOGD("ScriptEngine::cleanup end ...\n");
|
||||
}
|
||||
|
||||
ScriptEngine::ExceptionInfo ScriptEngine::_formatException(JSValueRef exception)
|
||||
{
|
||||
ExceptionInfo ret;
|
||||
|
||||
// Ignore Exception in forceConvertJsValueToStdString invocation to avoid infinite loop.
|
||||
internal::forceConvertJsValueToStdString(_cx, exception, &ret.message, true);
|
||||
|
||||
JSType type = JSValueGetType(_cx, exception);
|
||||
|
||||
if (type == kJSTypeObject)
|
||||
{
|
||||
JSObjectRef obj = JSValueToObject(_cx, exception, nullptr);
|
||||
JSStringRef stackKey = JSStringCreateWithUTF8CString("stack");
|
||||
JSValueRef stackVal = JSObjectGetProperty(_cx, obj, stackKey, nullptr);
|
||||
if (stackKey != nullptr)
|
||||
{
|
||||
type = JSValueGetType(_cx, stackVal);
|
||||
if (type == kJSTypeString)
|
||||
{
|
||||
JSStringRef stackStr = JSValueToStringCopy(_cx, stackVal, nullptr);
|
||||
internal::jsStringToStdString(_cx, stackStr, &ret.stack);
|
||||
JSStringRelease(stackStr);
|
||||
}
|
||||
JSStringRelease(stackKey);
|
||||
}
|
||||
|
||||
std::string line;
|
||||
std::string column;
|
||||
std::string filePath;
|
||||
JSPropertyNameArrayRef nameArr = JSObjectCopyPropertyNames(_cx, obj);
|
||||
size_t count =JSPropertyNameArrayGetCount(nameArr);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
JSStringRef jsName = JSPropertyNameArrayGetNameAtIndex(nameArr, i);
|
||||
JSValueRef jsValue = JSObjectGetProperty(_cx, obj, jsName, nullptr);
|
||||
|
||||
std::string name;
|
||||
internal::jsStringToStdString(_cx, jsName, &name);
|
||||
std::string value;
|
||||
|
||||
JSStringRef jsstr = JSValueToStringCopy(_cx, jsValue, nullptr);
|
||||
internal::jsStringToStdString(_cx, jsstr, &value);
|
||||
JSStringRelease(jsstr);
|
||||
|
||||
if (name == "line")
|
||||
{
|
||||
line = value;
|
||||
ret.lineno = (uint32_t)JSValueToNumber(_cx, jsValue, nullptr);
|
||||
}
|
||||
else if (name == "column")
|
||||
{
|
||||
column = value;
|
||||
}
|
||||
else if (name == "sourceURL")
|
||||
{
|
||||
filePath = value;
|
||||
ret.filePath = value;
|
||||
}
|
||||
}
|
||||
|
||||
ret.location = filePath + ":" + line + ":" + column;
|
||||
JSPropertyNameArrayRelease(nameArr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ScriptEngine::_clearException(JSValueRef exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
ExceptionInfo exceptionInfo = _formatException(exception);
|
||||
if (exceptionInfo.isValid())
|
||||
{
|
||||
std::string exceptionStr = exceptionInfo.message;
|
||||
exceptionStr += ", location: " + exceptionInfo.location;
|
||||
if (!exceptionInfo.stack.empty())
|
||||
{
|
||||
exceptionStr += "\nSTACK:\n" + exceptionInfo.stack;
|
||||
}
|
||||
SE_LOGE("ERROR: %s\n", exceptionStr.c_str());
|
||||
|
||||
callExceptionCallback(exceptionInfo.location.c_str(), exceptionInfo.message.c_str(), exceptionInfo.stack.c_str());
|
||||
|
||||
if (!_isErrorHandleWorking)
|
||||
{
|
||||
_isErrorHandleWorking = true;
|
||||
|
||||
Value errorHandler;
|
||||
if (_globalObj->getProperty("__errorHandler", &errorHandler) && errorHandler.isObject() && errorHandler.toObject()->isFunction())
|
||||
{
|
||||
ValueArray args;
|
||||
args.push_back(Value(exceptionInfo.filePath));
|
||||
args.push_back(Value(exceptionInfo.lineno));
|
||||
args.push_back(Value(exceptionInfo.message));
|
||||
args.push_back(Value(exceptionInfo.stack));
|
||||
errorHandler.toObject()->call(args, _globalObj);
|
||||
}
|
||||
|
||||
_isErrorHandleWorking = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SE_LOGE("ERROR: __errorHandler has exception\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::callExceptionCallback(const char * location, const char * message, const char * stack)
|
||||
{
|
||||
if(_nativeExceptionCallback) {
|
||||
_nativeExceptionCallback(location, message, stack);
|
||||
}
|
||||
if(_jsExceptionCallback) {
|
||||
_jsExceptionCallback(location, message, stack);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::setExceptionCallback(const ExceptionCallback& cb)
|
||||
{
|
||||
_nativeExceptionCallback = cb;
|
||||
}
|
||||
|
||||
void ScriptEngine::setJSExceptionCallback(const ExceptionCallback& cb)
|
||||
{
|
||||
_jsExceptionCallback = cb;
|
||||
}
|
||||
|
||||
bool ScriptEngine::isGarbageCollecting()
|
||||
{
|
||||
return _isGarbageCollecting;
|
||||
}
|
||||
|
||||
void ScriptEngine::_setGarbageCollecting(bool isGarbageCollecting)
|
||||
{
|
||||
_isGarbageCollecting = isGarbageCollecting;
|
||||
}
|
||||
|
||||
Object* ScriptEngine::getGlobalObject()
|
||||
{
|
||||
return _globalObj;
|
||||
}
|
||||
|
||||
void ScriptEngine::addBeforeInitHook(const std::function<void()>& hook)
|
||||
{
|
||||
_beforeInitHookArray.push_back(hook);
|
||||
}
|
||||
|
||||
void ScriptEngine::addAfterInitHook(const std::function<void()>& hook)
|
||||
{
|
||||
_afterInitHookArray.push_back(hook);
|
||||
}
|
||||
|
||||
void ScriptEngine::addBeforeCleanupHook(const std::function<void()>& hook)
|
||||
{
|
||||
_beforeCleanupHookArray.push_back(hook);
|
||||
}
|
||||
|
||||
void ScriptEngine::addAfterCleanupHook(const std::function<void()>& hook)
|
||||
{
|
||||
_afterCleanupHookArray.push_back(hook);
|
||||
}
|
||||
|
||||
void ScriptEngine::addRegisterCallback(RegisterCallback cb)
|
||||
{
|
||||
assert(std::find(_registerCallbackArray.begin(), _registerCallbackArray.end(), cb) == _registerCallbackArray.end());
|
||||
_registerCallbackArray.push_back(cb);
|
||||
}
|
||||
|
||||
bool ScriptEngine::start()
|
||||
{
|
||||
if (!init())
|
||||
return false;
|
||||
|
||||
bool ok = false;
|
||||
_startTime = std::chrono::steady_clock::now();
|
||||
|
||||
for (auto cb : _registerCallbackArray)
|
||||
{
|
||||
ok = cb(_globalObj);
|
||||
assert(ok);
|
||||
if (!ok)
|
||||
break;
|
||||
}
|
||||
|
||||
// After ScriptEngine is started, _registerCallbackArray isn't needed. Therefore, clear it here.
|
||||
_registerCallbackArray.clear();
|
||||
return ok;
|
||||
}
|
||||
|
||||
void ScriptEngine::garbageCollect()
|
||||
{
|
||||
// JSSynchronousGarbageCollectForDebugging is private API in JavaScriptCore framework.
|
||||
// AppStore will reject the apps reference non-public symbols.
|
||||
// Therefore, we use JSSynchronousGarbageCollectForDebugging for easily debugging memory
|
||||
// problems in debug mode, and use public API JSGarbageCollect in release mode to pass
|
||||
// the AppStore's verify check.
|
||||
#if SE_DEBUG > 0
|
||||
SE_LOGD("GC begin ..., (Native -> JS map) count: %d\n", (int)NativePtrToObjectMap::size());
|
||||
JSSynchronousGarbageCollectForDebugging(_cx);
|
||||
SE_LOGD("GC end ..., (Native -> JS map) count: %d\n", (int)NativePtrToObjectMap::size());
|
||||
#else
|
||||
JSGarbageCollect(_cx);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ScriptEngine::evalString(const char* script, ssize_t length/* = -1 */, Value* ret/* = nullptr */, const char* fileName/* = nullptr */)
|
||||
{
|
||||
if(_engineThreadId != std::this_thread::get_id())
|
||||
{
|
||||
// `evalString` should run in main thread
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(script != nullptr);
|
||||
if (length < 0)
|
||||
length = strlen(script);
|
||||
|
||||
if (fileName == nullptr)
|
||||
fileName = "(no filename)";
|
||||
|
||||
// Fix the source url is too long displayed in Safari debugger.
|
||||
std::string sourceUrl = fileName;
|
||||
static const std::string prefixKey = "/temp/quick-scripts/";
|
||||
size_t prefixPos = sourceUrl.find(prefixKey);
|
||||
if (prefixPos != std::string::npos)
|
||||
{
|
||||
sourceUrl = sourceUrl.substr(prefixPos + prefixKey.length());
|
||||
}
|
||||
|
||||
std::string exceptionStr;
|
||||
std::string scriptStr(script, length);
|
||||
|
||||
JSValueRef exception = nullptr;
|
||||
JSStringRef jsSourceUrl = JSStringCreateWithUTF8CString(sourceUrl.c_str());
|
||||
JSStringRef jsScript = JSStringCreateWithUTF8CString(scriptStr.c_str());
|
||||
JSValueRef result = nullptr;
|
||||
|
||||
bool ok = JSCheckScriptSyntax(_cx, jsScript, jsSourceUrl, 1, &exception);;
|
||||
if (ok)
|
||||
{
|
||||
result = JSEvaluateScript(_cx, jsScript, nullptr, jsSourceUrl, 1, &exception);
|
||||
|
||||
if (exception != nullptr)
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exception == nullptr)
|
||||
{
|
||||
SE_LOGD("Unknown syntax error parsing file %s\n", fileName);
|
||||
}
|
||||
}
|
||||
|
||||
JSStringRelease(jsScript);
|
||||
JSStringRelease(jsSourceUrl);
|
||||
|
||||
if (ok)
|
||||
{
|
||||
if (ret != nullptr)
|
||||
internal::jsToSeValue(_cx, result, ret);
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
SE_LOGE("ScriptEngine::evalString script %s, failed!\n", fileName);
|
||||
}
|
||||
|
||||
_clearException(exception);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void ScriptEngine::setFileOperationDelegate(const FileOperationDelegate& delegate)
|
||||
{
|
||||
_fileOperationDelegate = delegate;
|
||||
}
|
||||
|
||||
const ScriptEngine::FileOperationDelegate& ScriptEngine::getFileOperationDelegate() const
|
||||
{
|
||||
return _fileOperationDelegate;
|
||||
}
|
||||
|
||||
bool ScriptEngine::runScript(const std::string& path, Value* ret/* = nullptr */)
|
||||
{
|
||||
assert(!path.empty());
|
||||
assert(_fileOperationDelegate.isValid());
|
||||
|
||||
std::string scriptBuffer = _fileOperationDelegate.onGetStringFromFile(path);
|
||||
|
||||
if (!scriptBuffer.empty())
|
||||
{
|
||||
return evalString(scriptBuffer.c_str(), scriptBuffer.length(), ret, path.c_str());
|
||||
}
|
||||
|
||||
SE_LOGE("ScriptEngine::runScript script %s, buffer is empty!\n", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptEngine::clearException()
|
||||
{
|
||||
//IDEA:
|
||||
}
|
||||
|
||||
void ScriptEngine::enableDebugger(const std::string& serverAddr, uint32_t port, bool isWait)
|
||||
{
|
||||
// empty implementation
|
||||
_isDebuggerEnabled = true;
|
||||
}
|
||||
|
||||
bool ScriptEngine::isDebuggerEnabled() const
|
||||
{
|
||||
return _isDebuggerEnabled;
|
||||
}
|
||||
|
||||
void ScriptEngine::mainLoopUpdate()
|
||||
{
|
||||
// empty implementation
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
33
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/SeApi.h
Normal file
33
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/SeApi.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "ScriptEngine.hpp"
|
||||
#include "Class.hpp"
|
||||
#include "Object.hpp"
|
||||
#include "../Value.hpp"
|
||||
#include "../State.hpp"
|
||||
#include "HelperMacros.h"
|
||||
#include "Utils.hpp"
|
||||
378
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Utils.cpp
Normal file
378
cocos2d-x/cocos/scripting/js-bindings/jswrapper/jsc/Utils.cpp
Normal file
@@ -0,0 +1,378 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "Utils.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
#include "Object.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
#include "../HandleObject.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
JSContextRef __cx = nullptr;
|
||||
}
|
||||
|
||||
void setContext(JSContextRef cx)
|
||||
{
|
||||
__cx = cx;
|
||||
}
|
||||
|
||||
bool defineProperty(Object* obj, const char* name, JSObjectCallAsFunctionCallback jsGetter, JSObjectCallAsFunctionCallback jsSetter)
|
||||
{
|
||||
bool ok = true;
|
||||
Object* globalObject = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value v;
|
||||
ok = globalObject->getProperty("Object", &v);
|
||||
if (!ok || !v.isObject())
|
||||
{
|
||||
SE_LOGD("ERROR: couldn't find Object\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Value definePropertyFunc;
|
||||
ok = v.toObject()->getProperty("defineProperty", &definePropertyFunc);
|
||||
if (!ok || !v.isObject())
|
||||
{
|
||||
SE_LOGD("ERROR: couldn't find Object.defineProperty\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ValueArray args;
|
||||
args.reserve(3);
|
||||
args.push_back(Value(obj));
|
||||
args.push_back(Value(name));
|
||||
|
||||
HandleObject desc(Object::createPlainObject());
|
||||
|
||||
JSObjectRef getter = nullptr;
|
||||
if (jsGetter != nullptr)
|
||||
{
|
||||
getter = JSObjectMakeFunctionWithCallback(__cx, nullptr, jsGetter);
|
||||
}
|
||||
JSObjectRef setter = nullptr;
|
||||
|
||||
if (jsSetter != nullptr)
|
||||
{
|
||||
setter = JSObjectMakeFunctionWithCallback(__cx, nullptr, jsSetter);
|
||||
}
|
||||
|
||||
assert(getter != nullptr || setter != nullptr);
|
||||
|
||||
if (getter != nullptr)
|
||||
{
|
||||
HandleObject getterObj(Object::_createJSObject(nullptr, getter));
|
||||
desc->setProperty("get", Value(getterObj));
|
||||
}
|
||||
|
||||
if (setter != nullptr)
|
||||
{
|
||||
HandleObject setterObj(Object::_createJSObject(nullptr, setter));
|
||||
desc->setProperty("set", Value(setterObj));
|
||||
}
|
||||
|
||||
args.push_back(Value(desc));
|
||||
definePropertyFunc.toObject()->call(args, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void jsToSeArgs(JSContextRef cx, unsigned short argc, const JSValueRef* argv, ValueArray* outArr)
|
||||
{
|
||||
outArr->reserve(argc);
|
||||
for (unsigned short i = 0; i < argc; ++i)
|
||||
{
|
||||
Value v;
|
||||
jsToSeValue(cx, argv[i], &v);
|
||||
outArr->push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsArgs(JSContextRef cx, const ValueArray& args, JSValueRef* outArr)
|
||||
{
|
||||
for (size_t i = 0, len = args.size(); i < len; ++i)
|
||||
{
|
||||
seToJsValue(cx, args[i], &outArr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void jsToSeValue(JSContextRef cx, JSValueRef jsValue, Value* data)
|
||||
{
|
||||
if (JSValueIsNull(cx, jsValue))
|
||||
{
|
||||
data->setNull();
|
||||
}
|
||||
else if (JSValueIsUndefined(cx, jsValue))
|
||||
{
|
||||
data->setUndefined();
|
||||
}
|
||||
else if (JSValueIsNumber(cx, jsValue))
|
||||
{
|
||||
JSValueRef exception = nullptr;
|
||||
double number = JSValueToNumber(cx, jsValue, &exception);
|
||||
if (exception != nullptr)
|
||||
{
|
||||
ScriptEngine::getInstance()->_clearException(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
data->setNumber(number);
|
||||
}
|
||||
}
|
||||
else if (JSValueIsBoolean(cx, jsValue))
|
||||
{
|
||||
data->setBoolean(JSValueToBoolean(cx, jsValue));
|
||||
}
|
||||
else if (JSValueIsString(cx, jsValue))
|
||||
{
|
||||
std::string str;
|
||||
forceConvertJsValueToStdString(cx, jsValue, &str);
|
||||
data->setString(str);
|
||||
}
|
||||
else if (JSValueIsObject(cx, jsValue))
|
||||
{
|
||||
JSValueRef exception = nullptr;
|
||||
JSObjectRef jsobj = JSValueToObject(cx, jsValue, &exception);
|
||||
if (exception != nullptr)
|
||||
{
|
||||
ScriptEngine::getInstance()->_clearException(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
void* nativePtr = internal::getPrivate(jsobj);
|
||||
Object* obj = nullptr;
|
||||
if (nativePtr != nullptr)
|
||||
{
|
||||
obj = Object::getObjectWithPtr(nativePtr);
|
||||
}
|
||||
|
||||
if (obj == nullptr)
|
||||
{
|
||||
obj = Object::_createJSObject(nullptr, jsobj);
|
||||
}
|
||||
data->setObject(obj, true);
|
||||
obj->decRef();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsValue(JSContextRef cx, const Value& v, JSValueRef* jsval)
|
||||
{
|
||||
switch (v.getType()) {
|
||||
case Value::Type::Null:
|
||||
*jsval = JSValueMakeNull(cx);
|
||||
break;
|
||||
|
||||
case Value::Type::Number:
|
||||
*jsval = JSValueMakeNumber(cx, v.toNumber());
|
||||
break;
|
||||
|
||||
case Value::Type::String:
|
||||
{
|
||||
JSStringRef str = JSStringCreateWithUTF8CString(v.toString().c_str());
|
||||
*jsval = JSValueMakeString(cx, str);
|
||||
JSStringRelease(str);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::Boolean:
|
||||
*jsval = JSValueMakeBoolean(cx, v.toBoolean());
|
||||
break;
|
||||
|
||||
case Value::Type::Object:
|
||||
*jsval = v.toObject()->_getJSObject();
|
||||
break;
|
||||
|
||||
default: // Undefined
|
||||
*jsval = JSValueMakeUndefined(cx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void forceConvertJsValueToStdString(JSContextRef cx, JSValueRef jsval, std::string* ret, bool ignoreException/* = false */)
|
||||
{
|
||||
JSValueRef exception = nullptr;
|
||||
JSStringRef jsstr = JSValueToStringCopy(cx, jsval, &exception);
|
||||
if (exception != nullptr)
|
||||
{
|
||||
// NOTE: ignoreException is true in ScriptEngine::_formatException because it's already in the _clearException process.
|
||||
// Adds this flag to avoid infinite loop of _clearException -> _formatException -> forceConvertJsValueToStdString -> _clearException -> ...
|
||||
if (!ignoreException)
|
||||
{
|
||||
ScriptEngine::getInstance()->_clearException(exception);
|
||||
}
|
||||
ret->clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
jsStringToStdString(cx, jsstr, ret);
|
||||
}
|
||||
|
||||
if (jsstr != nullptr)
|
||||
JSStringRelease(jsstr);
|
||||
}
|
||||
|
||||
void jsStringToStdString(JSContextRef cx, JSStringRef jsStr, std::string* ret)
|
||||
{
|
||||
size_t len = JSStringGetLength(jsStr);
|
||||
const size_t BUF_SIZE = len * 4 + 1;
|
||||
char* buf = (char*)malloc(BUF_SIZE);
|
||||
memset(buf, 0, BUF_SIZE);
|
||||
JSStringGetUTF8CString(jsStr, buf, BUF_SIZE);
|
||||
*ret = buf;
|
||||
free(buf);
|
||||
}
|
||||
|
||||
const char* KEY_PRIVATE_DATA = "__cc_private_data";
|
||||
|
||||
bool hasPrivate(JSObjectRef obj)
|
||||
{
|
||||
void* data = JSObjectGetPrivate(obj);
|
||||
if (data != nullptr)
|
||||
return true;
|
||||
|
||||
JSStringRef key = JSStringCreateWithUTF8CString(KEY_PRIVATE_DATA);
|
||||
bool found = JSObjectHasProperty(__cx, obj, key);
|
||||
JSStringRelease(key);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void setPrivate(JSObjectRef obj, void* data, JSObjectFinalizeCallback finalizeCb)
|
||||
{
|
||||
bool ok = JSObjectSetPrivate(obj, data);
|
||||
if (ok)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
assert(finalizeCb);
|
||||
Object* privateObj = Object::createObjectWithClass(__jsb_CCPrivateData_class);
|
||||
privateObj->root();
|
||||
|
||||
internal::PrivateData* privateData = (internal::PrivateData*)malloc(sizeof(internal::PrivateData));
|
||||
privateData->data = data;
|
||||
privateData->finalizeCb = finalizeCb;
|
||||
ok = JSObjectSetPrivate(privateObj->_getJSObject(), privateData);
|
||||
assert(ok);
|
||||
|
||||
JSStringRef key = JSStringCreateWithUTF8CString(KEY_PRIVATE_DATA);
|
||||
JSValueRef exception = nullptr;
|
||||
JSObjectSetProperty(__cx, obj, key, privateObj->_getJSObject(), kJSPropertyAttributeDontEnum, &exception);
|
||||
if (exception != nullptr)
|
||||
{
|
||||
ScriptEngine::getInstance()->_clearException(exception);
|
||||
}
|
||||
JSStringRelease(key);
|
||||
|
||||
privateObj->unroot();
|
||||
privateObj->decRef();
|
||||
}
|
||||
|
||||
void* getPrivate(JSObjectRef obj)
|
||||
{
|
||||
void* data = JSObjectGetPrivate(obj);
|
||||
if (data != nullptr)
|
||||
return data;
|
||||
|
||||
JSStringRef key = JSStringCreateWithUTF8CString(KEY_PRIVATE_DATA);
|
||||
bool found = JSObjectHasProperty(__cx, obj, key);
|
||||
if (found)
|
||||
{
|
||||
JSValueRef exception = nullptr;
|
||||
JSValueRef privateDataVal = JSObjectGetProperty(__cx, obj, key, &exception);
|
||||
do
|
||||
{
|
||||
if (exception != nullptr)
|
||||
break;
|
||||
|
||||
JSObjectRef jsobj = JSValueToObject(__cx, privateDataVal, &exception);
|
||||
if (exception != nullptr)
|
||||
break;
|
||||
|
||||
internal::PrivateData* privateData = (internal::PrivateData*)JSObjectGetPrivate(jsobj);
|
||||
assert(privateData != nullptr);
|
||||
data = privateData->data;
|
||||
} while(false);
|
||||
|
||||
if (exception != nullptr)
|
||||
{
|
||||
ScriptEngine::getInstance()->_clearException(exception);
|
||||
}
|
||||
}
|
||||
JSStringRelease(key);
|
||||
return data;
|
||||
}
|
||||
|
||||
void clearPrivate(JSObjectRef obj)
|
||||
{
|
||||
void* data = JSObjectGetPrivate(obj);
|
||||
if (data != nullptr)
|
||||
{
|
||||
JSObjectSetPrivate(obj, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
JSStringRef key = JSStringCreateWithUTF8CString(KEY_PRIVATE_DATA); //IDEA: cache the key string
|
||||
if (JSObjectHasProperty(__cx, obj, key))
|
||||
{
|
||||
JSValueRef exception = nullptr;
|
||||
do
|
||||
{
|
||||
JSValueRef value = JSObjectGetProperty(__cx, obj, key, &exception);
|
||||
if (exception != nullptr)
|
||||
break;
|
||||
|
||||
JSObjectRef propertyObj = JSValueToObject(__cx, value, &exception);
|
||||
if (exception != nullptr)
|
||||
break;
|
||||
|
||||
internal::PrivateData* privateData = (internal::PrivateData*)JSObjectGetPrivate(propertyObj);
|
||||
free(privateData);
|
||||
JSObjectSetPrivate(propertyObj, nullptr);
|
||||
bool ok = JSObjectDeleteProperty(__cx, obj, key, nullptr);
|
||||
assert(ok);
|
||||
} while (false);
|
||||
|
||||
if (exception != nullptr)
|
||||
{
|
||||
ScriptEngine::getInstance()->_clearException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
JSStringRelease(key);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace se { namespace internal {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
@@ -0,0 +1,65 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2016 Chukong Technologies Inc.
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos2d-x.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
#include "../Value.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct PrivateData
|
||||
{
|
||||
void* data;
|
||||
JSObjectFinalizeCallback finalizeCb;
|
||||
};
|
||||
|
||||
void setContext(JSContextRef cx);
|
||||
|
||||
bool defineProperty(Object* obj, const char* name, JSObjectCallAsFunctionCallback jsGetter, JSObjectCallAsFunctionCallback jsSetter);
|
||||
|
||||
void jsToSeArgs(JSContextRef cx, unsigned short argc, const JSValueRef* argv, ValueArray* outArr);
|
||||
void seToJsArgs(JSContextRef cx, const ValueArray& args, JSValueRef* outArr);
|
||||
void jsToSeValue(JSContextRef cx, JSValueRef jsval, Value* v);
|
||||
void seToJsValue(JSContextRef cx, const Value& v, JSValueRef* jsval);
|
||||
|
||||
void forceConvertJsValueToStdString(JSContextRef cx, JSValueRef jsval, std::string* ret, bool ignoreException = false);
|
||||
void jsStringToStdString(JSContextRef cx, JSStringRef jsStr, std::string* ret);
|
||||
|
||||
bool hasPrivate(JSObjectRef obj);
|
||||
void setPrivate(JSObjectRef obj, void* data, JSObjectFinalizeCallback finalizeCb);
|
||||
void* getPrivate(JSObjectRef obj);
|
||||
void clearPrivate(JSObjectRef obj);
|
||||
|
||||
} // namespace internal {
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
Reference in New Issue
Block a user