mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2026-01-09 23:48:13 +00:00
初始化
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
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 "HandleObject.hpp"
|
||||
#include "Object.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
HandleObject::HandleObject(Object* obj)
|
||||
: _obj(obj)
|
||||
{
|
||||
if (_obj != nullptr)
|
||||
{
|
||||
// se::HandleObject could not be used for native binding object.
|
||||
assert(!_obj->_getClass());
|
||||
_obj->root();
|
||||
}
|
||||
}
|
||||
|
||||
HandleObject::~HandleObject()
|
||||
{
|
||||
if (_obj != nullptr)
|
||||
{
|
||||
_obj->unroot();
|
||||
_obj->decRef();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
113
cocos2d-x/cocos/scripting/js-bindings/jswrapper/HandleObject.hpp
Normal file
113
cocos2d-x/cocos/scripting/js-bindings/jswrapper/HandleObject.hpp
Normal file
@@ -0,0 +1,113 @@
|
||||
/****************************************************************************
|
||||
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 <stddef.h>
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* HandleObject is a helper class for easily release, root and unroot an non-native-binding se::Object.
|
||||
|
||||
{
|
||||
se::HandleObject obj(se::Object::createPlainObject());
|
||||
obj->setProperty(...);
|
||||
otherObject->setProperty("foo", se::Value(obj));
|
||||
}
|
||||
|
||||
is equal to:
|
||||
|
||||
{
|
||||
se::Object* obj = se::Object::createPlainObject();
|
||||
obj->root(); // root after object created to avoid object is garbage collected
|
||||
|
||||
obj->setProperty(...);
|
||||
otherObject->setProperty("foo", se::Value(obj));
|
||||
|
||||
obj->unroot(); // unroot object after obj is used.
|
||||
obj->decRef(); // Decrement referent count to avoid memory leak.
|
||||
}
|
||||
|
||||
HandleObject should not be used to create a native binding object since the created binding object
|
||||
should be holded by JavaScript VM and released in finalize callback internally.
|
||||
|
||||
*/
|
||||
class HandleObject
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor of HandleObject
|
||||
* @param[in] obj The se::Object to attach.
|
||||
*/
|
||||
HandleObject(Object* obj);
|
||||
|
||||
/**
|
||||
* @brief The destructor of HandleObject
|
||||
*/
|
||||
~HandleObject();
|
||||
|
||||
/**
|
||||
* @brief The pointer operator
|
||||
* @return The se::Object attached.
|
||||
*/
|
||||
inline Object* operator->() const
|
||||
{
|
||||
return _obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the se::Object attached.
|
||||
* @return The se::Object attached.
|
||||
*/
|
||||
inline Object* get() const
|
||||
{
|
||||
return _obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests whether HandleObject holds an invalid se::Object.
|
||||
* @return true if HandleObject holds an invalid se::Object, otherwise false.
|
||||
*/
|
||||
inline bool isEmpty() const
|
||||
{
|
||||
return (_obj == nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
HandleObject(const HandleObject&) = delete;
|
||||
void operator=(const HandleObject&) = delete;
|
||||
HandleObject(HandleObject&&) = delete;
|
||||
void operator=(HandleObject&&) = delete;
|
||||
|
||||
void* operator new(size_t size) = delete;
|
||||
void operator delete(void*, size_t) = delete;
|
||||
|
||||
Object* _obj;
|
||||
friend class Object;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
162
cocos2d-x/cocos/scripting/js-bindings/jswrapper/MappingUtils.cpp
Normal file
162
cocos2d-x/cocos/scripting/js-bindings/jswrapper/MappingUtils.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/****************************************************************************
|
||||
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 "MappingUtils.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
// NativePtrToObjectMap
|
||||
NativePtrToObjectMap::Map* NativePtrToObjectMap::__nativePtrToObjectMap = nullptr;
|
||||
|
||||
bool NativePtrToObjectMap::init()
|
||||
{
|
||||
if (__nativePtrToObjectMap == nullptr)
|
||||
__nativePtrToObjectMap = new (std::nothrow) NativePtrToObjectMap::Map();
|
||||
|
||||
return __nativePtrToObjectMap != nullptr;
|
||||
}
|
||||
|
||||
void NativePtrToObjectMap::destroy()
|
||||
{
|
||||
if (__nativePtrToObjectMap != nullptr)
|
||||
{
|
||||
delete __nativePtrToObjectMap;
|
||||
__nativePtrToObjectMap = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NativePtrToObjectMap::emplace(void* nativeObj, Object* seObj)
|
||||
{
|
||||
__nativePtrToObjectMap->emplace(nativeObj, seObj);
|
||||
}
|
||||
|
||||
NativePtrToObjectMap::Map::iterator NativePtrToObjectMap::find(void* nativeObj)
|
||||
{
|
||||
return __nativePtrToObjectMap->find(nativeObj);
|
||||
}
|
||||
|
||||
NativePtrToObjectMap::Map::iterator NativePtrToObjectMap::erase(Map::iterator iter)
|
||||
{
|
||||
return __nativePtrToObjectMap->erase(iter);
|
||||
}
|
||||
|
||||
void NativePtrToObjectMap::erase(void* nativeObj)
|
||||
{
|
||||
__nativePtrToObjectMap->erase(nativeObj);
|
||||
}
|
||||
|
||||
void NativePtrToObjectMap::clear()
|
||||
{
|
||||
__nativePtrToObjectMap->clear();
|
||||
}
|
||||
|
||||
size_t NativePtrToObjectMap::size()
|
||||
{
|
||||
return __nativePtrToObjectMap->size();
|
||||
}
|
||||
|
||||
const NativePtrToObjectMap::Map& NativePtrToObjectMap::instance()
|
||||
{
|
||||
return *__nativePtrToObjectMap;
|
||||
}
|
||||
|
||||
NativePtrToObjectMap::Map::iterator NativePtrToObjectMap::begin()
|
||||
{
|
||||
return __nativePtrToObjectMap->begin();
|
||||
}
|
||||
|
||||
NativePtrToObjectMap::Map::iterator NativePtrToObjectMap::end()
|
||||
{
|
||||
return __nativePtrToObjectMap->end();
|
||||
}
|
||||
|
||||
// NonRefNativePtrCreatedByCtorMap
|
||||
|
||||
NonRefNativePtrCreatedByCtorMap::Map* NonRefNativePtrCreatedByCtorMap::__nonRefNativeObjectCreatedByCtorMap = nullptr;
|
||||
|
||||
bool NonRefNativePtrCreatedByCtorMap::init()
|
||||
{
|
||||
if (__nonRefNativeObjectCreatedByCtorMap == nullptr)
|
||||
__nonRefNativeObjectCreatedByCtorMap = new (std::nothrow) NonRefNativePtrCreatedByCtorMap::Map();
|
||||
|
||||
return __nonRefNativeObjectCreatedByCtorMap != nullptr;
|
||||
}
|
||||
|
||||
void NonRefNativePtrCreatedByCtorMap::destroy()
|
||||
{
|
||||
if (__nonRefNativeObjectCreatedByCtorMap != nullptr)
|
||||
{
|
||||
delete __nonRefNativeObjectCreatedByCtorMap;
|
||||
__nonRefNativeObjectCreatedByCtorMap = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NonRefNativePtrCreatedByCtorMap::emplace(void* nativeObj)
|
||||
{
|
||||
__nonRefNativeObjectCreatedByCtorMap->emplace(nativeObj, true);
|
||||
}
|
||||
|
||||
NonRefNativePtrCreatedByCtorMap::Map::iterator NonRefNativePtrCreatedByCtorMap::find(void* nativeObj)
|
||||
{
|
||||
return __nonRefNativeObjectCreatedByCtorMap->find(nativeObj);
|
||||
}
|
||||
|
||||
NonRefNativePtrCreatedByCtorMap::Map::iterator NonRefNativePtrCreatedByCtorMap::erase(Map::iterator iter)
|
||||
{
|
||||
return __nonRefNativeObjectCreatedByCtorMap->erase(iter);
|
||||
}
|
||||
|
||||
void NonRefNativePtrCreatedByCtorMap::erase(void* nativeObj)
|
||||
{
|
||||
__nonRefNativeObjectCreatedByCtorMap->erase(nativeObj);
|
||||
}
|
||||
|
||||
void NonRefNativePtrCreatedByCtorMap::clear()
|
||||
{
|
||||
__nonRefNativeObjectCreatedByCtorMap->clear();
|
||||
}
|
||||
|
||||
size_t NonRefNativePtrCreatedByCtorMap::size()
|
||||
{
|
||||
return __nonRefNativeObjectCreatedByCtorMap->size();
|
||||
}
|
||||
|
||||
const NonRefNativePtrCreatedByCtorMap::Map& NonRefNativePtrCreatedByCtorMap::instance()
|
||||
{
|
||||
return *__nonRefNativeObjectCreatedByCtorMap;
|
||||
}
|
||||
|
||||
NonRefNativePtrCreatedByCtorMap::Map::iterator NonRefNativePtrCreatedByCtorMap::begin()
|
||||
{
|
||||
return __nonRefNativeObjectCreatedByCtorMap->begin();
|
||||
}
|
||||
|
||||
NonRefNativePtrCreatedByCtorMap::Map::iterator NonRefNativePtrCreatedByCtorMap::end()
|
||||
{
|
||||
return __nonRefNativeObjectCreatedByCtorMap->end();
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace se {
|
||||
@@ -0,0 +1,85 @@
|
||||
/****************************************************************************
|
||||
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 <unordered_map>
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
class NativePtrToObjectMap
|
||||
{
|
||||
public:
|
||||
// key: native ptr, value: se::Object
|
||||
using Map = std::unordered_map<void*, Object*>;
|
||||
|
||||
static bool init();
|
||||
static void destroy();
|
||||
|
||||
static Map::iterator find(void* nativeObj);
|
||||
static Map::iterator erase(Map::iterator iter);
|
||||
static void erase(void* nativeObj);
|
||||
static void clear();
|
||||
static size_t size();
|
||||
|
||||
static const Map& instance();
|
||||
|
||||
static Map::iterator begin();
|
||||
static Map::iterator end();
|
||||
|
||||
private:
|
||||
static void emplace(void* nativeObj, Object* seObj);
|
||||
static Map* __nativePtrToObjectMap;
|
||||
|
||||
friend class Object;
|
||||
};
|
||||
|
||||
class NonRefNativePtrCreatedByCtorMap
|
||||
{
|
||||
public:
|
||||
// key: native ptr, value: non-ref object created by ctor
|
||||
using Map = std::unordered_map<void*, bool>;
|
||||
|
||||
static bool init();
|
||||
static void destroy();
|
||||
|
||||
static void emplace(void* nativeObj);
|
||||
static Map::iterator find(void* nativeObj);
|
||||
static Map::iterator erase(Map::iterator iter);
|
||||
static void erase(void* nativeObj);
|
||||
static void clear();
|
||||
static size_t size();
|
||||
|
||||
static const Map& instance();
|
||||
|
||||
static Map::iterator begin();
|
||||
static Map::iterator end();
|
||||
|
||||
private:
|
||||
static Map* __nonRefNativeObjectCreatedByCtorMap;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
44
cocos2d-x/cocos/scripting/js-bindings/jswrapper/Object.hpp
Normal file
44
cocos2d-x/cocos/scripting/js-bindings/jswrapper/Object.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
#include "sm/Object.hpp"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
#include "v8/Object.hpp"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
#include "jsc/Object.hpp"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
#include "chakracore/Object.hpp"
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/****************************************************************************
|
||||
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 "RefCounter.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
void RefCounter::incRef()
|
||||
{
|
||||
++_refCount;
|
||||
}
|
||||
|
||||
void RefCounter::decRef()
|
||||
{
|
||||
--_refCount;
|
||||
if (_refCount == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int RefCounter::getRefCount()
|
||||
{
|
||||
return _refCount;
|
||||
}
|
||||
|
||||
RefCounter::RefCounter()
|
||||
: _refCount(1)
|
||||
{
|
||||
}
|
||||
|
||||
RefCounter::~RefCounter()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
@@ -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
|
||||
|
||||
namespace se {
|
||||
|
||||
/**
|
||||
* This class is used to manage reference-counting providing a simple interface and a counter.
|
||||
*
|
||||
*/
|
||||
class RefCounter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Increases reference count by one.
|
||||
*/
|
||||
void incRef();
|
||||
|
||||
/**
|
||||
* @brief Decrements the reference count, if it reaches zero, destroys this instance of RefCounter to release its memory.
|
||||
* @note Please note that after calling this function, the caller should absolutely avoid to use the pointer to this instance since it may not be valid anymore.
|
||||
*/
|
||||
void decRef();
|
||||
|
||||
/**
|
||||
* @brief Gets reference count.
|
||||
* @return The reference count.
|
||||
* @note When this goes to zero during a decRef() call, the object will auto-delete itself.
|
||||
*/
|
||||
unsigned int getRefCount();
|
||||
|
||||
protected:
|
||||
// Default constructor
|
||||
// Initialises the internal reference count to 1.
|
||||
RefCounter();
|
||||
// Destructor
|
||||
virtual ~RefCounter();
|
||||
|
||||
private:
|
||||
unsigned int _refCount;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
49
cocos2d-x/cocos/scripting/js-bindings/jswrapper/SeApi.h
Normal file
49
cocos2d-x/cocos/scripting/js-bindings/jswrapper/SeApi.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
#include "sm/SeApi.h"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
#include "v8/SeApi.h"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
#include "jsc/SeApi.h"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
#include "chakracore/SeApi.h"
|
||||
#endif
|
||||
|
||||
#include "Value.hpp"
|
||||
#include "Object.hpp"
|
||||
#include "State.hpp"
|
||||
#include "HandleObject.hpp"
|
||||
#include "MappingUtils.hpp"
|
||||
97
cocos2d-x/cocos/scripting/js-bindings/jswrapper/State.cpp
Normal file
97
cocos2d-x/cocos/scripting/js-bindings/jswrapper/State.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/****************************************************************************
|
||||
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 "State.hpp"
|
||||
#include "Object.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
State::State()
|
||||
: _nativeThisObject(nullptr)
|
||||
, _thisObject(nullptr)
|
||||
, _args(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
State::~State()
|
||||
{
|
||||
SAFE_DEC_REF(_thisObject);
|
||||
}
|
||||
|
||||
State::State(void* nativeThisObject)
|
||||
: _nativeThisObject(nativeThisObject)
|
||||
, _thisObject(nullptr)
|
||||
, _args(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
State::State(void* nativeThisObject, const ValueArray& args)
|
||||
: _nativeThisObject(nativeThisObject)
|
||||
, _thisObject(nullptr)
|
||||
, _args(&args)
|
||||
{
|
||||
}
|
||||
|
||||
State::State(Object* thisObject, const ValueArray& args)
|
||||
: _nativeThisObject(nullptr)
|
||||
, _thisObject(thisObject)
|
||||
, _args(&args)
|
||||
{
|
||||
if (_thisObject != nullptr)
|
||||
{
|
||||
_thisObject->incRef();
|
||||
}
|
||||
}
|
||||
|
||||
void* State::nativeThisObject() const
|
||||
{
|
||||
return _nativeThisObject;
|
||||
}
|
||||
|
||||
const ValueArray& State::args() const
|
||||
{
|
||||
if (_args != nullptr)
|
||||
{
|
||||
return *(_args);
|
||||
}
|
||||
return EmptyValueArray;
|
||||
}
|
||||
|
||||
Object* State::thisObject()
|
||||
{
|
||||
if (nullptr == _thisObject && nullptr != _nativeThisObject)
|
||||
{
|
||||
_thisObject = se::Object::getObjectWithPtr(_nativeThisObject);
|
||||
}
|
||||
// _nativeThisObject in Static method will be nullptr
|
||||
// assert(_thisObject != nullptr);
|
||||
return _thisObject;
|
||||
}
|
||||
|
||||
Value& State::rval()
|
||||
{
|
||||
return _retVal;
|
||||
}
|
||||
} // namespace se {
|
||||
111
cocos2d-x/cocos/scripting/js-bindings/jswrapper/State.hpp
Normal file
111
cocos2d-x/cocos/scripting/js-bindings/jswrapper/State.hpp
Normal file
@@ -0,0 +1,111 @@
|
||||
/****************************************************************************
|
||||
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 "Value.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* State represents an environment while a function or an accesstor is invoked from JavaScript.
|
||||
*/
|
||||
class State final
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Gets void* pointer of `this` object's private data.
|
||||
* @return A void* pointer of `this` object's private data.
|
||||
*/
|
||||
void* nativeThisObject() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the arguments of native binding functions or accesstors.
|
||||
* @return The arguments of native binding functions or accesstors.
|
||||
*/
|
||||
const ValueArray& args() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the JavaScript `this` object wrapped in se::Object.
|
||||
* @return The JavaScript `this` object wrapped in se::Object.
|
||||
*/
|
||||
Object* thisObject();
|
||||
|
||||
/**
|
||||
* @brief Gets the return value reference. Used for setting return value for a function.
|
||||
* @return The return value reference.
|
||||
*/
|
||||
Value& rval();
|
||||
|
||||
// Private API used in wrapper
|
||||
/**
|
||||
* @brief
|
||||
* @param[in]
|
||||
* @return
|
||||
*/
|
||||
State();
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param[in]
|
||||
* @return
|
||||
*/
|
||||
~State();
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param[in]
|
||||
* @return
|
||||
*/
|
||||
State(void* nativeThisObject);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param[in]
|
||||
* @return
|
||||
*/
|
||||
State(void* nativeThisObject, const ValueArray& args);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param[in]
|
||||
* @return
|
||||
*/
|
||||
State(Object* thisObject, const ValueArray& args);
|
||||
private:
|
||||
|
||||
// Disable copy/move constructor, copy/move assigment
|
||||
State(const State&);
|
||||
State(State&&);
|
||||
State& operator=(const State&);
|
||||
State& operator=(State&&);
|
||||
|
||||
void* _nativeThisObject; //weak ref
|
||||
Object* _thisObject; //weak ref
|
||||
const ValueArray* _args; //weak ref
|
||||
Value _retVal; //weak ref
|
||||
};
|
||||
}
|
||||
583
cocos2d-x/cocos/scripting/js-bindings/jswrapper/Value.cpp
Normal file
583
cocos2d-x/cocos/scripting/js-bindings/jswrapper/Value.cpp
Normal file
@@ -0,0 +1,583 @@
|
||||
/****************************************************************************
|
||||
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 "Value.hpp"
|
||||
#include "Object.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
ValueArray EmptyValueArray;
|
||||
|
||||
Value Value::Null = Value(Type::Null);
|
||||
Value Value::Undefined = Value(Type::Undefined);
|
||||
|
||||
Value::Value()
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
memset(&_u, 0, sizeof(_u));
|
||||
}
|
||||
|
||||
Value::Value(Type type)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
memset(&_u, 0, sizeof(_u));
|
||||
reset(type);
|
||||
}
|
||||
|
||||
Value::Value(const Value& v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
*this = v;
|
||||
}
|
||||
|
||||
Value::Value(Value&& v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
*this = std::move(v);
|
||||
}
|
||||
|
||||
Value::Value(bool v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setBoolean(v);
|
||||
}
|
||||
|
||||
Value::Value(int8_t v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setInt8(v);
|
||||
}
|
||||
|
||||
Value::Value(uint8_t v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setUint8(v);
|
||||
}
|
||||
|
||||
Value::Value(int32_t v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setInt32(v);
|
||||
}
|
||||
|
||||
Value::Value(uint32_t v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setUint32(v);
|
||||
}
|
||||
|
||||
Value::Value(int16_t v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setInt16(v);
|
||||
}
|
||||
|
||||
Value::Value(uint16_t v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setUint16(v);
|
||||
}
|
||||
|
||||
Value::Value(long v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setLong(v);
|
||||
}
|
||||
|
||||
Value::Value(unsigned long v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setUlong(v);
|
||||
}
|
||||
|
||||
Value::Value(float v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setFloat(v);
|
||||
}
|
||||
|
||||
Value::Value(double v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setNumber(v);
|
||||
}
|
||||
|
||||
Value::Value(const char* v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setString(v);
|
||||
}
|
||||
|
||||
Value::Value(const std::string& v)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setString(v);
|
||||
}
|
||||
|
||||
Value::Value(Object* o, bool autoRootUnroot/* = false*/)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setObject(o, autoRootUnroot);
|
||||
}
|
||||
|
||||
Value::Value(const HandleObject& o, bool autoRootUnroot/* = false*/)
|
||||
: _type(Type::Undefined)
|
||||
, _autoRootUnroot(false)
|
||||
{
|
||||
setObject(o, autoRootUnroot);
|
||||
}
|
||||
|
||||
Value::~Value()
|
||||
{
|
||||
reset(Type::Undefined);
|
||||
}
|
||||
|
||||
Value& Value::operator=(const Value& v)
|
||||
{
|
||||
if (this != &v)
|
||||
{
|
||||
reset(v.getType());
|
||||
|
||||
switch (_type) {
|
||||
case Type::Null:
|
||||
case Type::Undefined:
|
||||
{
|
||||
memset(&_u, 0, sizeof(_u));
|
||||
break;
|
||||
}
|
||||
case Type::Number:
|
||||
_u._number = v._u._number;
|
||||
break;
|
||||
case Type::String:
|
||||
*_u._string = *v._u._string;
|
||||
break;
|
||||
case Type::Boolean:
|
||||
_u._boolean = v._u._boolean;
|
||||
break;
|
||||
case Type::Object:
|
||||
{
|
||||
setObject(v._u._object, v._autoRootUnroot);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value& Value::operator=(Value&& v)
|
||||
{
|
||||
if (this != &v)
|
||||
{
|
||||
reset(v.getType());
|
||||
|
||||
switch (_type) {
|
||||
case Type::Null:
|
||||
case Type::Undefined:
|
||||
{
|
||||
memset(&_u, 0, sizeof(_u));
|
||||
break;
|
||||
}
|
||||
case Type::Number:
|
||||
_u._number = v._u._number;
|
||||
break;
|
||||
case Type::String:
|
||||
*_u._string = std::move(*v._u._string);
|
||||
break;
|
||||
case Type::Boolean:
|
||||
_u._boolean = v._u._boolean;
|
||||
break;
|
||||
case Type::Object:
|
||||
{
|
||||
if (_u._object != nullptr) // When old value is also an Object, reset will take no effect, therefore, _u._object may not be nullptr.
|
||||
{
|
||||
if (_autoRootUnroot)
|
||||
{
|
||||
_u._object->unroot();
|
||||
}
|
||||
_u._object->decRef();
|
||||
}
|
||||
_u._object = v._u._object;
|
||||
_autoRootUnroot = v._autoRootUnroot;
|
||||
v._u._object = nullptr; // Reset to nullptr here to avoid 'release' operation in v.reset(Type::Undefined) since it's a move operation here.
|
||||
v._autoRootUnroot = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
v.reset(Type::Undefined);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Value& Value::operator=(bool v)
|
||||
// {
|
||||
// setBoolean(v);
|
||||
// return *this;
|
||||
// }
|
||||
//
|
||||
// Value& Value::operator=(double v)
|
||||
// {
|
||||
// setNumber(v);
|
||||
// return *this;
|
||||
// }
|
||||
//
|
||||
// Value& Value::operator=(const std::string& v)
|
||||
// {
|
||||
// setString(v);
|
||||
// return *this;
|
||||
// }
|
||||
//
|
||||
// Value& Value::operator=(Object* o)
|
||||
// {
|
||||
// setObject(o);
|
||||
// return *this;
|
||||
// }
|
||||
//
|
||||
// Value& Value::operator=(const HandleObject& o)
|
||||
// {
|
||||
// setObject(o);
|
||||
// return *this;
|
||||
// }
|
||||
|
||||
void Value::setUndefined()
|
||||
{
|
||||
reset(Type::Undefined);
|
||||
}
|
||||
|
||||
void Value::setNull()
|
||||
{
|
||||
reset(Type::Null);
|
||||
}
|
||||
|
||||
void Value::setBoolean(bool v)
|
||||
{
|
||||
reset(Type::Boolean);
|
||||
_u._boolean = v;
|
||||
}
|
||||
|
||||
void Value::setInt8(int8_t v)
|
||||
{
|
||||
reset(Type::Number);
|
||||
_u._number = (double)v;
|
||||
}
|
||||
|
||||
void Value::setUint8(uint8_t v)
|
||||
{
|
||||
reset(Type::Number);
|
||||
_u._number = (double)v;
|
||||
}
|
||||
|
||||
void Value::setInt32(int32_t v)
|
||||
{
|
||||
reset(Type::Number);
|
||||
_u._number = (double)v;
|
||||
}
|
||||
|
||||
void Value::setUint32(uint32_t v)
|
||||
{
|
||||
reset(Type::Number);
|
||||
_u._number = (double)v;
|
||||
}
|
||||
|
||||
void Value::setInt16(int16_t v)
|
||||
{
|
||||
reset(Type::Number);
|
||||
_u._number = (double)v;
|
||||
}
|
||||
|
||||
void Value::setUint16(uint16_t v)
|
||||
{
|
||||
reset(Type::Number);
|
||||
_u._number = (double)v;
|
||||
}
|
||||
|
||||
void Value::setLong(long v)
|
||||
{
|
||||
reset(Type::Number);
|
||||
_u._number = (double)v;
|
||||
}
|
||||
|
||||
void Value::setUlong(unsigned long v)
|
||||
{
|
||||
reset(Type::Number);
|
||||
_u._number = (double)v;
|
||||
}
|
||||
|
||||
void Value::setFloat(float v)
|
||||
{
|
||||
reset(Type::Number);
|
||||
_u._number = (double)v;
|
||||
}
|
||||
|
||||
void Value::setNumber(double v)
|
||||
{
|
||||
reset(Type::Number);
|
||||
_u._number = v;
|
||||
}
|
||||
|
||||
void Value::setString(const char* v)
|
||||
{
|
||||
if (v != nullptr)
|
||||
{
|
||||
reset(Type::String);
|
||||
*_u._string = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
reset(Type::Null);
|
||||
}
|
||||
}
|
||||
|
||||
void Value::setString(const std::string& v)
|
||||
{
|
||||
reset(Type::String);
|
||||
*_u._string = v;
|
||||
}
|
||||
|
||||
void Value::setObject(Object* object, bool autoRootUnroot/* = false*/)
|
||||
{
|
||||
if (object == nullptr)
|
||||
{
|
||||
reset(Type::Null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_type != Type::Object)
|
||||
{
|
||||
reset(Type::Object);
|
||||
}
|
||||
|
||||
if (_u._object != object)
|
||||
{
|
||||
if (object != nullptr)
|
||||
{
|
||||
object->incRef();
|
||||
if (autoRootUnroot)
|
||||
{
|
||||
object->root();
|
||||
}
|
||||
}
|
||||
|
||||
if (_u._object != nullptr) // When old value is also an Object, reset will take no effect, therefore, _u._object may not be nullptr.
|
||||
{
|
||||
if (_autoRootUnroot)
|
||||
{
|
||||
_u._object->unroot();
|
||||
}
|
||||
_u._object->decRef();
|
||||
}
|
||||
_u._object = object;
|
||||
_autoRootUnroot = autoRootUnroot;
|
||||
}
|
||||
else
|
||||
{
|
||||
_autoRootUnroot = autoRootUnroot;
|
||||
if (_autoRootUnroot)
|
||||
{
|
||||
_u._object->root();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Value::setObject(const HandleObject& o, bool autoRootUnroot/* = false*/)
|
||||
{
|
||||
setObject(o.get(), autoRootUnroot);
|
||||
}
|
||||
|
||||
int8_t Value::toInt8() const
|
||||
{
|
||||
return static_cast<int8_t>(toNumber());
|
||||
}
|
||||
|
||||
uint8_t Value::toUint8() const
|
||||
{
|
||||
return static_cast<uint8_t>(toNumber());
|
||||
}
|
||||
|
||||
int16_t Value::toInt16() const
|
||||
{
|
||||
return static_cast<int16_t>(toNumber());
|
||||
}
|
||||
|
||||
uint16_t Value::toUint16() const
|
||||
{
|
||||
return static_cast<uint16_t>(toNumber());
|
||||
}
|
||||
|
||||
int32_t Value::toInt32() const
|
||||
{
|
||||
return static_cast<int32_t>(toNumber());
|
||||
}
|
||||
|
||||
uint32_t Value::toUint32() const
|
||||
{
|
||||
return static_cast<uint32_t>(toNumber());
|
||||
}
|
||||
|
||||
long Value::toLong() const
|
||||
{
|
||||
return static_cast<long>(toNumber());
|
||||
}
|
||||
|
||||
unsigned long Value::toUlong() const
|
||||
{
|
||||
return static_cast<unsigned long>(toNumber());
|
||||
}
|
||||
|
||||
float Value::toFloat() const
|
||||
{
|
||||
return static_cast<float>(toNumber());
|
||||
}
|
||||
|
||||
double Value::toNumber() const
|
||||
{
|
||||
assert(_type == Type::Number || _type == Type::Boolean);
|
||||
if (_type == Type::Boolean)
|
||||
{
|
||||
if (_u._boolean)
|
||||
return 1.0;
|
||||
else
|
||||
return 0.0;
|
||||
}
|
||||
return _u._number;
|
||||
}
|
||||
|
||||
bool Value::toBoolean() const
|
||||
{
|
||||
assert(_type == Type::Boolean);
|
||||
return _u._boolean;
|
||||
}
|
||||
|
||||
const std::string& Value::toString() const
|
||||
{
|
||||
assert(_type == Type::String);
|
||||
return *_u._string;
|
||||
}
|
||||
|
||||
std::string Value::toStringForce() const
|
||||
{
|
||||
std::string ret;
|
||||
if (_type == Type::String)
|
||||
{
|
||||
ret = *_u._string;
|
||||
}
|
||||
else if (_type == Type::Boolean)
|
||||
{
|
||||
ret = _u._boolean ? "true" : "false";
|
||||
}
|
||||
else if (_type == Type::Number)
|
||||
{
|
||||
char tmp[50] = {0};
|
||||
snprintf(tmp, sizeof(tmp), "%.17g", _u._number);
|
||||
ret = tmp;
|
||||
}
|
||||
else if (_type == Type::Object)
|
||||
{
|
||||
ret = toObject()->toString();
|
||||
}
|
||||
else if (_type == Type::Null)
|
||||
{
|
||||
ret = "null";
|
||||
}
|
||||
else if (_type == Type::Undefined)
|
||||
{
|
||||
ret = "undefined";
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Object* Value::toObject() const
|
||||
{
|
||||
assert(isObject());
|
||||
return _u._object;
|
||||
}
|
||||
|
||||
void Value::reset(Type type)
|
||||
{
|
||||
if (_type != type)
|
||||
{
|
||||
switch (_type) {
|
||||
case Type::String:
|
||||
delete _u._string;
|
||||
break;
|
||||
case Type::Object:
|
||||
{
|
||||
if (_u._object != nullptr)
|
||||
{
|
||||
if (_autoRootUnroot)
|
||||
{
|
||||
_u._object->unroot();
|
||||
}
|
||||
_u._object->decRef();
|
||||
_u._object = nullptr;
|
||||
}
|
||||
|
||||
_autoRootUnroot = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&_u, 0, sizeof(_u));
|
||||
|
||||
switch (type) {
|
||||
case Type::String:
|
||||
_u._string = new std::string();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_type = type;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
421
cocos2d-x/cocos/scripting/js-bindings/jswrapper/Value.hpp
Normal file
421
cocos2d-x/cocos/scripting/js-bindings/jswrapper/Value.hpp
Normal file
@@ -0,0 +1,421 @@
|
||||
/****************************************************************************
|
||||
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 <vector>
|
||||
#include <string>
|
||||
|
||||
#include "HandleObject.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* se::Value represents a JavaScript Value.
|
||||
* It could be undefined, null, number, boolean, string and object which exists in JavaScript.
|
||||
*/
|
||||
class Value final
|
||||
{
|
||||
public:
|
||||
enum class Type : char
|
||||
{
|
||||
Undefined = 0,
|
||||
Null,
|
||||
Number,
|
||||
Boolean,
|
||||
String,
|
||||
Object
|
||||
};
|
||||
|
||||
static Value Null;
|
||||
static Value Undefined;
|
||||
|
||||
/**
|
||||
* @brief The default constructor.
|
||||
*/
|
||||
Value();
|
||||
|
||||
/**
|
||||
* @brief The copy constructor.
|
||||
*/
|
||||
Value(const Value& v);
|
||||
|
||||
/**
|
||||
* @brief The move constructor.
|
||||
*/
|
||||
Value(Value&& v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a boolean arguement.
|
||||
*/
|
||||
explicit Value(bool v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a int8_t arguement.
|
||||
*/
|
||||
explicit Value(int8_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a uint8_t arguement.
|
||||
*/
|
||||
explicit Value(uint8_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a int16_t arguement.
|
||||
*/
|
||||
explicit Value(int16_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a uint16_t arguement.
|
||||
*/
|
||||
explicit Value(uint16_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a int32_t arguement.
|
||||
*/
|
||||
explicit Value(int32_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a uint32_t arguement.
|
||||
*/
|
||||
explicit Value(uint32_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a long arguement.
|
||||
*/
|
||||
explicit Value(long v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a unsigned long arguement.
|
||||
*/
|
||||
explicit Value(unsigned long v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a float arguement.
|
||||
*/
|
||||
explicit Value(float v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a double arguement.
|
||||
*/
|
||||
explicit Value(double v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with an UTF8 null-terminated string argument.
|
||||
*/
|
||||
explicit Value(const char* v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with an UTF8 string argument.
|
||||
*/
|
||||
explicit Value(const std::string& v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with an Object.
|
||||
* @param o A se::Object to be set.
|
||||
* @param[in] autoRootUnroot Whether to root se::Object and unroot it in se::Value's destructor or unroot it while object is replaced. Default value is false.
|
||||
*/
|
||||
explicit Value(Object* o, bool autoRootUnroot = false);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a HandleObject.
|
||||
* @param o A se::HandleObject to be set.
|
||||
* @param[in] autoRootUnroot Whether to root se::HandleObject and unroot it in se::Value's destructor or unroot it while object is replaced. Default value is false.
|
||||
*/
|
||||
explicit Value(const HandleObject& o, bool autoRootUnroot = false);
|
||||
|
||||
/**
|
||||
* @brief The destructor of se::Value
|
||||
*/
|
||||
~Value();
|
||||
|
||||
/**
|
||||
* @brief The copy assignment operator.
|
||||
*/
|
||||
Value& operator=(const Value& v);
|
||||
|
||||
/**
|
||||
* @brief The move assignment operator.
|
||||
*/
|
||||
Value& operator=(Value&& v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to undefined.
|
||||
*/
|
||||
void setUndefined();
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to null.
|
||||
*/
|
||||
void setNull();
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a boolean value.
|
||||
* @param[in] v The boolean value to be set.
|
||||
*/
|
||||
void setBoolean(bool v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a int8_t value.
|
||||
* @param[in] v The int8_t value to be set.
|
||||
*/
|
||||
void setInt8(int8_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a uint8_t value.
|
||||
* @param[in] v The uint8_t value to be set.
|
||||
*/
|
||||
void setUint8(uint8_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a int16_t value.
|
||||
* @param[in] v The int16_t value to be set.
|
||||
*/
|
||||
void setInt16(int16_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a uint16_t value.
|
||||
* @param[in] v The uint16_t value to be set.
|
||||
*/
|
||||
void setUint16(uint16_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a int32_t value.
|
||||
* @param[in] v The int32_t value to be set.
|
||||
*/
|
||||
void setInt32(int32_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a uint32_t value.
|
||||
* @param[in] v The uint32_t value to be set.
|
||||
*/
|
||||
void setUint32(uint32_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a long value.
|
||||
* @param[in] v The long value to be set.
|
||||
*/
|
||||
void setLong(long v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a unsigned long value.
|
||||
* @param[in] v The unsigned long value to be set.
|
||||
*/
|
||||
void setUlong(unsigned long v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a float value.
|
||||
* @param[in] v The float value to be set.
|
||||
*/
|
||||
void setFloat(float v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a double value.
|
||||
* @param[in] v The double value to be set.
|
||||
*/
|
||||
void setNumber(double v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to an UTF8 null-terminated string value.
|
||||
* @param[in] v The UTF8 null-terminated string value to be set.
|
||||
*/
|
||||
void setString(const char* v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to string value.
|
||||
* @param[in] v The string value to be set.
|
||||
*/
|
||||
void setString(const std::string& v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to se::Object value.
|
||||
* @param[in] o The se::Object to be set.
|
||||
* @param[in] autoRootUnroot Whether to root se::Object and unroot it in se::Value's destructor or unroot it while object is replaced. Default value is false.
|
||||
*/
|
||||
void setObject(Object* o, bool autoRootUnroot = false);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to se::HandleObject value.
|
||||
* @param[in] o The se::Object to be set.
|
||||
* @param[in] autoRootUnroot Whether to root se::HandleObject and unroot it in se::Value's destructor or unroot it while object is replaced. Default value is false.
|
||||
*/
|
||||
void setObject(const HandleObject& o, bool autoRootUnroot = false);
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to int8_t.
|
||||
* @return int8_t integer.
|
||||
*/
|
||||
int8_t toInt8() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to uint8_t.
|
||||
* @return uint8_t integer.
|
||||
*/
|
||||
uint8_t toUint8() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to int16_t.
|
||||
* @return int16_t integer.
|
||||
*/
|
||||
int16_t toInt16() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to uint16_t.
|
||||
* @return uint16_t integer.
|
||||
*/
|
||||
uint16_t toUint16() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to int32_t.
|
||||
* @return int32_t integer.
|
||||
*/
|
||||
int32_t toInt32() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to uint32_t.
|
||||
* @return uint32_t integer.
|
||||
*/
|
||||
uint32_t toUint32() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to long.
|
||||
* @return long integer.
|
||||
*/
|
||||
long toLong() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to unsigned long.
|
||||
* @return unsigned long integer.
|
||||
*/
|
||||
unsigned long toUlong() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to float number.
|
||||
* @return float number.
|
||||
*/
|
||||
float toFloat() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to double number.
|
||||
* @return double number.
|
||||
*/
|
||||
double toNumber() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to boolean.
|
||||
* @return boolean.
|
||||
*/
|
||||
bool toBoolean() const;
|
||||
|
||||
/**
|
||||
* @brief Gets std::string if se::Value stores a string. It will trigger an assertion if se::Value isn't a string.
|
||||
* @return A std::string reference.
|
||||
* @see toStringForce
|
||||
*/
|
||||
const std::string& toString() const;
|
||||
|
||||
/**
|
||||
* @brief Converts a se::Value to std::string. Could be invoked even when se::Value isn't a string.
|
||||
* @return A copied std::string value.
|
||||
* @see toString
|
||||
*/
|
||||
std::string toStringForce() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the se::Object pointer if se::Value stores an object. It will trigger an assertion if se::Value isn't a object.
|
||||
* @return A se::Object pointer.
|
||||
*/
|
||||
Object* toObject() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the type of se::Value.
|
||||
* @return The type of se::Value.
|
||||
*/
|
||||
inline Type getType() const { return _type; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a number.
|
||||
* @return true if se::Value stores a number, otherwise false.
|
||||
*/
|
||||
inline bool isNumber() const { return _type == Type::Number; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a string.
|
||||
* @return true if se::Value stores a string, otherwise false.
|
||||
*/
|
||||
inline bool isString() const { return _type == Type::String; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores an object.
|
||||
* @return true if se::Value stores an object, otherwise false.
|
||||
*/
|
||||
inline bool isObject() const { return _type == Type::Object; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a boolean.
|
||||
* @return true if se::Value stores a boolean, otherwise false.
|
||||
*/
|
||||
inline bool isBoolean() const { return _type == Type::Boolean; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores an undefined value.
|
||||
* @return true if se::Value stores an undefined value, otherwise false.
|
||||
*/
|
||||
inline bool isUndefined() const { return _type == Type::Undefined; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a null value.
|
||||
* @return true if se::Value stores a null value, otherwise false.
|
||||
*/
|
||||
inline bool isNull() const { return _type == Type::Null; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a null or an undefined value.
|
||||
* @return true if se::Value stores a null or an undefined value, otherwise false.
|
||||
*/
|
||||
inline bool isNullOrUndefined() const { return (isNull() || isUndefined()); }
|
||||
|
||||
private:
|
||||
explicit Value(Type type);
|
||||
void reset(Type type);
|
||||
|
||||
union {
|
||||
bool _boolean;
|
||||
double _number;
|
||||
std::string* _string;
|
||||
Object* _object;
|
||||
} _u;
|
||||
|
||||
Type _type;
|
||||
bool _autoRootUnroot;
|
||||
};
|
||||
|
||||
using ValueArray = std::vector<Value>;
|
||||
extern ValueArray EmptyValueArray;
|
||||
|
||||
} // namespace se {
|
||||
|
||||
typedef se::Object* se_object_ptr;
|
||||
@@ -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 "ChakraCore.h"
|
||||
#include "ChakraCoreVersion.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <assert.h>
|
||||
|
||||
#include "HelperMacros.h"
|
||||
@@ -0,0 +1,247 @@
|
||||
/****************************************************************************
|
||||
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_CHAKRACORE
|
||||
|
||||
#include "Object.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "State.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
#include "../HandleObject.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
#define JS_FN(name, func) {name, func}
|
||||
#define JS_FS_END JS_FN(0, 0)
|
||||
#define JS_PSGS(name, getter, setter) {name, getter, setter}
|
||||
#define JS_PS_END JS_PSGS(0, 0, 0)
|
||||
|
||||
namespace {
|
||||
// std::unordered_map<std::string, Class *> __clsMap;
|
||||
std::vector<Class*> __allClasses;
|
||||
|
||||
JsValueRef emptyContructor(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
|
||||
{
|
||||
return JS_INVALID_REFERENCE;
|
||||
}
|
||||
|
||||
void defaultFinalizeCallback(void* nativeThisObject)
|
||||
{
|
||||
if (nativeThisObject != nullptr)
|
||||
{
|
||||
State state(nativeThisObject);
|
||||
Object* _thisObject = state.thisObject();
|
||||
if (_thisObject) _thisObject->_cleanup(nativeThisObject);
|
||||
SAFE_DEC_REF(_thisObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Class::Class()
|
||||
: _parent(nullptr)
|
||||
, _proto(nullptr)
|
||||
, _parentProto(nullptr)
|
||||
, _ctor(nullptr)
|
||||
, _finalizeOp(nullptr)
|
||||
{
|
||||
__allClasses.push_back(this);
|
||||
}
|
||||
|
||||
Class::~Class()
|
||||
{
|
||||
}
|
||||
|
||||
Class* Class::create(const std::string& className, Object* obj, Object* parentProto, JsNativeFunction 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, JsNativeFunction ctor)
|
||||
{
|
||||
_name = clsName;
|
||||
_parent = parent;
|
||||
if (_parent != nullptr)
|
||||
_parent->incRef();
|
||||
_parentProto = parentProto;
|
||||
|
||||
if (_parentProto != nullptr)
|
||||
_parentProto->incRef();
|
||||
_ctor = ctor;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::install()
|
||||
{
|
||||
JsValueRef funcName;
|
||||
_CHECK(JsCreateString(_name.c_str(), _name.length(), &funcName));
|
||||
JsValueRef jsConstructor;
|
||||
if (_ctor == nullptr)
|
||||
{
|
||||
_ctor = emptyContructor;
|
||||
}
|
||||
_CHECK(JsCreateNamedFunction(funcName, _ctor, nullptr, &jsConstructor));
|
||||
|
||||
HandleObject ctorObj(Object::_createJSObject(nullptr, jsConstructor));
|
||||
|
||||
// create class's prototype and project its member functions
|
||||
JsValueRef prototype;
|
||||
_CHECK(JsCreateObject(&prototype));
|
||||
|
||||
Object* prototypeObj = Object::_createJSObject(this, prototype);
|
||||
prototypeObj->root();
|
||||
|
||||
for (const auto& func : _funcs)
|
||||
{
|
||||
prototypeObj->defineFunction(func.name, func.func);
|
||||
}
|
||||
|
||||
for (const auto& property : _properties)
|
||||
{
|
||||
internal::defineProperty(prototype, property.name, property.getter, property.setter, true, true);
|
||||
}
|
||||
|
||||
prototypeObj->setProperty("constructor", Value(ctorObj));
|
||||
|
||||
ctorObj->setProperty("prototype", Value(prototypeObj));
|
||||
|
||||
if (_parentProto != nullptr)
|
||||
{
|
||||
_CHECK(JsSetPrototype(prototype, _parentProto->_getJSObject()));
|
||||
}
|
||||
|
||||
for (const auto& sfunc : _staticFuncs)
|
||||
{
|
||||
ctorObj->defineFunction(sfunc.name, sfunc.func);
|
||||
}
|
||||
|
||||
for (const auto& property : _staticProperties)
|
||||
{
|
||||
internal::defineProperty(jsConstructor, property.name, property.getter, property.setter, true, true);
|
||||
}
|
||||
|
||||
_proto = prototypeObj;
|
||||
_parent->setProperty(_name.c_str(), Value(ctorObj));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineFunction(const char *name, JsNativeFunction func)
|
||||
{
|
||||
JSFunctionSpec cb = JS_FN(name, func);
|
||||
_funcs.push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineProperty(const char *name, JsNativeFunction getter, JsNativeFunction setter)
|
||||
{
|
||||
JSPropertySpec property = JS_PSGS(name, getter, setter);
|
||||
_properties.push_back(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticFunction(const char *name, JsNativeFunction func)
|
||||
{
|
||||
JSFunctionSpec cb = JS_FN(name, func);
|
||||
_staticFuncs.push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticProperty(const char *name, JsNativeFunction getter, JsNativeFunction setter)
|
||||
{
|
||||
JSPropertySpec property = JS_PSGS(name, getter, setter);
|
||||
_staticProperties.push_back(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineFinalizeFunction(JsFinalizeCallback func)
|
||||
{
|
||||
_finalizeOp = func;
|
||||
return true;
|
||||
}
|
||||
|
||||
// JsValueRef Class::_createJSObject(const std::string &clsName, Class** outCls)
|
||||
// {
|
||||
// auto iter = __clsMap.find(clsName);
|
||||
// if (iter == __clsMap.end())
|
||||
// {
|
||||
// *outCls = nullptr;
|
||||
// return nullptr;
|
||||
// }
|
||||
//
|
||||
// Class* thiz = iter->second;
|
||||
// *outCls = thiz;
|
||||
// return _createJSObjectWithClass(thiz);
|
||||
// }
|
||||
|
||||
JsValueRef Class::_createJSObjectWithClass(Class* cls)
|
||||
{
|
||||
JsValueRef obj;
|
||||
|
||||
JsFinalizeCallback finalizeCb = cls->_finalizeOp == nullptr ? defaultFinalizeCallback : cls->_finalizeOp;
|
||||
_CHECK(JsCreateExternalObject(nullptr, finalizeCb, &obj));
|
||||
|
||||
_CHECK(JsSetPrototype(obj, cls->getProto()->_getJSObject()));
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Class::getProto()
|
||||
{
|
||||
return _proto;
|
||||
}
|
||||
|
||||
void Class::destroy()
|
||||
{
|
||||
SAFE_DEC_REF(_parent);
|
||||
SAFE_DEC_REF(_proto);
|
||||
SAFE_DEC_REF(_parentProto);
|
||||
}
|
||||
|
||||
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_CHAKRACORE
|
||||
@@ -0,0 +1,160 @@
|
||||
/****************************************************************************
|
||||
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_CHAKRACORE
|
||||
|
||||
#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, JsNativeFunction 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, JsNativeFunction 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, JsNativeFunction getter, JsNativeFunction 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, JsNativeFunction 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, JsNativeFunction getter, JsNativeFunction 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(JsFinalizeCallback 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, JsNativeFunction ctor);
|
||||
void destroy();
|
||||
|
||||
// static JsValueRef _createJSObject(const std::string &clsName, Class** outCls);
|
||||
static JsValueRef _createJSObjectWithClass(Class* cls);
|
||||
|
||||
static void cleanup();
|
||||
|
||||
struct JSFunctionSpec
|
||||
{
|
||||
const char* name;
|
||||
JsNativeFunction func;
|
||||
};
|
||||
|
||||
struct JSPropertySpec
|
||||
{
|
||||
const char* name;
|
||||
JsNativeFunction getter;
|
||||
JsNativeFunction setter;
|
||||
};
|
||||
|
||||
std::string _name;
|
||||
Object* _parent;
|
||||
Object* _proto;
|
||||
Object* _parentProto;
|
||||
|
||||
JsNativeFunction _ctor;
|
||||
|
||||
std::vector<JSFunctionSpec> _funcs;
|
||||
std::vector<JSFunctionSpec> _staticFuncs;
|
||||
std::vector<JSPropertySpec> _properties;
|
||||
std::vector<JSPropertySpec> _staticProperties;
|
||||
JsFinalizeCallback _finalizeOp;
|
||||
|
||||
friend class ScriptEngine;
|
||||
friend class Object;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/****************************************************************************
|
||||
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_CHAKRACORE
|
||||
|
||||
#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(JsValueRef _callee, bool _isConstructCall, JsValueRef* _argv, unsigned short argc, void* _callbackState)
|
||||
|
||||
#define SE_BIND_FUNC(funcName) \
|
||||
JsValueRef funcName##Registry(JsValueRef _callee, bool _isConstructCall, JsValueRef* _argv, unsigned short argc, void* _callbackState) \
|
||||
{ \
|
||||
assert(argc > 0); \
|
||||
--argc; \
|
||||
JsValueRef _jsRet = JS_INVALID_REFERENCE; \
|
||||
bool ret = true; \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(argc, _argv+1, &args); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_argv[0]); \
|
||||
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(state.rval(), &_jsRet); \
|
||||
return _jsRet; \
|
||||
}
|
||||
|
||||
#define SE_BIND_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(void* nativeThisObject) \
|
||||
{ \
|
||||
if (nativeThisObject != nullptr) \
|
||||
{ \
|
||||
auto se = se::ScriptEngine::getInstance(); \
|
||||
se->_setGarbageCollecting(true); \
|
||||
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__); \
|
||||
} \
|
||||
SAFE_DEC_REF(_thisObject); \
|
||||
se->_setGarbageCollecting(false); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SE_DECLARE_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(void* nativeThisObject);
|
||||
|
||||
|
||||
// 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) \
|
||||
JsValueRef funcName##Registry(JsValueRef _callee, bool _isConstructCall, JsValueRef* _argv, unsigned short argc, void* _callbackState) \
|
||||
{ \
|
||||
assert(argc > 0); \
|
||||
--argc; \
|
||||
bool ret = true; \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(argc, _argv+1, &args); \
|
||||
se::Value thisVal(se::Object::createObjectWithClass(cls), true); \
|
||||
se::Object* thisObject = thisVal.toObject(); \
|
||||
JsValueRef _jsRet = thisObject->_getJSObject(); \
|
||||
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_SUB_CLS_CTOR(funcName, cls, finalizeCb) \
|
||||
JsValueRef funcName##Registry(JsValueRef _callee, bool _isConstructCall, JsValueRef* _argv, unsigned short argc, void* _callbackState) \
|
||||
{ \
|
||||
assert(argc > 0); \
|
||||
--argc; \
|
||||
JsValueRef _jsRet = JS_INVALID_REFERENCE; \
|
||||
bool ret = true; \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(argc, _argv+1, &args); \
|
||||
se::Object* thisObject = se::Object::_createJSObject(cls, _argv[0]); \
|
||||
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(JsValueRef _callee, bool _isConstructCall, JsValueRef* _argv, unsigned short _argc, void* _callbackState) \
|
||||
{ \
|
||||
assert(_argc == 1); \
|
||||
JsValueRef _jsRet = JS_INVALID_REFERENCE; \
|
||||
bool ret = true; \
|
||||
void* nativeThisObject = se::internal::getPrivate(_argv[0]); \
|
||||
se::State state(nativeThisObject); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
se::internal::seToJsValue(state.rval(), &_jsRet); \
|
||||
return _jsRet; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_PROP_SET(funcName) \
|
||||
JsValueRef funcName##Registry(JsValueRef _callee, bool _isConstructCall, JsValueRef* _argv, unsigned short _argc, void* _callbackState) \
|
||||
{ \
|
||||
assert(_argc == 2); \
|
||||
bool ret = true; \
|
||||
void* nativeThisObject = se::internal::getPrivate(_argv[0]); \
|
||||
se::Value data; \
|
||||
se::internal::jsToSeValue(_argv[1], &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 JS_INVALID_REFERENCE; \
|
||||
}
|
||||
|
||||
|
||||
#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
|
||||
|
||||
#define _CHECK(cmd) \
|
||||
do \
|
||||
{ \
|
||||
JsErrorCode _errCode = cmd; \
|
||||
if (_errCode != JsNoError) \
|
||||
{ \
|
||||
SE_LOGE("Error 0x%x at '%s, %s, %d'\n", \
|
||||
_errCode, #cmd, __FILE__, __LINE__); \
|
||||
assert(false); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
@@ -0,0 +1,811 @@
|
||||
/****************************************************************************
|
||||
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 "Object.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
|
||||
#include "Utils.hpp"
|
||||
#include "Class.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
#include "../MappingUtils.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
Object::Object()
|
||||
: _cls(nullptr)
|
||||
, _obj(JS_INVALID_REFERENCE)
|
||||
, _privateData(nullptr)
|
||||
, _finalizeCb(nullptr)
|
||||
, _rootCount(0)
|
||||
, _isCleanup(false)
|
||||
{
|
||||
_currentVMId = ScriptEngine::getInstance()->getVMId();
|
||||
}
|
||||
|
||||
Object::~Object()
|
||||
{
|
||||
_cleanup();
|
||||
}
|
||||
|
||||
Object* Object::createPlainObject()
|
||||
{
|
||||
JsValueRef jsobj;
|
||||
_CHECK(JsCreateObject(&jsobj));
|
||||
Object* obj = _createJSObject(nullptr, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createArrayObject(size_t length)
|
||||
{
|
||||
JsValueRef jsObj = JS_INVALID_REFERENCE;
|
||||
_CHECK(JsCreateArray((unsigned int)length, &jsObj));
|
||||
Object* obj = _createJSObject(nullptr, jsObj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createArrayBufferObject(void* data, size_t byteLength)
|
||||
{
|
||||
Object* obj = nullptr;
|
||||
JsValueRef jsobj;
|
||||
_CHECK(JsCreateArrayBuffer((unsigned int)byteLength, &jsobj));
|
||||
ChakraBytePtr buffer = nullptr;
|
||||
unsigned int bufferLength = 0;
|
||||
if (JsNoError == JsGetArrayBufferStorage(jsobj, &buffer, &bufferLength))
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
memcpy((void*)buffer, data, byteLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset((void*)buffer, 0, byteLength);
|
||||
}
|
||||
obj = Object::_createJSObject(nullptr, jsobj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createTypedArray(TypedArrayType type, void* data, size_t byteLength)
|
||||
{
|
||||
if (type == TypedArrayType::NONE)
|
||||
{
|
||||
SE_LOGE("Don't pass se::Object::TypedArrayType::NONE to createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type == TypedArrayType::UINT8_CLAMPED)
|
||||
{
|
||||
SE_LOGE("Doesn't support to create Uint8ClampedArray with Object::createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JsTypedArrayType typedArrayType;
|
||||
size_t elementLength = 0;
|
||||
|
||||
switch (type) {
|
||||
case TypedArrayType::INT8:
|
||||
typedArrayType = JsArrayTypeInt8;
|
||||
elementLength = byteLength;
|
||||
break;
|
||||
case TypedArrayType::INT16:
|
||||
typedArrayType = JsArrayTypeInt16;
|
||||
elementLength = byteLength / 2;
|
||||
break;
|
||||
case TypedArrayType::INT32:
|
||||
typedArrayType = JsArrayTypeInt32;
|
||||
elementLength = byteLength / 4;
|
||||
break;
|
||||
case TypedArrayType::UINT8:
|
||||
typedArrayType = JsArrayTypeUint8;
|
||||
elementLength = byteLength;
|
||||
break;
|
||||
case TypedArrayType::UINT16:
|
||||
typedArrayType = JsArrayTypeUint16;
|
||||
elementLength = byteLength / 2;
|
||||
break;
|
||||
case TypedArrayType::UINT32:
|
||||
typedArrayType = JsArrayTypeUint32;
|
||||
elementLength = byteLength / 4;
|
||||
break;
|
||||
case TypedArrayType::FLOAT32:
|
||||
typedArrayType = JsArrayTypeFloat32;
|
||||
elementLength = byteLength / 4;
|
||||
break;
|
||||
case TypedArrayType::FLOAT64:
|
||||
typedArrayType = JsArrayTypeFloat64;
|
||||
elementLength = byteLength / 8;
|
||||
break;
|
||||
default:
|
||||
assert(false); // Should never go here.
|
||||
break;
|
||||
}
|
||||
|
||||
Object* obj = nullptr;
|
||||
JsValueRef jsobj;
|
||||
_CHECK(JsCreateTypedArray(typedArrayType, JS_INVALID_REFERENCE, 0, (unsigned int)elementLength, &jsobj));
|
||||
ChakraBytePtr buffer = nullptr;
|
||||
unsigned int bufferLength = 0;
|
||||
JsTypedArrayType arrType;
|
||||
int elementSize = 0;
|
||||
if (JsNoError == JsGetTypedArrayStorage(jsobj, &buffer, &bufferLength, &arrType, &elementSize))
|
||||
{
|
||||
//If data has content,then will copy data into buffer,or will only clear buffer.
|
||||
if (data) {
|
||||
memcpy((void*)buffer, data, byteLength);
|
||||
}else{
|
||||
memset((void*)buffer, 0, byteLength);
|
||||
}
|
||||
|
||||
obj = Object::_createJSObject(nullptr, jsobj);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createUint8TypedArray(uint8_t* data, size_t dataCount)
|
||||
{
|
||||
return createTypedArray(TypedArrayType::UINT8, data, dataCount);
|
||||
}
|
||||
|
||||
Object* Object::createJSONObject(const std::string& jsonStr)
|
||||
{
|
||||
bool ok = false;
|
||||
Object* obj = nullptr;
|
||||
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsonVal;
|
||||
ok = global->getProperty("JSON", &jsonVal);
|
||||
assert(ok);
|
||||
|
||||
Value parseVal;
|
||||
ok = jsonVal.toObject()->getProperty("parse", &parseVal);
|
||||
assert(ok);
|
||||
|
||||
Value ret;
|
||||
ValueArray args;
|
||||
args.push_back(Value(jsonStr));
|
||||
if (parseVal.toObject()->call(args, jsonVal.toObject(), &ret))
|
||||
{
|
||||
obj = Object::_createJSObject(nullptr, ret.toObject());
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::getObjectWithPtr(void* ptr)
|
||||
{
|
||||
Object* obj = nullptr;
|
||||
auto iter = NativePtrToObjectMap::find(ptr);
|
||||
if (iter != NativePtrToObjectMap::end())
|
||||
{
|
||||
obj = iter->second;
|
||||
obj->incRef();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::_createJSObject(Class* cls, JsValueRef obj)
|
||||
{
|
||||
Object* ret = new Object();
|
||||
if (!ret->init(obj))
|
||||
{
|
||||
delete ret;
|
||||
ret = nullptr;
|
||||
}
|
||||
|
||||
ret->_cls = cls;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Object* Object::createObjectWithClass(Class* cls)
|
||||
{
|
||||
JsValueRef jsobj = Class::_createJSObjectWithClass(cls);
|
||||
Object* obj = Object::_createJSObject(cls, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool Object::init(JsValueRef obj)
|
||||
{
|
||||
_obj = obj;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Object::_cleanup(void* nativeObject/* = nullptr*/)
|
||||
{
|
||||
if (_isCleanup)
|
||||
return;
|
||||
|
||||
auto se = ScriptEngine::getInstance();
|
||||
if (_currentVMId == se->getVMId())
|
||||
{
|
||||
if (_privateData != nullptr)
|
||||
{
|
||||
if (_obj != nullptr)
|
||||
{
|
||||
if (nativeObject == nullptr)
|
||||
{
|
||||
nativeObject = internal::getPrivate(_obj);
|
||||
}
|
||||
|
||||
if (nativeObject != nullptr)
|
||||
{
|
||||
auto iter = NativePtrToObjectMap::find(nativeObject);
|
||||
if (iter != NativePtrToObjectMap::end())
|
||||
{
|
||||
NativePtrToObjectMap::erase(iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_rootCount > 0)
|
||||
{
|
||||
// Don't unprotect if it's in cleanup, otherwise, it will trigger crash.
|
||||
if (!se->isInCleanup() && !se->isGarbageCollecting())
|
||||
{
|
||||
unsigned int count = 0;
|
||||
_CHECK(JsRelease(_obj, &count));
|
||||
}
|
||||
_rootCount = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SE_LOGD("Object::_cleanup, ScriptEngine was initialized again, ignore cleanup work, oldVMId: %u, newVMId: %u\n", _currentVMId, se->getVMId());
|
||||
}
|
||||
|
||||
_isCleanup = true;
|
||||
}
|
||||
|
||||
void Object::cleanup()
|
||||
{
|
||||
ScriptEngine::getInstance()->addAfterCleanupHook([](){
|
||||
const auto& instance = NativePtrToObjectMap::instance();
|
||||
se::Object* obj = nullptr;
|
||||
for (const auto& e : instance)
|
||||
{
|
||||
obj = e.second;
|
||||
obj->_isCleanup = true; // _cleanup will invoke NativePtrToObjectMap::erase method which will break this for loop. It isn't needed at ScriptEngine::cleanup step.
|
||||
obj->decRef();
|
||||
}
|
||||
NativePtrToObjectMap::clear();
|
||||
NonRefNativePtrCreatedByCtorMap::clear();
|
||||
});
|
||||
}
|
||||
|
||||
void Object::_setFinalizeCallback(JsFinalizeCallback finalizeCb)
|
||||
{
|
||||
_finalizeCb = finalizeCb;
|
||||
}
|
||||
|
||||
bool Object::getProperty(const char* name, Value* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
data->setUndefined();
|
||||
|
||||
JsPropertyIdRef propertyId;
|
||||
_CHECK(JsCreatePropertyId(name, strlen(name), &propertyId));
|
||||
|
||||
bool exist = false;
|
||||
JsHasProperty(_obj, propertyId, &exist);
|
||||
|
||||
if (exist)
|
||||
{
|
||||
JsValueRef jsValue;
|
||||
_CHECK(JsGetProperty(_obj, propertyId, &jsValue));
|
||||
internal::jsToSeValue(jsValue, data);
|
||||
}
|
||||
|
||||
return exist;
|
||||
}
|
||||
|
||||
bool Object::setProperty(const char* name, const Value& v)
|
||||
{
|
||||
JsValueRef jsValue = JS_INVALID_REFERENCE;
|
||||
internal::seToJsValue(v, &jsValue);
|
||||
JsPropertyIdRef propertyId;
|
||||
_CHECK(JsCreatePropertyId(name, strlen(name), &propertyId));
|
||||
_CHECK(JsSetProperty(_obj, propertyId, jsValue, true));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::defineProperty(const char *name, JsNativeFunction getter, JsNativeFunction setter)
|
||||
{
|
||||
return internal::defineProperty(_obj, name, getter, setter, true, true);
|
||||
}
|
||||
|
||||
bool Object::call(const ValueArray& args, Object* thisObject, Value* rval/* = nullptr*/)
|
||||
{
|
||||
assert(isFunction());
|
||||
|
||||
JsValueRef contextObject;
|
||||
|
||||
if (thisObject != nullptr)
|
||||
{
|
||||
contextObject = thisObject->_obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
_CHECK(JsGetUndefinedValue(&contextObject));
|
||||
}
|
||||
|
||||
JsValueRef* jsArgs = (JsValueRef*)malloc(sizeof(JsValueRef) * (args.size() + 1)); // Requires thisArg as first argument of arguments.
|
||||
|
||||
std::vector<Object*> toUnrootedObjects;
|
||||
|
||||
for (auto& arg : args)
|
||||
{
|
||||
if (arg.isObject())
|
||||
{
|
||||
if (!arg.toObject()->isRooted())
|
||||
{
|
||||
arg.toObject()->root();
|
||||
toUnrootedObjects.push_back(arg.toObject());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!args.empty())
|
||||
{
|
||||
internal::seToJsArgs(args, jsArgs + 1);
|
||||
}
|
||||
|
||||
jsArgs[0] = contextObject;
|
||||
|
||||
JsValueRef rcValue = JS_INVALID_REFERENCE;
|
||||
JsErrorCode errCode = JsCallFunction(_obj, jsArgs, args.size() + 1, &rcValue);
|
||||
free(jsArgs);
|
||||
|
||||
for (auto& obj: toUnrootedObjects)
|
||||
{
|
||||
obj->unroot();
|
||||
}
|
||||
|
||||
if (errCode == JsNoError)
|
||||
{
|
||||
if (rval != nullptr)
|
||||
{
|
||||
JsValueType type;
|
||||
JsGetValueType(rcValue, &type);
|
||||
if (rval != JS_INVALID_REFERENCE && type != JsUndefined)
|
||||
{
|
||||
internal::jsToSeValue(rcValue, rval);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
se::ScriptEngine::getInstance()->clearException();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::defineFunction(const char* funcName, JsNativeFunction func)
|
||||
{
|
||||
JsPropertyIdRef propertyId = JS_INVALID_REFERENCE;
|
||||
_CHECK(JsCreatePropertyId(funcName, strlen(funcName), &propertyId));
|
||||
|
||||
JsValueRef funcVal = JS_INVALID_REFERENCE;
|
||||
_CHECK(JsCreateFunction(func, nullptr, &funcVal));
|
||||
|
||||
_CHECK(JsSetProperty(_obj, propertyId, funcVal, true));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isArrayOfObject(JsValueRef obj)
|
||||
{
|
||||
JsValueType type;
|
||||
if (JsNoError == JsGetValueType(obj, &type))
|
||||
{
|
||||
return type == JsArray;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getArrayLengthOfObject(JsValueRef arrObj, uint32_t* length)
|
||||
{
|
||||
assert(isArrayOfObject(arrObj));
|
||||
assert(length != nullptr);
|
||||
|
||||
JsErrorCode err = JsNoError;
|
||||
JsPropertyIdRef propertyId = JS_INVALID_REFERENCE;
|
||||
const char* lengthName = "length";
|
||||
err = JsCreatePropertyId(lengthName, strlen(lengthName), &propertyId);
|
||||
if (err != JsNoError)
|
||||
return false;
|
||||
|
||||
JsValueRef jsLen = JS_INVALID_REFERENCE;
|
||||
err = JsGetProperty(arrObj, propertyId, &jsLen);
|
||||
if (err != JsNoError)
|
||||
return false;
|
||||
|
||||
int intVal = 0;
|
||||
err = JsNumberToInt(jsLen, &intVal);
|
||||
if (err != JsNoError)
|
||||
return false;
|
||||
|
||||
*length = (uint32_t)intVal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::isArray() const
|
||||
{
|
||||
return isArrayOfObject(_obj);
|
||||
}
|
||||
|
||||
bool Object::getArrayLength(uint32_t* length) const
|
||||
{
|
||||
return getArrayLengthOfObject(_obj, length);
|
||||
}
|
||||
|
||||
bool Object::getArrayElement(uint32_t index, Value* data) const
|
||||
{
|
||||
assert(isArray());
|
||||
assert(data != nullptr);
|
||||
|
||||
JsErrorCode err = JsNoError;
|
||||
JsValueRef result = JS_INVALID_REFERENCE;
|
||||
JsValueRef jsIndex = JS_INVALID_REFERENCE;
|
||||
err = JsIntToNumber(index, &jsIndex);
|
||||
if (err != JsNoError)
|
||||
return false;
|
||||
|
||||
err = JsGetIndexedProperty(_obj, jsIndex, &result);
|
||||
if (err != JsNoError)
|
||||
return false;
|
||||
|
||||
internal::jsToSeValue(result, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::setArrayElement(uint32_t index, const Value& data)
|
||||
{
|
||||
assert(isArray());
|
||||
|
||||
JsErrorCode err = JsNoError;
|
||||
JsValueRef jsIndex = JS_INVALID_REFERENCE;
|
||||
err = JsIntToNumber(index, &jsIndex);
|
||||
if (err != JsNoError)
|
||||
return false;
|
||||
|
||||
JsValueRef value = JS_INVALID_REFERENCE;
|
||||
internal::seToJsValue(data, &value);
|
||||
|
||||
err = JsSetIndexedProperty(_obj, jsIndex, value);
|
||||
if (err != JsNoError)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::getAllKeys(std::vector<std::string>* allKeys) const
|
||||
{
|
||||
assert(allKeys != nullptr);
|
||||
|
||||
JsErrorCode err = JsNoError;
|
||||
JsValueRef keys = JS_INVALID_REFERENCE;
|
||||
err = JsGetOwnPropertyNames(_obj, &keys);
|
||||
if (err != JsNoError)
|
||||
return false;
|
||||
|
||||
uint32_t len = 0;
|
||||
bool ok = false;
|
||||
ok = getArrayLengthOfObject(keys, &len);
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
std::string key;
|
||||
for (uint32_t index = 0; index < len; ++index)
|
||||
{
|
||||
JsValueRef indexValue = JS_INVALID_REFERENCE;
|
||||
err = JsIntToNumber(index, &indexValue);
|
||||
if (err != JsNoError)
|
||||
return false;
|
||||
|
||||
JsValueRef nameValue = JS_INVALID_REFERENCE;
|
||||
JsGetIndexedProperty(keys, indexValue, &nameValue);
|
||||
|
||||
internal::jsStringToStdString(nameValue, &key);
|
||||
allKeys->push_back(key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::isFunction() const
|
||||
{
|
||||
JsValueType type;
|
||||
_CHECK(JsGetValueType(_obj, &type));
|
||||
if (_obj != JS_INVALID_REFERENCE && type == JsFunction)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::_isNativeFunction() const
|
||||
{
|
||||
if (isFunction())
|
||||
{
|
||||
std::string info = toString();
|
||||
if (info.find("[native code]") != std::string::npos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::isTypedArray() const
|
||||
{
|
||||
JsValueType type;
|
||||
if (JsNoError == JsGetValueType(_obj, &type))
|
||||
{
|
||||
return type == JsTypedArray;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Object::TypedArrayType Object::getTypedArrayType() const
|
||||
{
|
||||
TypedArrayType ret = TypedArrayType::NONE;
|
||||
if (!isTypedArray())
|
||||
return ret;
|
||||
|
||||
JsTypedArrayType type;
|
||||
JsValueRef arrayBuffer = JS_INVALID_REFERENCE;
|
||||
unsigned int byteOffset = 0;
|
||||
unsigned int byteLength = 0;
|
||||
|
||||
if (JsNoError == JsGetTypedArrayInfo(_obj, &type, &arrayBuffer, &byteOffset, &byteLength))
|
||||
{
|
||||
if (type == JsArrayTypeInt8)
|
||||
ret = TypedArrayType::INT8;
|
||||
else if (type == JsArrayTypeInt16)
|
||||
ret = TypedArrayType::INT16;
|
||||
else if (type == JsArrayTypeInt32)
|
||||
ret = TypedArrayType::INT32;
|
||||
else if (type == JsArrayTypeUint8)
|
||||
ret = TypedArrayType::UINT8;
|
||||
else if (type == JsArrayTypeUint8Clamped)
|
||||
ret = TypedArrayType::UINT8_CLAMPED;
|
||||
else if (type == JsArrayTypeUint16)
|
||||
ret = TypedArrayType::UINT16;
|
||||
else if (type == JsArrayTypeUint32)
|
||||
ret = TypedArrayType::UINT32;
|
||||
else if (type == JsArrayTypeFloat32)
|
||||
ret = TypedArrayType::FLOAT32;
|
||||
else if (type == JsArrayTypeFloat64)
|
||||
ret = TypedArrayType::FLOAT64;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Object::getTypedArrayData(uint8_t** ptr, size_t* length) const
|
||||
{
|
||||
assert(isTypedArray());
|
||||
JsTypedArrayType arrayType;
|
||||
ChakraBytePtr buffer = nullptr;
|
||||
unsigned int bufferLength = 0;
|
||||
int elementSize = 0;
|
||||
bool ret = false;
|
||||
if (JsNoError == JsGetTypedArrayStorage(_obj, &buffer, &bufferLength, &arrayType, &elementSize))
|
||||
{
|
||||
*ptr = buffer;
|
||||
*length = bufferLength;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ptr = nullptr;
|
||||
*length = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Object::isArrayBuffer() const
|
||||
{
|
||||
JsValueType type;
|
||||
if (JsNoError == JsGetValueType(_obj, &type))
|
||||
{
|
||||
return type == JsArrayBuffer;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::getArrayBufferData(uint8_t** ptr, size_t* length) const
|
||||
{
|
||||
assert(isArrayBuffer());
|
||||
ChakraBytePtr buffer = nullptr;
|
||||
unsigned int bufferLength = 0;
|
||||
bool ret = false;
|
||||
if (JsNoError == JsGetArrayBufferStorage(_obj, &buffer, &bufferLength))
|
||||
{
|
||||
*ptr = buffer;
|
||||
*length = bufferLength;
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ptr = nullptr;
|
||||
*length = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* Object::getPrivateData() const
|
||||
{
|
||||
if (_privateData == nullptr)
|
||||
{
|
||||
const_cast<Object*>(this)->_privateData = internal::getPrivate(_obj);
|
||||
}
|
||||
return _privateData;
|
||||
}
|
||||
|
||||
void Object::setPrivateData(void* data)
|
||||
{
|
||||
assert(_privateData == nullptr);
|
||||
assert(NativePtrToObjectMap::find(data) == NativePtrToObjectMap::end());
|
||||
internal::setPrivate(_obj, data, _finalizeCb);
|
||||
NativePtrToObjectMap::emplace(data, this);
|
||||
_privateData = data;
|
||||
}
|
||||
|
||||
void Object::clearPrivateData(bool clearMapping)
|
||||
{
|
||||
if (_privateData != nullptr)
|
||||
{
|
||||
if (clearMapping)
|
||||
NativePtrToObjectMap::erase(_privateData);
|
||||
internal::clearPrivate(_obj);
|
||||
_privateData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
JsValueRef Object::_getJSObject() const
|
||||
{
|
||||
return _obj;
|
||||
}
|
||||
|
||||
Class* Object::_getClass() const
|
||||
{
|
||||
return _cls;
|
||||
}
|
||||
|
||||
void Object::root()
|
||||
{
|
||||
if (_rootCount == 0)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
_CHECK(JsAddRef(_obj, &count));
|
||||
}
|
||||
++_rootCount;
|
||||
}
|
||||
|
||||
void Object::unroot()
|
||||
{
|
||||
if (_rootCount > 0)
|
||||
{
|
||||
--_rootCount;
|
||||
if (_rootCount == 0)
|
||||
{
|
||||
// Don't unprotect if it's in cleanup, otherwise, it will trigger crash.
|
||||
auto se = ScriptEngine::getInstance();
|
||||
if (_currentVMId == se->getVMId())
|
||||
{
|
||||
if (!se->isInCleanup() && !se->isGarbageCollecting())
|
||||
{
|
||||
unsigned int count = 0;
|
||||
_CHECK(JsRelease(_obj, &count));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SE_LOGD("Object::unroot, ScriptEngine was initialized again, ignore cleanup work, oldVMId: %u, newVMId: %u\n", _currentVMId, se->getVMId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Object::isRooted() const
|
||||
{
|
||||
return _rootCount > 0;
|
||||
}
|
||||
|
||||
bool Object::strictEquals(Object* o) const
|
||||
{
|
||||
bool same = false;
|
||||
_CHECK(JsStrictEquals(_obj, o->_obj, &same));
|
||||
return same;
|
||||
}
|
||||
|
||||
bool Object::attachObject(Object* obj)
|
||||
{
|
||||
assert(obj);
|
||||
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal))
|
||||
return false;
|
||||
Object* jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("registerNativeRef", &func))
|
||||
return false;
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::detachObject(Object* obj)
|
||||
{
|
||||
assert(obj);
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal))
|
||||
return false;
|
||||
Object* jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("unregisterNativeRef", &func))
|
||||
return false;
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Object::toString() const
|
||||
{
|
||||
std::string ret;
|
||||
if (isFunction() || isArray() || isTypedArray())
|
||||
{
|
||||
internal::forceConvertJsValueToStdString(_obj, &ret);
|
||||
}
|
||||
else if (isArrayBuffer())
|
||||
{
|
||||
ret = "[object ArrayBuffer]";
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = "[object Object]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
@@ -0,0 +1,386 @@
|
||||
/****************************************************************************
|
||||
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_CHAKRACORE
|
||||
|
||||
#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.
|
||||
*/
|
||||
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 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, JsNativeFunction getter, JsNativeFunction 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, JsNativeFunction 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* obj) 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, JsValueRef obj);
|
||||
JsValueRef _getJSObject() const;
|
||||
Class* _getClass() const;
|
||||
void _cleanup(void* nativeObject = nullptr);
|
||||
void _setFinalizeCallback(JsFinalizeCallback finalizeCb);
|
||||
bool _isNativeFunction() const;
|
||||
//
|
||||
private:
|
||||
static void cleanup();
|
||||
|
||||
Object();
|
||||
virtual ~Object();
|
||||
bool init(JsValueRef obj);
|
||||
|
||||
Class* _cls;
|
||||
JsValueRef _obj;
|
||||
void* _privateData;
|
||||
JsFinalizeCallback _finalizeCb;
|
||||
|
||||
uint32_t _rootCount;
|
||||
uint32_t _currentVMId;
|
||||
bool _isCleanup;
|
||||
|
||||
friend class ScriptEngine;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
|
||||
@@ -0,0 +1,539 @@
|
||||
/****************************************************************************
|
||||
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_CHAKRACORE
|
||||
|
||||
#include "Object.hpp"
|
||||
#include "Class.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "../State.hpp"
|
||||
#include "../MappingUtils.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
Class* __jsb_CCPrivateData_class = nullptr;
|
||||
|
||||
namespace {
|
||||
ScriptEngine* __instance = nullptr;
|
||||
|
||||
JsValueRef __forceGC(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
|
||||
{
|
||||
ScriptEngine::getInstance()->garbageCollect();
|
||||
return JS_INVALID_REFERENCE;
|
||||
}
|
||||
|
||||
JsValueRef __log(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
|
||||
{
|
||||
if (argumentCount > 1)
|
||||
{
|
||||
std::string str;
|
||||
internal::forceConvertJsValueToStdString(arguments[1], &str);
|
||||
SE_LOGD("JS: %s\n", str.c_str());
|
||||
}
|
||||
return JS_INVALID_REFERENCE;
|
||||
}
|
||||
|
||||
void myJsBeforeCollectCallback(void *callbackState)
|
||||
{
|
||||
SE_LOGD("GC start ...\n");
|
||||
}
|
||||
|
||||
JsValueRef privateDataContructor(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
|
||||
{
|
||||
return JS_INVALID_REFERENCE;
|
||||
}
|
||||
|
||||
void privateDataFinalize(void *data)
|
||||
{
|
||||
internal::PrivateData* p = (internal::PrivateData*)data;
|
||||
if (p->finalizeCb != nullptr)
|
||||
p->finalizeCb(p->data);
|
||||
free(p);
|
||||
}
|
||||
|
||||
// For console stuff
|
||||
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;
|
||||
}
|
||||
|
||||
bool JSB_console_log(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "");
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_log)
|
||||
|
||||
bool JSB_console_debug(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[DEBUG]: ");
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_debug)
|
||||
|
||||
bool JSB_console_info(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[INFO]: ");
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_info)
|
||||
|
||||
bool JSB_console_warn(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[WARN]: ");
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_warn)
|
||||
|
||||
bool JSB_console_error(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[ERROR]: ");
|
||||
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);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_assert)
|
||||
|
||||
} // namespace {
|
||||
|
||||
ScriptEngine *ScriptEngine::getInstance()
|
||||
{
|
||||
if (__instance == nullptr)
|
||||
{
|
||||
__instance = new ScriptEngine();
|
||||
}
|
||||
|
||||
return __instance;
|
||||
}
|
||||
|
||||
void ScriptEngine::destroyInstance()
|
||||
{
|
||||
delete __instance;
|
||||
__instance = nullptr;
|
||||
}
|
||||
|
||||
ScriptEngine::ScriptEngine()
|
||||
: _rt(JS_INVALID_RUNTIME_HANDLE)
|
||||
, _cx(JS_INVALID_REFERENCE)
|
||||
, _globalObj(nullptr)
|
||||
, _exceptionCallback(nullptr)
|
||||
, _currentSourceContext(0)
|
||||
, _vmId(0)
|
||||
, _isValid(false)
|
||||
, _isInCleanup(false)
|
||||
, _isErrorHandleWorking(false)
|
||||
, _isGarbageCollecting(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool ScriptEngine::init()
|
||||
{
|
||||
cleanup();
|
||||
SE_LOGD("Initializing ChakraCore, version: %d.%d.%d\n", CHAKRA_CORE_MAJOR_VERSION, CHAKRA_CORE_MINOR_VERSION, CHAKRA_CORE_PATCH_VERSION);
|
||||
|
||||
++_vmId;
|
||||
for (const auto& hook : _beforeInitHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_beforeInitHookArray.clear();
|
||||
|
||||
_CHECK(JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &_rt));
|
||||
_CHECK(JsCreateContext(_rt, &_cx));
|
||||
_CHECK(JsSetCurrentContext(_cx));
|
||||
|
||||
NativePtrToObjectMap::init();
|
||||
NonRefNativePtrCreatedByCtorMap::init();
|
||||
|
||||
// Set up ES6 Promise
|
||||
// if (JsSetPromiseContinuationCallback(PromiseContinuationCallback, &taskQueue) != JsNoError)
|
||||
|
||||
JsValueRef globalObj = JS_INVALID_REFERENCE;
|
||||
_CHECK(JsGetGlobalObject(&globalObj));
|
||||
|
||||
_CHECK(JsSetRuntimeBeforeCollectCallback(_rt, nullptr, myJsBeforeCollectCallback));
|
||||
|
||||
_globalObj = Object::_createJSObject(nullptr, globalObj);
|
||||
_globalObj->root();
|
||||
_globalObj->setProperty("window", Value(_globalObj));
|
||||
|
||||
// ChakraCore isn't shipped with a console variable. Make a fake one.
|
||||
Value consoleVal;
|
||||
bool hasConsole = _globalObj->getProperty("console", &consoleVal) && consoleVal.isObject();
|
||||
assert(!hasConsole);
|
||||
|
||||
HandleObject consoleObj(Object::createPlainObject());
|
||||
consoleObj->defineFunction("log", _SE(JSB_console_log));
|
||||
consoleObj->defineFunction("debug", _SE(JSB_console_debug));
|
||||
consoleObj->defineFunction("info", _SE(JSB_console_info));
|
||||
consoleObj->defineFunction("warn", _SE(JSB_console_warn));
|
||||
consoleObj->defineFunction("error", _SE(JSB_console_error));
|
||||
consoleObj->defineFunction("assert", _SE(JSB_console_assert));
|
||||
|
||||
_globalObj->setProperty("console", Value(consoleObj));
|
||||
|
||||
_globalObj->setProperty("scriptEngineType", Value("ChakraCore"));
|
||||
|
||||
_globalObj->defineFunction("log", __log);
|
||||
_globalObj->defineFunction("forceGC", __forceGC);
|
||||
|
||||
__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;
|
||||
|
||||
_isInCleanup = true;
|
||||
for (const auto& hook : _beforeCleanupHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_beforeCleanupHookArray.clear();
|
||||
|
||||
SAFE_DEC_REF(_globalObj);
|
||||
Object::cleanup();
|
||||
Class::cleanup();
|
||||
garbageCollect();
|
||||
|
||||
_CHECK(JsSetCurrentContext(JS_INVALID_REFERENCE));
|
||||
_CHECK(JsDisposeRuntime(_rt));
|
||||
|
||||
_cx = nullptr;
|
||||
_globalObj = nullptr;
|
||||
_isValid = false;
|
||||
|
||||
_registerCallbackArray.clear();
|
||||
|
||||
for (const auto& hook : _afterCleanupHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_afterCleanupHookArray.clear();
|
||||
_isInCleanup = false;
|
||||
|
||||
NativePtrToObjectMap::destroy();
|
||||
NonRefNativePtrCreatedByCtorMap::destroy();
|
||||
}
|
||||
|
||||
ScriptEngine::ExceptionInfo ScriptEngine::formatException(JsValueRef exception)
|
||||
{
|
||||
ExceptionInfo ret;
|
||||
if (exception == JS_INVALID_REFERENCE)
|
||||
return ret;
|
||||
|
||||
std::vector<std::string> allKeys;
|
||||
Object* exceptionObj = Object::_createJSObject(nullptr, exception);
|
||||
exceptionObj->getAllKeys(&allKeys);
|
||||
|
||||
for (const auto& key : allKeys)
|
||||
{
|
||||
Value tmp;
|
||||
if (exceptionObj->getProperty(key.c_str(), &tmp))
|
||||
{
|
||||
// SE_LOGD("[%s]=%s\n", key.c_str(), tmp.toStringForce().c_str());
|
||||
if (key == "message")
|
||||
{
|
||||
ret.message = tmp.toString();
|
||||
}
|
||||
else if (key == "stack")
|
||||
{
|
||||
ret.stack = tmp.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exceptionObj->decRef();
|
||||
|
||||
ret.location = "(see stack)";
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool ScriptEngine::isGarbageCollecting()
|
||||
{
|
||||
return _isGarbageCollecting;
|
||||
}
|
||||
|
||||
void ScriptEngine::_setGarbageCollecting(bool isGarbageCollecting)
|
||||
{
|
||||
_isGarbageCollecting = isGarbageCollecting;
|
||||
}
|
||||
|
||||
void ScriptEngine::garbageCollect()
|
||||
{
|
||||
SE_LOGD("GC begin ..., (Native -> JS map) count: %d\n", (int)NativePtrToObjectMap::size());
|
||||
_CHECK(JsCollectGarbage(_rt));
|
||||
SE_LOGD("GC end ..., (Native -> JS map) count: %d\n", (int)NativePtrToObjectMap::size());
|
||||
}
|
||||
|
||||
void ScriptEngine::clearException()
|
||||
{
|
||||
bool hasException = false;
|
||||
_CHECK(JsHasException(&hasException));
|
||||
|
||||
if (hasException)
|
||||
{
|
||||
JsValueRef exception;
|
||||
_CHECK(JsGetAndClearException(&exception));
|
||||
|
||||
ExceptionInfo exceptionInfo = formatException(exception);
|
||||
SE_LOGD("ERROR: %s, location: %s, \nSTACK:\n%s\n", exceptionInfo.message.c_str(), exceptionInfo.location.c_str(), exceptionInfo.stack.c_str());
|
||||
|
||||
if (_exceptionCallback != nullptr)
|
||||
{
|
||||
_exceptionCallback(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.location));
|
||||
args.push_back(Value(0));
|
||||
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::setExceptionCallback(const ExceptionCallback& cb)
|
||||
{
|
||||
_exceptionCallback = cb;
|
||||
}
|
||||
|
||||
bool ScriptEngine::evalString(const char* script, ssize_t length/* = -1 */, Value* ret/* = nullptr */, const char* fileName/* = nullptr */)
|
||||
{
|
||||
assert(script != nullptr);
|
||||
if (length < 0)
|
||||
length = strlen(script);
|
||||
|
||||
if (fileName == nullptr)
|
||||
fileName = "(no filename)";
|
||||
|
||||
JsValueRef fname;
|
||||
_CHECK(JsCreateString(fileName, strlen(fileName), &fname));
|
||||
|
||||
JsValueRef scriptSource;
|
||||
_CHECK(JsCreateString(script, length, &scriptSource));
|
||||
|
||||
JsValueRef result;
|
||||
// Run the script.
|
||||
JsErrorCode errCode = JsRun(scriptSource, _currentSourceContext++, fname, JsParseScriptAttributeNone, &result);
|
||||
|
||||
if (errCode != JsNoError)
|
||||
{
|
||||
SE_LOGE("ScriptEngine::evalString script %s, failed!\n", fileName);
|
||||
clearException();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ret != nullptr)
|
||||
{
|
||||
JsValueType type;
|
||||
JsGetValueType(result, &type);
|
||||
if (type != JsUndefined)
|
||||
{
|
||||
internal::jsToSeValue(result, ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret->setUndefined();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 buffer is empty!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptEngine::enableDebugger(const std::string& serverAddr, uint32_t port, bool isWait)
|
||||
{
|
||||
//IDEA:
|
||||
}
|
||||
|
||||
bool ScriptEngine::isDebuggerEnabled() const
|
||||
{
|
||||
//IDEA:
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptEngine::mainLoopUpdate()
|
||||
{
|
||||
//IDEA:
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
@@ -0,0 +1,311 @@
|
||||
/****************************************************************************
|
||||
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_CHAKRACORE
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
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 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 _setGarbageCollecting(bool isGarbageCollecting);
|
||||
void _retainScriptObject(void* owner, void* target);
|
||||
void _releaseScriptObject(void* owner, void* target);
|
||||
//
|
||||
private:
|
||||
ScriptEngine();
|
||||
~ScriptEngine();
|
||||
|
||||
struct ExceptionInfo
|
||||
{
|
||||
std::string location;
|
||||
std::string message;
|
||||
std::string stack;
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !message.empty();
|
||||
}
|
||||
};
|
||||
ExceptionInfo formatException(JsValueRef exception);
|
||||
|
||||
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;
|
||||
|
||||
JsRuntimeHandle _rt;
|
||||
JsContextRef _cx;
|
||||
Object* _globalObj;
|
||||
|
||||
FileOperationDelegate _fileOperationDelegate;
|
||||
ExceptionCallback _exceptionCallback;
|
||||
|
||||
uint32_t _currentSourceContext;
|
||||
uint32_t _vmId;
|
||||
|
||||
bool _isValid;
|
||||
bool _isInCleanup;
|
||||
bool _isGarbageCollecting;
|
||||
bool _isErrorHandleWorking;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/****************************************************************************
|
||||
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 "HelperMacros.h"
|
||||
#include "Utils.hpp"
|
||||
@@ -0,0 +1,336 @@
|
||||
/****************************************************************************
|
||||
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_CHAKRACORE
|
||||
|
||||
#include "Object.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
#include "../HandleObject.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
namespace internal {
|
||||
|
||||
bool defineProperty(JsValueRef obj, const char* name, JsNativeFunction getter, JsNativeFunction setter, bool enumerable, bool configurable)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
JsPropertyIdRef propertyId = JS_INVALID_REFERENCE;
|
||||
_CHECK(JsCreatePropertyId(name, strlen(name), &propertyId));
|
||||
JsValueRef propertyDescriptor;
|
||||
_CHECK(JsCreateObject(&propertyDescriptor));
|
||||
|
||||
const char* tmp = nullptr;
|
||||
JsValueRef jsValue = JS_INVALID_REFERENCE;
|
||||
JsPropertyIdRef id = JS_INVALID_REFERENCE;
|
||||
|
||||
if (getter != nullptr)
|
||||
{
|
||||
tmp = "get";
|
||||
_CHECK(JsCreateFunction(getter, nullptr, &jsValue));
|
||||
_CHECK(JsCreatePropertyId(tmp, strlen(tmp), &id));
|
||||
_CHECK(JsSetProperty(propertyDescriptor, id, jsValue, true));
|
||||
}
|
||||
|
||||
if (setter != nullptr)
|
||||
{
|
||||
tmp = "set";
|
||||
_CHECK(JsCreateFunction(setter, nullptr, &jsValue));
|
||||
_CHECK(JsCreatePropertyId(tmp, strlen(tmp), &id));
|
||||
_CHECK(JsSetProperty(propertyDescriptor, id, jsValue, true));
|
||||
}
|
||||
|
||||
JsValueRef trueValue;
|
||||
_CHECK(JsGetTrueValue(&trueValue));
|
||||
|
||||
tmp = "enumerable";
|
||||
_CHECK(JsCreatePropertyId(tmp, strlen(tmp), &id));
|
||||
_CHECK(JsSetProperty(propertyDescriptor, id, trueValue, true));
|
||||
|
||||
tmp = "configurable";
|
||||
_CHECK(JsCreatePropertyId(tmp, strlen(tmp), &id));
|
||||
_CHECK(JsSetProperty(propertyDescriptor, id, trueValue, true));
|
||||
|
||||
_CHECK(JsDefineProperty(obj, propertyId, propertyDescriptor, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
void jsToSeArgs(int argc, const JsValueRef* argv, ValueArray* outArr)
|
||||
{
|
||||
outArr->reserve(argc);
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
Value v;
|
||||
jsToSeValue(argv[i], &v);
|
||||
outArr->push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsArgs(const ValueArray& args, JsValueRef* outArr)
|
||||
{
|
||||
for (size_t i = 0, len = args.size(); i < len; ++i)
|
||||
{
|
||||
seToJsValue(args[i], &outArr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void jsToSeValue(JsValueRef jsValue, Value* data)
|
||||
{
|
||||
JsValueType type;
|
||||
_CHECK(JsGetValueType(jsValue, &type));
|
||||
|
||||
if (type == JsNull)
|
||||
{
|
||||
data->setNull();
|
||||
}
|
||||
else if (type == JsUndefined)
|
||||
{
|
||||
data->setUndefined();
|
||||
}
|
||||
else if (type == JsNumber)
|
||||
{
|
||||
double v = 0.0;
|
||||
_CHECK(JsNumberToDouble(jsValue, &v));
|
||||
data->setNumber(v);
|
||||
}
|
||||
else if (type == JsBoolean)
|
||||
{
|
||||
bool v = false;
|
||||
_CHECK(JsBooleanToBool(jsValue, &v));
|
||||
data->setBoolean(v);
|
||||
}
|
||||
else if (type == JsString)
|
||||
{
|
||||
std::string str;
|
||||
forceConvertJsValueToStdString(jsValue, &str);
|
||||
data->setString(str);
|
||||
}
|
||||
else if (type == JsObject || type == JsFunction || type == JsArrayBuffer
|
||||
|| type == JsTypedArray || type == JsArray)
|
||||
{
|
||||
void* nativePtr = internal::getPrivate(jsValue);
|
||||
Object* obj = nullptr;
|
||||
if (nativePtr != nullptr)
|
||||
{
|
||||
obj = Object::getObjectWithPtr(nativePtr);
|
||||
}
|
||||
|
||||
if (obj == nullptr)
|
||||
{
|
||||
obj = Object::_createJSObject(nullptr, jsValue);
|
||||
}
|
||||
data->setObject(obj, true);
|
||||
obj->decRef();
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsValue(const Value& v, JsValueRef* jsval)
|
||||
{
|
||||
switch (v.getType()) {
|
||||
case Value::Type::Null:
|
||||
_CHECK(JsGetNullValue(jsval));
|
||||
break;
|
||||
|
||||
case Value::Type::Number:
|
||||
_CHECK(JsDoubleToNumber(v.toNumber(), jsval));
|
||||
break;
|
||||
|
||||
case Value::Type::String:
|
||||
{
|
||||
_CHECK(JsCreateString(v.toString().c_str(), v.toString().length(), jsval));
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::Boolean:
|
||||
_CHECK(JsBoolToBoolean(v.toBoolean(), jsval));
|
||||
break;
|
||||
|
||||
case Value::Type::Object:
|
||||
*jsval = v.toObject()->_getJSObject();
|
||||
break;
|
||||
|
||||
default: // Undefined
|
||||
_CHECK(JsGetUndefinedValue(jsval));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void forceConvertJsValueToStdString(JsValueRef jsval, std::string* ret)
|
||||
{
|
||||
JsValueRef strVal = JS_INVALID_REFERENCE;
|
||||
_CHECK(JsConvertValueToString(jsval, &strVal));
|
||||
jsStringToStdString(strVal, ret);
|
||||
}
|
||||
|
||||
void jsStringToStdString(JsValueRef strVal, std::string* ret)
|
||||
{
|
||||
// Get the buffer size
|
||||
size_t bufSize = 0;
|
||||
_CHECK(JsCopyString(strVal, nullptr, 0, &bufSize));
|
||||
// Allocate buffer
|
||||
char* buf = (char*)malloc(bufSize + 1);
|
||||
memset(buf, 0, bufSize + 1);
|
||||
// Copy
|
||||
_CHECK(JsCopyString(strVal, buf, bufSize, nullptr));
|
||||
*ret = buf;
|
||||
free(buf);
|
||||
}
|
||||
|
||||
const char* KEY_PRIVATE_DATA = "__cc_private_data";
|
||||
|
||||
bool hasPrivate(JsValueRef obj)
|
||||
{
|
||||
JsValueType type;
|
||||
_CHECK(JsGetValueType(obj, &type));
|
||||
if (type != JsObject && type != JsFunction
|
||||
&& type != JsArray && type != JsSymbol
|
||||
&& type != JsArrayBuffer && type != JsTypedArray
|
||||
&& type != JsDataView)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isExist = false;
|
||||
JsErrorCode err = JsHasExternalData(obj, &isExist);
|
||||
assert(err == JsNoError);
|
||||
if (isExist)
|
||||
return true;
|
||||
|
||||
JsPropertyIdRef propertyId = JS_INVALID_REFERENCE;
|
||||
_CHECK(JsCreatePropertyId(KEY_PRIVATE_DATA, strlen(KEY_PRIVATE_DATA), &propertyId));
|
||||
_CHECK(JsHasProperty(obj, propertyId, &isExist));
|
||||
return isExist;
|
||||
}
|
||||
|
||||
void setPrivate(JsValueRef obj, void* data, JsFinalizeCallback finalizeCb)
|
||||
{
|
||||
bool isExist = false;
|
||||
_CHECK(JsHasExternalData(obj, &isExist));
|
||||
if (isExist)
|
||||
{
|
||||
_CHECK(JsSetExternalData(obj, data));
|
||||
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;
|
||||
_CHECK(JsSetExternalData(privateObj->_getJSObject(), privateData));
|
||||
// SE_LOGD("setPrivate: %p\n", data);
|
||||
|
||||
JsPropertyIdRef propertyId = JS_INVALID_REFERENCE;
|
||||
JsCreatePropertyId(KEY_PRIVATE_DATA, strlen(KEY_PRIVATE_DATA), &propertyId);
|
||||
_CHECK(JsSetProperty(obj, propertyId, privateObj->_getJSObject(), true));
|
||||
|
||||
privateObj->unroot();
|
||||
privateObj->decRef();
|
||||
}
|
||||
|
||||
void* getPrivate(JsValueRef obj)
|
||||
{
|
||||
JsValueType type;
|
||||
_CHECK(JsGetValueType(obj, &type));
|
||||
if (type != JsObject && type != JsFunction
|
||||
&& type != JsArray && type != JsSymbol
|
||||
&& type != JsArrayBuffer && type != JsTypedArray
|
||||
&& type != JsDataView)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* data = nullptr;
|
||||
bool isExist = false;
|
||||
_CHECK(JsHasExternalData(obj, &isExist));
|
||||
if (isExist)
|
||||
{
|
||||
_CHECK(JsGetExternalData(obj, &data));
|
||||
return data;
|
||||
}
|
||||
|
||||
JsPropertyIdRef propertyId = JS_INVALID_REFERENCE;
|
||||
_CHECK(JsCreatePropertyId(KEY_PRIVATE_DATA, strlen(KEY_PRIVATE_DATA), &propertyId));
|
||||
_CHECK(JsHasProperty(obj, propertyId, &isExist));
|
||||
if (isExist)
|
||||
{
|
||||
JsValueRef privateDataVal;
|
||||
_CHECK(JsGetProperty(obj, propertyId, &privateDataVal));
|
||||
void* tmpPrivateData = nullptr;
|
||||
_CHECK(JsGetExternalData(privateDataVal, &tmpPrivateData));
|
||||
internal::PrivateData* privateData = (internal::PrivateData*)tmpPrivateData;
|
||||
assert(privateData);
|
||||
data = privateData->data;
|
||||
}
|
||||
// SE_LOGD("getPrivate: %p\n", data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void clearPrivate(JsValueRef obj)
|
||||
{
|
||||
JsValueType type;
|
||||
_CHECK(JsGetValueType(obj, &type));
|
||||
if (type != JsObject && type != JsFunction
|
||||
&& type != JsArray && type != JsSymbol
|
||||
&& type != JsArrayBuffer && type != JsTypedArray
|
||||
&& type != JsDataView)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool isExist = false;
|
||||
JsErrorCode err = JsHasExternalData(obj, &isExist);
|
||||
assert(err == JsNoError);
|
||||
if (isExist)
|
||||
{
|
||||
_CHECK(JsSetExternalData(obj, nullptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
JsPropertyIdRef propertyId = JS_INVALID_REFERENCE;
|
||||
_CHECK(JsCreatePropertyId(KEY_PRIVATE_DATA, strlen(KEY_PRIVATE_DATA), &propertyId));
|
||||
_CHECK(JsHasProperty(obj, propertyId, &isExist));
|
||||
if (isExist)
|
||||
{
|
||||
void* data = nullptr;
|
||||
_CHECK(JsGetExternalData(obj, &data));
|
||||
internal::PrivateData* privateData = (internal::PrivateData*)data;
|
||||
free(privateData);
|
||||
JsSetExternalData(obj, nullptr);
|
||||
_CHECK(JsDeleteProperty(obj, propertyId, true, nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace se { namespace internal {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
@@ -0,0 +1,63 @@
|
||||
/****************************************************************************
|
||||
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_CHAKRACORE
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
#include "../Value.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct PrivateData
|
||||
{
|
||||
void* data;
|
||||
JsFinalizeCallback finalizeCb;
|
||||
};
|
||||
|
||||
bool defineProperty(JsValueRef obj, const char* name, JsNativeFunction getter, JsNativeFunction setter, bool enumerable, bool configurable);
|
||||
|
||||
void jsToSeArgs(int argc, const JsValueRef* argv, ValueArray* outArr);
|
||||
void seToJsArgs(const ValueArray& args, JsValueRef* outArr);
|
||||
void jsToSeValue(JsValueRef jsval, Value* v);
|
||||
void seToJsValue(const Value& v, JsValueRef* jsval);
|
||||
|
||||
void forceConvertJsValueToStdString(JsValueRef jsval, std::string* ret);
|
||||
void jsStringToStdString(JsValueRef jsStr, std::string* ret);
|
||||
|
||||
bool hasPrivate(JsValueRef obj);
|
||||
void setPrivate(JsValueRef obj, void* data, JsFinalizeCallback finalizeCb);
|
||||
void* getPrivate(JsValueRef obj);
|
||||
void clearPrivate(JsValueRef obj);
|
||||
|
||||
} // namespace internal {
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
100
cocos2d-x/cocos/scripting/js-bindings/jswrapper/config.cpp
Normal file
100
cocos2d-x/cocos/scripting/js-bindings/jswrapper/config.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "config.hpp"
|
||||
#include <stdio.h>
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(_WIN32) && defined(_WINDOWS)
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
static void _winLog(FILE* fp, const char *format, va_list args)
|
||||
{
|
||||
static const int MAX_LOG_LENGTH = 16 * 1024;
|
||||
int bufferSize = MAX_LOG_LENGTH;
|
||||
char* buf = nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
buf = new (std::nothrow) char[bufferSize];
|
||||
if (buf == nullptr)
|
||||
return; // not enough memory
|
||||
|
||||
int ret = vsnprintf(buf, bufferSize - 3, format, args);
|
||||
if (ret < 0)
|
||||
{
|
||||
bufferSize *= 2;
|
||||
|
||||
delete[] buf;
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
} while (true);
|
||||
|
||||
int pos = 0;
|
||||
int len = strlen(buf);
|
||||
char tempBuf[MAX_LOG_LENGTH + 1] = { 0 };
|
||||
WCHAR wszBuf[MAX_LOG_LENGTH + 1] = { 0 };
|
||||
|
||||
do
|
||||
{
|
||||
std::copy(buf + pos, buf + pos + MAX_LOG_LENGTH, tempBuf);
|
||||
|
||||
tempBuf[MAX_LOG_LENGTH] = 0;
|
||||
|
||||
MultiByteToWideChar(CP_UTF8, 0, tempBuf, -1, wszBuf, sizeof(wszBuf));
|
||||
OutputDebugStringW(wszBuf);
|
||||
WideCharToMultiByte(CP_ACP, 0, wszBuf, -1, tempBuf, sizeof(tempBuf), nullptr, FALSE);
|
||||
|
||||
fprintf(fp, "%s", tempBuf);
|
||||
fflush(fp);
|
||||
|
||||
pos += MAX_LOG_LENGTH;
|
||||
|
||||
} while (pos < len);
|
||||
|
||||
fflush(stdout);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
void seLogD(const char * format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_winLog(stdout, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void seLogE(const char * format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_winLog(stderr, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#endif // #if defined(_WIN32) && defined(_WINDOWS)
|
||||
139
cocos2d-x/cocos/scripting/js-bindings/jswrapper/config.hpp
Normal file
139
cocos2d-x/cocos/scripting/js-bindings/jswrapper/config.hpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/****************************************************************************
|
||||
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
|
||||
|
||||
#define SCRIPT_ENGINE_NONE 0
|
||||
//#define SCRIPT_ENGINE_SM 1
|
||||
#define SCRIPT_ENGINE_V8 2
|
||||
#define SCRIPT_ENGINE_JSC 3
|
||||
//#define SCRIPT_ENGINE_CHAKRACORE 4
|
||||
|
||||
#define SCRIPT_ENGINE_V8_ON_MAC 1 // default using v8 on macOS, set 0 to disable
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_OS_OSX
|
||||
#if (SCRIPT_ENGINE_V8_ON_MAC == 0)
|
||||
#define SCRIPT_ENGINE_TYPE SCRIPT_ENGINE_JSC
|
||||
#else
|
||||
#define SCRIPT_ENGINE_TYPE SCRIPT_ENGINE_V8
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#ifdef __arm64__
|
||||
#define SCRIPT_ENGINE_TYPE SCRIPT_ENGINE_V8
|
||||
#else
|
||||
#define SCRIPT_ENGINE_TYPE SCRIPT_ENGINE_JSC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//TODO how to make simulator build with v8 too? Because in release mode, it will build
|
||||
// which means it will build armv7, but v8 doesn't support armv7.
|
||||
#else
|
||||
#define SCRIPT_ENGINE_TYPE SCRIPT_ENGINE_V8
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef USE_V8_DEBUGGER
|
||||
#if defined(COCOS2D_DEBUG) && COCOS2D_DEBUG > 0
|
||||
#define USE_V8_DEBUGGER 1
|
||||
#else
|
||||
#define USE_V8_DEBUGGER 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define SE_LOG_TO_JS_ENV 0 // print log to JavaScript environment, for example DevTools
|
||||
|
||||
#if !defined(ANDROID_INSTANT) && defined(USE_V8_DEBUGGER) && USE_V8_DEBUGGER > 0
|
||||
#define SE_ENABLE_INSPECTOR 1
|
||||
#define SE_DEBUG 2
|
||||
#define HAVE_INSPECTOR 1
|
||||
#else
|
||||
#define SE_ENABLE_INSPECTOR 0
|
||||
#define SE_DEBUG 0
|
||||
#define HAVE_INSPECTOR 0
|
||||
#endif
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#define LOG_TAG "jswrapper"
|
||||
#define SE_LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#define SE_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
#elif defined(_WIN32) && defined(_WINDOWS)
|
||||
|
||||
#ifndef QUOTEME_
|
||||
#define QUOTEME_(x) #x
|
||||
#endif
|
||||
|
||||
#ifndef QUOTEME
|
||||
#define QUOTEME(x) QUOTEME_(x)
|
||||
#endif
|
||||
|
||||
void seLogD(const char * format, ...);
|
||||
void seLogE(const char * format, ...);
|
||||
|
||||
#define LOG_TAG "jswrapper"
|
||||
#define SE_LOGD(fmt, ...) seLogD("D/" LOG_TAG " (" QUOTEME(__LINE__) "): " fmt "", ##__VA_ARGS__)
|
||||
#define SE_LOGE(fmt, ...) seLogE("E/" LOG_TAG " (" QUOTEME(__LINE__) "): " fmt "", ##__VA_ARGS__)
|
||||
|
||||
#else
|
||||
|
||||
#define SE_LOGD(...) do { fprintf(stdout, __VA_ARGS__); fflush(stdout); } while (false)
|
||||
#define SE_LOGE(...) do { fprintf(stderr, __VA_ARGS__); fflush(stderr); } while (false)
|
||||
|
||||
#endif
|
||||
|
||||
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
||||
|
||||
#define __POSIX__
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(_WINDOWS)
|
||||
#include <BaseTsd.h>
|
||||
|
||||
#ifndef __SSIZE_T
|
||||
#define __SSIZE_T
|
||||
typedef SSIZE_T ssize_t;
|
||||
#define _SSIZE_T_DEFINED // libuv also defines ssize_t, use the one defined here.
|
||||
#endif // __SSIZE_T
|
||||
|
||||
#endif // #if defined(_WIN32) && defined(_WINDOWS)
|
||||
|
||||
/** @def SE_DEPRECATED_ATTRIBUTE
|
||||
* Only certain compilers support __attribute__((deprecated)).
|
||||
*/
|
||||
#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)))
|
||||
#define SE_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
|
||||
#elif _MSC_VER >= 1400 //vs 2005 or higher
|
||||
#define SE_DEPRECATED_ATTRIBUTE __declspec(deprecated)
|
||||
#else
|
||||
#define SE_DEPRECATED_ATTRIBUTE
|
||||
#endif // SE_DEPRECATED_ATTRIBUTE
|
||||
|
||||
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
|
||||
43
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Base.h
Normal file
43
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Base.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/****************************************************************************
|
||||
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
|
||||
|
||||
#ifndef UINT64_C
|
||||
#define UINT64_C(value) __CONCAT(value, ULL)
|
||||
#endif
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "js/Initialization.h"
|
||||
#include "js/Conversions.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <assert.h>
|
||||
|
||||
#include "HelperMacros.h"
|
||||
237
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Class.cpp
Normal file
237
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Class.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
/****************************************************************************
|
||||
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"
|
||||
#include "Object.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
namespace se {
|
||||
|
||||
// --- Global Lookup for Constructor Functions
|
||||
|
||||
namespace {
|
||||
// std::unordered_map<std::string, Class *> __clsMap;
|
||||
JSContext* __cx = nullptr;
|
||||
|
||||
bool empty_constructor(JSContext* cx, uint32_t argc, JS::Value* vp)
|
||||
{
|
||||
assert(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Class*> __allClasses;
|
||||
}
|
||||
|
||||
Class::Class()
|
||||
: _parent(nullptr)
|
||||
, _proto(nullptr)
|
||||
, _parentProto(nullptr)
|
||||
, _ctor(nullptr)
|
||||
, _finalizeOp(nullptr)
|
||||
{
|
||||
memset(&_jsCls, 0, sizeof(_jsCls));
|
||||
memset(&_classOps, 0, sizeof(_classOps));
|
||||
|
||||
__allClasses.push_back(this);
|
||||
}
|
||||
|
||||
Class::~Class()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Class* Class::create(const char* className, Object* obj, Object* parentProto, JSNative ctor)
|
||||
{
|
||||
Class* cls = new Class();
|
||||
if (cls != nullptr && !cls->init(className, obj, parentProto, ctor))
|
||||
{
|
||||
delete cls;
|
||||
cls = nullptr;
|
||||
}
|
||||
return cls;
|
||||
}
|
||||
|
||||
bool Class::init(const char* clsName, Object* parent, Object *parentProto, JSNative ctor)
|
||||
{
|
||||
_name = clsName;
|
||||
|
||||
_parent = parent;
|
||||
if (_parent != nullptr)
|
||||
_parent->incRef();
|
||||
|
||||
_parentProto = parentProto;
|
||||
if (_parentProto != nullptr)
|
||||
_parentProto->incRef();
|
||||
|
||||
_ctor = ctor;
|
||||
if (_ctor == nullptr)
|
||||
{
|
||||
_ctor = empty_constructor;
|
||||
}
|
||||
|
||||
// SE_LOGD("Class init ( %s ) ...\n", clsName);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Class::destroy()
|
||||
{
|
||||
SAFE_DEC_REF(_parent);
|
||||
SAFE_DEC_REF(_proto);
|
||||
SAFE_DEC_REF(_parentProto);
|
||||
}
|
||||
|
||||
bool Class::install()
|
||||
{
|
||||
// assert(__clsMap.find(_name) == __clsMap.end());
|
||||
//
|
||||
// __clsMap.emplace(_name, this);
|
||||
|
||||
_jsCls.name = _name;
|
||||
if (_finalizeOp != nullptr)
|
||||
{
|
||||
_jsCls.flags = JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE; //IDEA: Use JSCLASS_BACKGROUND_FINALIZE to improve GC performance
|
||||
_classOps.finalize = _finalizeOp;
|
||||
}
|
||||
else
|
||||
{
|
||||
_jsCls.flags = JSCLASS_HAS_PRIVATE;
|
||||
}
|
||||
|
||||
_jsCls.cOps = &_classOps;
|
||||
|
||||
JSObject* parentObj = _parentProto != nullptr ? _parentProto->_getJSObject() : nullptr;
|
||||
JS::RootedObject parent(__cx, _parent->_getJSObject());
|
||||
JS::RootedObject parentProto(__cx, parentObj);
|
||||
|
||||
_funcs.push_back(JS_FS_END);
|
||||
_properties.push_back(JS_PS_END);
|
||||
_staticFuncs.push_back(JS_FS_END);
|
||||
_staticProperties.push_back(JS_PS_END);
|
||||
|
||||
JSObject* jsobj = JS_InitClass(__cx, parent, parentProto, &_jsCls, _ctor, 0, _properties.data(), _funcs.data(), _staticProperties.data(), _staticFuncs.data());
|
||||
if (jsobj != nullptr)
|
||||
{
|
||||
_proto = Object::_createJSObject(nullptr, jsobj);
|
||||
// SE_LOGD("_proto: %p, name: %s\n", _proto, _name);
|
||||
_proto->root();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Class::defineFunction(const char *name, JSNative func)
|
||||
{
|
||||
JSFunctionSpec cb = JS_FN(name, func, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE);
|
||||
_funcs.push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineProperty(const char *name, JSNative getter, JSNative setter)
|
||||
{
|
||||
JSPropertySpec property = JS_PSGS(name, getter, setter, JSPROP_ENUMERATE | JSPROP_PERMANENT);
|
||||
_properties.push_back(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticFunction(const char *name, JSNative func)
|
||||
{
|
||||
JSFunctionSpec cb = JS_FN(name, func, 0, JSPROP_PERMANENT | JSPROP_ENUMERATE);
|
||||
_staticFuncs.push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticProperty(const char *name, JSNative getter, JSNative setter)
|
||||
{
|
||||
JSPropertySpec property = JS_PSGS(name, getter, setter, JSPROP_ENUMERATE | JSPROP_PERMANENT);
|
||||
_staticProperties.push_back(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineFinalizeFunction(JSFinalizeOp func)
|
||||
{
|
||||
_finalizeOp = func;
|
||||
return true;
|
||||
}
|
||||
|
||||
// JSObject* Class::_createJSObject(const std::string &clsName, Class** outCls)
|
||||
// {
|
||||
// auto iter = __clsMap.find(clsName);
|
||||
// if (iter == __clsMap.end())
|
||||
// {
|
||||
// *outCls = nullptr;
|
||||
// return nullptr;
|
||||
// }
|
||||
//
|
||||
// Class* thiz = iter->second;
|
||||
// JS::RootedObject obj(__cx, _createJSObjectWithClass(thiz));
|
||||
// *outCls = thiz;
|
||||
// return obj;
|
||||
// }
|
||||
|
||||
JSObject* Class::_createJSObjectWithClass(Class* cls)
|
||||
{
|
||||
JSObject* proto = cls->_proto != nullptr ? cls->_proto->_getJSObject() : nullptr;
|
||||
JS::RootedObject jsProto(__cx, proto);
|
||||
JS::RootedObject obj(__cx, JS_NewObjectWithGivenProto(__cx, &cls->_jsCls, jsProto));
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Class::setContext(JSContext* cx)
|
||||
{
|
||||
__cx = cx;
|
||||
}
|
||||
|
||||
Object *Class::getProto()
|
||||
{
|
||||
return _proto;
|
||||
}
|
||||
|
||||
JSFinalizeOp Class::_getFinalizeCb() const
|
||||
{
|
||||
return _finalizeOp;
|
||||
}
|
||||
|
||||
void Class::cleanup()
|
||||
{
|
||||
for (auto cls : __allClasses)
|
||||
{
|
||||
cls->destroy();
|
||||
}
|
||||
|
||||
se::ScriptEngine::getInstance()->addAfterCleanupHook([](){
|
||||
for (auto cls : __allClasses)
|
||||
{
|
||||
delete cls;
|
||||
}
|
||||
__allClasses.clear();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
154
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Class.hpp
Normal file
154
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Class.hpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
#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 char* className, Object* obj, Object* parentProto, JSNative 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, JSNative 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, JSNative getter, JSNative 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, JSNative 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, JSNative getter, JSNative 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(JSFinalizeOp 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; }
|
||||
|
||||
// Private API used in wrapper
|
||||
JSFinalizeOp _getFinalizeCb() const;
|
||||
//
|
||||
private:
|
||||
Class();
|
||||
~Class();
|
||||
|
||||
bool init(const char* clsName, Object* obj, Object* parentProto, JSNative ctor);
|
||||
void destroy();
|
||||
|
||||
// static JSObject* _createJSObject(const std::string &clsName, Class** outCls);
|
||||
static JSObject* _createJSObjectWithClass(Class* cls);
|
||||
|
||||
static void setContext(JSContext* cx);
|
||||
static void cleanup();
|
||||
|
||||
const char* _name;
|
||||
Object* _parent;
|
||||
Object* _proto;
|
||||
Object* _parentProto;
|
||||
|
||||
JSNative _ctor;
|
||||
|
||||
JSClass _jsCls;
|
||||
JSClassOps _classOps;
|
||||
|
||||
std::vector<JSFunctionSpec> _funcs;
|
||||
std::vector<JSFunctionSpec> _staticFuncs;
|
||||
std::vector<JSPropertySpec> _properties;
|
||||
std::vector<JSPropertySpec> _staticProperties;
|
||||
JSFinalizeOp _finalizeOp;
|
||||
|
||||
friend class ScriptEngine;
|
||||
friend class Object;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
extern uint32_t __jsbInvocationCount;
|
||||
|
||||
#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) \
|
||||
bool funcName##Registry(JSContext* _cx, unsigned argc, JS::Value* _vp)
|
||||
|
||||
#define SE_BIND_FUNC(funcName) \
|
||||
bool funcName##Registry(JSContext* _cx, unsigned argc, JS::Value* _vp) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
JS::Value _thiz = _argv.computeThis(_cx); \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, &args); \
|
||||
JS::RootedObject _thizObj(_cx, _thiz.toObjectOrNull()); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_cx, _thizObj); \
|
||||
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::setReturnValue(_cx, state.rval(), _argv); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define SE_DECLARE_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(JSFreeOp* _fop, JSObject* _obj);
|
||||
|
||||
#define SE_BIND_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(JSFreeOp* _fop, JSObject* _obj) \
|
||||
{ \
|
||||
void* nativeThisObject = JS_GetPrivate(_obj); \
|
||||
bool ret = false; \
|
||||
if (nativeThisObject == nullptr) \
|
||||
return;\
|
||||
se::State state(nativeThisObject); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_CTOR(funcName, cls, finalizeCb) \
|
||||
bool funcName##Registry(JSContext* _cx, unsigned argc, JS::Value* _vp) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, &args); \
|
||||
se::Object* thisObject = se::Object::createObjectWithClass(cls); \
|
||||
_argv.rval().setObject(*thisObject->_getJSObject()); \
|
||||
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 ret; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_SUB_CLS_CTOR(funcName, cls, finalizeCb) \
|
||||
bool funcName##Registry(JSContext* _cx, unsigned argc, JS::Value* _vp) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
JS::Value _thiz = _argv.computeThis(_cx); \
|
||||
se::ValueArray args; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, &args); \
|
||||
se::Object* thisObject = se::Object::_createJSObject(cls, _thiz.toObjectOrNull()); \
|
||||
thisObject->_setFinalizeCallback(finalizeCb##Registry); \
|
||||
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 ret; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_PROP_GET(funcName) \
|
||||
bool funcName##Registry(JSContext *_cx, unsigned argc, JS::Value* _vp) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
JS::Value _thiz = _argv.computeThis(_cx); \
|
||||
JS::RootedObject _thizObj(_cx, _thiz.toObjectOrNull()); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_cx, _thizObj); \
|
||||
se::State state(nativeThisObject); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
se::internal::setReturnValue(_cx, state.rval(), _argv); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_PROP_SET(funcName) \
|
||||
bool funcName##Registry(JSContext *_cx, unsigned _argc, JS::Value *_vp) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(_argc, _vp); \
|
||||
JS::Value _thiz = _argv.computeThis(_cx); \
|
||||
JS::RootedObject _thizObj(_cx, _thiz.toObjectOrNull()); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_cx, _thizObj); \
|
||||
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 ret; \
|
||||
}
|
||||
|
||||
|
||||
#define SE_TYPE_NAME(t) typeid(t).name()
|
||||
|
||||
#define SE_QUOTEME_(x) #x
|
||||
#define SE_QUOTEME(x) SE_QUOTEME_(x)
|
||||
|
||||
#define SE_REPORT_ERROR(fmt, ...) \
|
||||
SE_LOGD("ERROR (" __FILE__ ", " SE_QUOTEME(__LINE__) "): " fmt "\n", ##__VA_ARGS__); \
|
||||
JS_ReportErrorUTF8(se::ScriptEngine::getInstance()->_getContext(), fmt, ##__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_SM
|
||||
722
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Object.cpp
Normal file
722
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Object.cpp
Normal file
@@ -0,0 +1,722 @@
|
||||
/****************************************************************************
|
||||
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 "Object.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
#include "Utils.hpp"
|
||||
#include "Class.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
#include "../MappingUtils.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
std::unordered_map<Object*, void*> __objectMap; // Currently, the value `void*` is always nullptr
|
||||
|
||||
namespace {
|
||||
JSContext *__cx = nullptr;
|
||||
|
||||
void get_or_create_js_obj(JSContext* cx, JS::HandleObject obj, const std::string &name, JS::MutableHandleObject jsObj)
|
||||
{
|
||||
JS::RootedValue nsval(cx);
|
||||
JS_GetProperty(cx, obj, name.c_str(), &nsval);
|
||||
if (nsval.isNullOrUndefined()) {
|
||||
jsObj.set(JS_NewPlainObject(cx));
|
||||
nsval = JS::ObjectValue(*jsObj);
|
||||
JS_SetProperty(cx, obj, name.c_str(), nsval);
|
||||
} else {
|
||||
jsObj.set(nsval.toObjectOrNull());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object::Object()
|
||||
: _root(nullptr)
|
||||
, _privateData(nullptr)
|
||||
, _cls(nullptr)
|
||||
, _finalizeCb(nullptr)
|
||||
, _rootCount(0)
|
||||
{
|
||||
_currentVMId = ScriptEngine::getInstance()->getVMId();
|
||||
}
|
||||
|
||||
Object::~Object()
|
||||
{
|
||||
if (_rootCount > 0)
|
||||
{
|
||||
unprotect();
|
||||
}
|
||||
|
||||
auto iter = __objectMap.find(this);
|
||||
if (iter != __objectMap.end())
|
||||
{
|
||||
__objectMap.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
bool Object::init(Class* cls, JSObject* obj)
|
||||
{
|
||||
_cls = cls;
|
||||
_heap = obj;
|
||||
|
||||
assert(__objectMap.find(this) == __objectMap.end());
|
||||
__objectMap.emplace(this, nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Object* Object::_createJSObject(Class* cls, JSObject* obj)
|
||||
{
|
||||
Object* ret = new Object();
|
||||
if (!ret->init(cls, obj))
|
||||
{
|
||||
delete ret;
|
||||
ret = nullptr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Object* Object::createPlainObject()
|
||||
{
|
||||
Object* obj = Object::_createJSObject(nullptr, JS_NewPlainObject(__cx));
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createObjectWithClass(Class* cls)
|
||||
{
|
||||
JSObject* jsobj = Class::_createJSObjectWithClass(cls);
|
||||
Object* obj = Object::_createJSObject(cls, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::getObjectWithPtr(void* ptr)
|
||||
{
|
||||
Object* obj = nullptr;
|
||||
auto iter = NativePtrToObjectMap::find(ptr);
|
||||
if (iter != NativePtrToObjectMap::end())
|
||||
{
|
||||
obj = iter->second;
|
||||
obj->incRef();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createArrayObject(size_t length)
|
||||
{
|
||||
JS::RootedObject jsobj(__cx, JS_NewArrayObject(__cx, length));
|
||||
Object* obj = Object::_createJSObject(nullptr, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createArrayBufferObject(void* data, size_t byteLength)
|
||||
{
|
||||
JS::RootedObject jsobj(__cx, JS_NewArrayBuffer(__cx, (uint32_t)byteLength));
|
||||
bool isShared = false;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
uint8_t* tmpData = JS_GetArrayBufferData(jsobj, &isShared, nogc);
|
||||
if (data)
|
||||
{
|
||||
memcpy((void*)tmpData, (const void*)data, byteLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset((void*)tmpData, 0, byteLength);
|
||||
}
|
||||
Object* obj = Object::_createJSObject(nullptr, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createTypedArray(TypedArrayType type, void* data, size_t byteLength)
|
||||
{
|
||||
if (type == TypedArrayType::NONE)
|
||||
{
|
||||
SE_LOGE("Don't pass se::Object::TypedArrayType::NONE to createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type == TypedArrayType::UINT8_CLAMPED)
|
||||
{
|
||||
SE_LOGE("Doesn't support to create Uint8ClampedArray with Object::createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSObject* arr = nullptr;
|
||||
void* tmpData = nullptr;
|
||||
bool isShared = false;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
|
||||
switch (type) {
|
||||
case TypedArrayType::INT8:
|
||||
arr = JS_NewInt8Array(__cx, (uint32_t)byteLength);
|
||||
tmpData = JS_GetInt8ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::INT16:
|
||||
arr = JS_NewInt16Array(__cx, (uint32_t)byteLength/2);
|
||||
tmpData = JS_GetInt16ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::INT32:
|
||||
arr = JS_NewInt32Array(__cx, (uint32_t)byteLength/4);
|
||||
tmpData = JS_GetInt32ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::UINT8:
|
||||
arr = JS_NewUint8Array(__cx, (uint32_t)byteLength);
|
||||
tmpData = JS_GetUint8ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::UINT16:
|
||||
arr = JS_NewUint16Array(__cx, (uint32_t)byteLength/2);
|
||||
tmpData = JS_GetUint16ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::UINT32:
|
||||
arr = JS_NewUint32Array(__cx, (uint32_t)byteLength/4);
|
||||
tmpData = JS_GetUint32ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::FLOAT32:
|
||||
arr = JS_NewFloat32Array(__cx, (uint32_t)byteLength/4);
|
||||
tmpData = JS_GetFloat32ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
case TypedArrayType::FLOAT64:
|
||||
arr = JS_NewFloat64Array(__cx, (uint32_t)byteLength/8);
|
||||
tmpData = JS_GetFloat64ArrayData(arr, &isShared, nogc);
|
||||
break;
|
||||
default:
|
||||
assert(false); // Should never go here.
|
||||
break;
|
||||
}
|
||||
|
||||
//If data has content,then will copy data into buffer,or will only clear buffer.
|
||||
if (data) {
|
||||
memcpy(tmpData, (const void*)data, byteLength);
|
||||
}else{
|
||||
memset(tmpData, 0, byteLength);
|
||||
}
|
||||
|
||||
Object* obj = Object::_createJSObject(nullptr, arr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createUint8TypedArray(uint8_t* data, size_t dataCount)
|
||||
{
|
||||
return createTypedArray(TypedArrayType::UINT8, data, dataCount);
|
||||
}
|
||||
|
||||
Object* Object::createJSONObject(const std::string& jsonStr)
|
||||
{
|
||||
Value strVal(jsonStr);
|
||||
JS::RootedValue jsStr(__cx);
|
||||
internal::seToJsValue(__cx, strVal, &jsStr);
|
||||
JS::RootedValue jsObj(__cx);
|
||||
JS::RootedString rootedStr(__cx, jsStr.toString());
|
||||
Object* obj = nullptr;
|
||||
if (JS_ParseJSON(__cx, rootedStr, &jsObj))
|
||||
{
|
||||
obj = Object::_createJSObject(nullptr, jsObj.toObjectOrNull());
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Object::_setFinalizeCallback(JSFinalizeOp finalizeCb)
|
||||
{
|
||||
_finalizeCb = finalizeCb;
|
||||
}
|
||||
|
||||
bool Object::getProperty(const char* name, Value* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
data->setUndefined();
|
||||
|
||||
JSObject* jsobj = _getJSObject();
|
||||
if (jsobj == nullptr)
|
||||
return false;
|
||||
|
||||
JS::RootedObject object(__cx, jsobj);
|
||||
|
||||
bool found = false;
|
||||
bool ok = JS_HasProperty(__cx, object, name, &found);
|
||||
|
||||
if (!ok || !found)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::RootedValue rcValue(__cx);
|
||||
ok = JS_GetProperty(__cx, object, name, &rcValue);
|
||||
|
||||
if (ok && data)
|
||||
{
|
||||
internal::jsToSeValue(__cx, rcValue, data);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Object::setProperty(const char* name, const Value& v)
|
||||
{
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
|
||||
JS::RootedValue value(__cx);
|
||||
internal::seToJsValue(__cx, v, &value);
|
||||
return JS_SetProperty(__cx, object, name, value);
|
||||
}
|
||||
|
||||
bool Object::defineProperty(const char *name, JSNative getter, JSNative setter)
|
||||
{
|
||||
JS::RootedObject jsObj(__cx, _getJSObject());
|
||||
return JS_DefineProperty(__cx, jsObj, name, JS::UndefinedHandleValue, JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_SHARED, getter, setter);
|
||||
}
|
||||
|
||||
bool Object::call(const ValueArray& args, Object* thisObject, Value* rval/* = nullptr*/)
|
||||
{
|
||||
assert(isFunction());
|
||||
|
||||
JS::AutoValueVector jsarr(__cx);
|
||||
jsarr.reserve(args.size());
|
||||
internal::seToJsArgs(__cx, args, &jsarr);
|
||||
|
||||
JS::RootedObject contextObject(__cx);
|
||||
if (thisObject != nullptr)
|
||||
{
|
||||
contextObject.set(thisObject->_getJSObject());
|
||||
}
|
||||
|
||||
JSObject* funcObj = _getJSObject();
|
||||
JS::RootedValue func(__cx, JS::ObjectValue(*funcObj));
|
||||
JS::RootedValue rcValue(__cx);
|
||||
|
||||
bool ok = JS_CallFunctionValue(__cx, contextObject, func, jsarr, &rcValue);
|
||||
|
||||
if (ok)
|
||||
{
|
||||
if (rval != nullptr)
|
||||
internal::jsToSeValue(__cx, rcValue, rval);
|
||||
}
|
||||
else
|
||||
{
|
||||
se::ScriptEngine::getInstance()->clearException();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Object::defineFunction(const char *funcName, JSNative func)
|
||||
{
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
bool ok = JS_DefineFunction(__cx, object, funcName, func, 0, JSPROP_ENUMERATE | JSPROP_PERMANENT);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Object::getArrayLength(uint32_t* length) const
|
||||
{
|
||||
assert(length != nullptr);
|
||||
if (!isArray())
|
||||
return false;
|
||||
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
if (JS_GetArrayLength(__cx, object, length))
|
||||
return true;
|
||||
|
||||
*length = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::getArrayElement(uint32_t index, Value* data) const
|
||||
{
|
||||
assert(data != nullptr);
|
||||
if (!isArray())
|
||||
return false;
|
||||
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
JS::RootedValue rcValue(__cx);
|
||||
if (JS_GetElement(__cx, object, index, &rcValue))
|
||||
{
|
||||
internal::jsToSeValue(__cx, rcValue, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
data->setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::setArrayElement(uint32_t index, const Value& data)
|
||||
{
|
||||
if (!isArray())
|
||||
return false;
|
||||
|
||||
JS::RootedValue jsval(__cx);
|
||||
internal::seToJsValue(__cx, data, &jsval);
|
||||
JS::RootedObject thisObj(__cx, _getJSObject());
|
||||
return JS_SetElement(__cx, thisObj, index, jsval);
|
||||
}
|
||||
|
||||
bool Object::isFunction() const
|
||||
{
|
||||
return JS_ObjectIsFunction(__cx, _getJSObject());
|
||||
}
|
||||
|
||||
bool Object::_isNativeFunction(JSNative func) const
|
||||
{
|
||||
JSObject* obj = _getJSObject();
|
||||
return JS_ObjectIsFunction(__cx, obj) && JS_IsNativeFunction(obj, func);
|
||||
}
|
||||
|
||||
bool Object::isTypedArray() const
|
||||
{
|
||||
return JS_IsTypedArrayObject(_getJSObject());
|
||||
}
|
||||
|
||||
Object::TypedArrayType Object::getTypedArrayType() const
|
||||
{
|
||||
TypedArrayType ret = TypedArrayType::NONE;
|
||||
JSObject* obj = _getJSObject();
|
||||
if (JS_IsInt8Array(obj))
|
||||
ret = TypedArrayType::INT8;
|
||||
else if (JS_IsInt16Array(obj))
|
||||
ret = TypedArrayType::INT16;
|
||||
else if (JS_IsInt32Array(obj))
|
||||
ret = TypedArrayType::INT32;
|
||||
else if (JS_IsUint8Array(obj))
|
||||
ret = TypedArrayType::UINT8;
|
||||
else if (JS_IsUint8ClampedArray(obj))
|
||||
ret = TypedArrayType::UINT8_CLAMPED;
|
||||
else if (JS_IsUint16Array(obj))
|
||||
ret = TypedArrayType::UINT16;
|
||||
else if (JS_IsUint32Array(obj))
|
||||
ret = TypedArrayType::UINT32;
|
||||
else if (JS_IsFloat32Array(obj))
|
||||
ret = TypedArrayType::FLOAT32;
|
||||
else if (JS_IsFloat64Array(obj))
|
||||
ret = TypedArrayType::FLOAT64;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Object::getTypedArrayData(uint8_t** ptr, size_t* length) const
|
||||
{
|
||||
assert(JS_IsArrayBufferViewObject(_getJSObject()));
|
||||
bool isShared = false;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
*ptr = (uint8_t*)JS_GetArrayBufferViewData(_getJSObject(), &isShared, nogc);
|
||||
*length = JS_GetArrayBufferViewByteLength(_getJSObject());
|
||||
return (*ptr != nullptr);
|
||||
}
|
||||
|
||||
bool Object::isArray() const
|
||||
{
|
||||
JS::RootedValue value(__cx, JS::ObjectValue(*_getJSObject()));
|
||||
bool isArray = false;
|
||||
return JS_IsArrayObject(__cx, value, &isArray) && isArray;
|
||||
}
|
||||
|
||||
bool Object::isArrayBuffer() const
|
||||
{
|
||||
return JS_IsArrayBufferObject(_getJSObject());
|
||||
}
|
||||
|
||||
bool Object::getArrayBufferData(uint8_t** ptr, size_t* length) const
|
||||
{
|
||||
assert(isArrayBuffer());
|
||||
|
||||
bool isShared = false;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
*ptr = (uint8_t*)JS_GetArrayBufferData(_getJSObject(), &isShared, nogc);
|
||||
*length = JS_GetArrayBufferByteLength(_getJSObject());
|
||||
return (*ptr != nullptr);
|
||||
}
|
||||
|
||||
bool Object::getAllKeys(std::vector<std::string>* allKeys) const
|
||||
{
|
||||
assert(allKeys != nullptr);
|
||||
JS::RootedObject jsobj(__cx, _getJSObject());
|
||||
JS::Rooted<JS::IdVector> props(__cx, JS::IdVector(__cx));
|
||||
if (!JS_Enumerate(__cx, jsobj, &props))
|
||||
return false;
|
||||
|
||||
std::vector<std::string> keys;
|
||||
for (size_t i = 0, length = props.length(); i < length; ++i)
|
||||
{
|
||||
JS::RootedId id(__cx, props[i]);
|
||||
JS::RootedValue keyVal(__cx);
|
||||
JS_IdToValue(__cx, id, &keyVal);
|
||||
|
||||
if (JSID_IS_STRING(id))
|
||||
{
|
||||
JS::RootedString rootedKeyVal(__cx, keyVal.toString());
|
||||
allKeys->push_back(internal::jsToStdString(__cx, rootedKeyVal));
|
||||
}
|
||||
else if (JSID_IS_INT(id))
|
||||
{
|
||||
char buf[50] = {0};
|
||||
snprintf(buf, sizeof(buf), "%d", keyVal.toInt32());
|
||||
allKeys->push_back(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* Object::getPrivateData() const
|
||||
{
|
||||
if (_privateData == nullptr)
|
||||
{
|
||||
JS::RootedObject obj(__cx, _getJSObject());
|
||||
const_cast<Object*>(this)->_privateData = internal::getPrivate(__cx, obj);
|
||||
}
|
||||
return _privateData;
|
||||
}
|
||||
|
||||
void Object::setPrivateData(void* data)
|
||||
{
|
||||
assert(_privateData == nullptr);
|
||||
assert(NativePtrToObjectMap::find(data) == NativePtrToObjectMap::end());
|
||||
assert(_cls != nullptr);
|
||||
JS::RootedObject obj(__cx, _getJSObject());
|
||||
internal::setPrivate(__cx, obj, data, _finalizeCb);
|
||||
|
||||
NativePtrToObjectMap::emplace(data, this);
|
||||
_privateData = data;
|
||||
}
|
||||
|
||||
void Object::clearPrivateData(bool clearMapping)
|
||||
{
|
||||
if (_privateData != nullptr)
|
||||
{
|
||||
if (clearMapping)
|
||||
NativePtrToObjectMap::erase(_privateData);
|
||||
JS::RootedObject obj(__cx, _getJSObject());
|
||||
internal::clearPrivate(__cx, obj);
|
||||
_privateData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Object::setContext(JSContext *cx)
|
||||
{
|
||||
__cx = cx;
|
||||
}
|
||||
|
||||
// static
|
||||
void Object::cleanup()
|
||||
{
|
||||
for (const auto& e : __objectMap)
|
||||
{
|
||||
e.first->reset();
|
||||
}
|
||||
|
||||
ScriptEngine::getInstance()->addAfterCleanupHook([](){
|
||||
__objectMap.clear();
|
||||
const auto& instance = NativePtrToObjectMap::instance();
|
||||
for (const auto& e : instance)
|
||||
{
|
||||
e.second->decRef();
|
||||
}
|
||||
NativePtrToObjectMap::clear();
|
||||
NonRefNativePtrCreatedByCtorMap::clear();
|
||||
__cx = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
JSObject* Object::_getJSObject() const
|
||||
{
|
||||
return isRooted() ? _root->get() : _heap.get();
|
||||
}
|
||||
|
||||
void Object::root()
|
||||
{
|
||||
if (_rootCount == 0)
|
||||
{
|
||||
protect();
|
||||
}
|
||||
++_rootCount;
|
||||
}
|
||||
|
||||
void Object::unroot()
|
||||
{
|
||||
if (_rootCount > 0)
|
||||
{
|
||||
--_rootCount;
|
||||
if (_rootCount == 0)
|
||||
{
|
||||
unprotect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Object::protect()
|
||||
{
|
||||
assert(_root == nullptr);
|
||||
assert(_heap != JS::GCPolicy<JSObject*>::initial());
|
||||
|
||||
_root = new JS::PersistentRootedObject(__cx, _heap);
|
||||
_heap = JS::GCPolicy<JSObject*>::initial();
|
||||
}
|
||||
|
||||
void Object::unprotect()
|
||||
{
|
||||
if (_root == nullptr)
|
||||
return;
|
||||
|
||||
assert(_currentVMId == ScriptEngine::getInstance()->getVMId());
|
||||
assert(_heap == JS::GCPolicy<JSObject*>::initial());
|
||||
_heap = *_root;
|
||||
delete _root;
|
||||
_root = nullptr;
|
||||
}
|
||||
|
||||
void Object::reset()
|
||||
{
|
||||
if (_root != nullptr)
|
||||
{
|
||||
delete _root;
|
||||
_root = nullptr;
|
||||
}
|
||||
|
||||
_heap = JS::GCPolicy<JSObject*>::initial();
|
||||
}
|
||||
|
||||
/* Tracing makes no sense in the rooted case, because JS::PersistentRooted
|
||||
* already takes care of that. */
|
||||
void Object::trace(JSTracer* tracer, void* data)
|
||||
{
|
||||
assert(!isRooted());
|
||||
JS::TraceEdge(tracer, &_heap, "ccobj tracing");
|
||||
}
|
||||
|
||||
/* If not tracing, then you must call this method during GC in order to
|
||||
* update the object's location if it was moved, or null it out if it was
|
||||
* finalized. If the object was finalized, returns true. */
|
||||
bool Object::updateAfterGC(void* data)
|
||||
{
|
||||
assert(!isRooted());
|
||||
bool isGarbageCollected = false;
|
||||
internal::PrivateData* internalData = nullptr;
|
||||
|
||||
JSObject* oldPtr = _heap.unbarrieredGet();
|
||||
if (_heap.unbarrieredGet() != nullptr)
|
||||
JS_UpdateWeakPointerAfterGC(&_heap);
|
||||
|
||||
JSObject* newPtr = _heap.unbarrieredGet();
|
||||
|
||||
// IDEA: test to see ggc
|
||||
if (oldPtr != nullptr && newPtr != nullptr)
|
||||
{
|
||||
assert(oldPtr == newPtr);
|
||||
}
|
||||
isGarbageCollected = (newPtr == nullptr);
|
||||
if (isGarbageCollected && internalData != nullptr)
|
||||
{
|
||||
free(internalData);
|
||||
}
|
||||
return isGarbageCollected;
|
||||
}
|
||||
|
||||
bool Object::isRooted() const
|
||||
{
|
||||
return _rootCount > 0;
|
||||
}
|
||||
|
||||
bool Object::strictEquals(Object* o) const
|
||||
{
|
||||
JSObject* thisObj = _getJSObject();
|
||||
JSObject* oThisObj = o->_getJSObject();
|
||||
if ((thisObj == nullptr || oThisObj == nullptr) && thisObj != oThisObj)
|
||||
return false;
|
||||
|
||||
assert(thisObj);
|
||||
assert(oThisObj);
|
||||
JS::RootedValue v1(__cx, JS::ObjectValue(*_getJSObject()));
|
||||
JS::RootedValue v2(__cx, JS::ObjectValue(*o->_getJSObject()));
|
||||
bool same = false;
|
||||
bool ok = JS_SameValue(__cx, v1, v2, &same);
|
||||
return ok && same;
|
||||
}
|
||||
|
||||
bool Object::attachObject(Object* obj)
|
||||
{
|
||||
assert(obj);
|
||||
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal))
|
||||
return false;
|
||||
Object* jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("registerNativeRef", &func))
|
||||
return false;
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::detachObject(Object* obj)
|
||||
{
|
||||
assert(obj);
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal))
|
||||
return false;
|
||||
Object* jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("unregisterNativeRef", &func))
|
||||
return false;
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Object::toString() const
|
||||
{
|
||||
std::string ret;
|
||||
if (isFunction() || isArray() || isTypedArray())
|
||||
{
|
||||
JS::RootedValue val(__cx, JS::ObjectOrNullValue(_getJSObject()));
|
||||
internal::forceConvertJsValueToStdString(__cx, val, &ret);
|
||||
}
|
||||
else if (isArrayBuffer())
|
||||
{
|
||||
ret = "[object ArrayBuffer]";
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = "[object Object]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
399
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Object.hpp
Normal file
399
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Object.hpp
Normal file
@@ -0,0 +1,399 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
#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* data, 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* data, 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 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, JSNative getter, JSNative 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, JSNative 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, JSObject* obj);
|
||||
void _setFinalizeCallback(JSFinalizeOp finalizeCb);
|
||||
bool _isNativeFunction(JSNative func) const;
|
||||
JSObject* _getJSObject() const;
|
||||
Class* _getClass() const { return _cls; }
|
||||
//
|
||||
|
||||
private:
|
||||
Object();
|
||||
bool init(Class* cls, JSObject* obj);
|
||||
virtual ~Object();
|
||||
|
||||
static void setContext(JSContext* cx);
|
||||
static void cleanup();
|
||||
|
||||
void trace(JSTracer* tracer, void* data);
|
||||
bool updateAfterGC(void* data);
|
||||
|
||||
void protect();
|
||||
void unprotect();
|
||||
void reset();
|
||||
|
||||
JS::Heap<JSObject*> _heap; /* should be untouched if in rooted mode */
|
||||
JS::PersistentRootedObject* _root; /* should be null if not in rooted mode */
|
||||
|
||||
void* _privateData;
|
||||
|
||||
Class* _cls;
|
||||
JSFinalizeOp _finalizeCb;
|
||||
|
||||
uint32_t _rootCount;
|
||||
uint32_t _currentVMId;
|
||||
|
||||
friend class ScriptEngine;
|
||||
};
|
||||
|
||||
extern std::unordered_map<Object*, void*> __objectMap; // Currently, the value `void*` is always nullptr
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
1261
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/ScriptEngine.cpp
Normal file
1261
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/ScriptEngine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,310 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
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)
|
||||
{}
|
||||
|
||||
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() { JS_GC( _cx ); }
|
||||
|
||||
/**
|
||||
* @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 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
|
||||
JSContext* _getContext() { return _cx; }
|
||||
void _setGarbageCollecting(bool isGarbageCollecting);
|
||||
void _debugProcessInput(const std::string& str);
|
||||
//
|
||||
private:
|
||||
ScriptEngine();
|
||||
~ScriptEngine();
|
||||
|
||||
static void onWeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data);
|
||||
static void onWeakPointerZoneGroupCallback(JSContext* cx, void* data);
|
||||
|
||||
bool getScript(const std::string& path, JS::MutableHandleScript script);
|
||||
bool compileScript(const std::string& path, JS::MutableHandleScript script);
|
||||
|
||||
JSContext* _cx;
|
||||
JSCompartment* _oldCompartment;
|
||||
|
||||
Object* _globalObj;
|
||||
Object* _debugGlobalObj;
|
||||
|
||||
FileOperationDelegate _fileOperationDelegate;
|
||||
|
||||
std::vector<RegisterCallback> _registerCallbackArray;
|
||||
std::chrono::steady_clock::time_point _startTime;
|
||||
|
||||
std::vector<std::function<void()>> _beforeInitHookArray;
|
||||
std::vector<std::function<void()>> _afterInitHookArray;
|
||||
|
||||
std::vector<std::function<void()>> _beforeCleanupHookArray;
|
||||
std::vector<std::function<void()>> _afterCleanupHookArray;
|
||||
|
||||
ExceptionCallback _exceptionCallback;
|
||||
// name ~> JSScript map
|
||||
std::unordered_map<std::string, JS::PersistentRootedScript*> _filenameScriptMap;
|
||||
|
||||
std::string _debuggerServerAddr;
|
||||
uint32_t _debuggerServerPort;
|
||||
|
||||
uint32_t _vmId;
|
||||
|
||||
bool _isGarbageCollecting;
|
||||
bool _isValid;
|
||||
bool _isInCleanup;
|
||||
bool _isErrorHandleWorking;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
|
||||
33
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/SeApi.h
Normal file
33
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/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"
|
||||
280
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Utils.cpp
Normal file
280
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Utils.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
#include "Class.hpp"
|
||||
#include "Object.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
namespace internal {
|
||||
|
||||
void forceConvertJsValueToStdString(JSContext* cx, JS::HandleValue jsval, std::string* ret)
|
||||
{
|
||||
assert(ret != nullptr);
|
||||
JS::RootedString jsStr(cx, JS::ToString(cx, jsval));
|
||||
*ret = jsToStdString(cx, jsStr);
|
||||
}
|
||||
|
||||
std::string jsToStdString(JSContext* cx, JS::HandleString jsStr)
|
||||
{
|
||||
char* str = JS_EncodeStringToUTF8(cx, jsStr);
|
||||
std::string ret(str);
|
||||
JS_free(cx, str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jsToSeArgs(JSContext* cx, int argc, const JS::CallArgs& argv, ValueArray* outArr)
|
||||
{
|
||||
outArr->reserve(argc);
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
Value v;
|
||||
jsToSeValue(cx, argv[i], &v);
|
||||
outArr->push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsArgs(JSContext* cx, const ValueArray& args, JS::AutoValueVector* outArr)
|
||||
{
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
JS::RootedValue v(cx);
|
||||
seToJsValue(cx, arg, &v);
|
||||
outArr->append(v);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsValue(JSContext* cx, const Value& arg, JS::MutableHandleValue outVal)
|
||||
{
|
||||
switch( arg.getType())
|
||||
{
|
||||
case Value::Type::Number:
|
||||
{
|
||||
JS::RootedValue value(cx);
|
||||
value.setDouble(arg.toNumber());
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::String:
|
||||
{
|
||||
JS::UTF8Chars utf8Str(arg.toString().c_str(), arg.toString().length());
|
||||
JSString* string = JS_NewStringCopyUTF8N(cx, utf8Str);
|
||||
JS::RootedValue value(cx);
|
||||
value.setString(string);
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::Boolean:
|
||||
{
|
||||
JS::RootedValue value(cx);
|
||||
value.setBoolean(arg.toBoolean());
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::Object:
|
||||
{
|
||||
JS::RootedValue value(cx, JS::ObjectValue(*arg.toObject()->_getJSObject()));
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::Null:
|
||||
{
|
||||
JS::RootedValue value(cx);
|
||||
value.setNull();
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
|
||||
case Value::Type::Undefined:
|
||||
{
|
||||
JS::RootedValue value(cx);
|
||||
value.setUndefined();
|
||||
outVal.set(value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void jsToSeValue(JSContext* cx, JS::HandleValue jsval, Value* v)
|
||||
{
|
||||
if (jsval.isNumber())
|
||||
{
|
||||
v->setNumber(jsval.toNumber());
|
||||
}
|
||||
else if (jsval.isString())
|
||||
{
|
||||
JS::RootedString jsstr(cx, jsval.toString());
|
||||
v->setString(jsToStdString(cx, jsstr));
|
||||
}
|
||||
else if (jsval.isBoolean())
|
||||
{
|
||||
v->setBoolean(jsval.toBoolean());
|
||||
}
|
||||
else if (jsval.isObject())
|
||||
{
|
||||
Object* object = nullptr;
|
||||
|
||||
JS::RootedObject jsobj(cx, jsval.toObjectOrNull());
|
||||
void* nativeObj = getPrivate(cx, jsobj);
|
||||
|
||||
if (nativeObj != nullptr)
|
||||
{
|
||||
object = Object::getObjectWithPtr(nativeObj);
|
||||
}
|
||||
|
||||
if (object == nullptr)
|
||||
{
|
||||
object = Object::_createJSObject(nullptr, jsval.toObjectOrNull());
|
||||
}
|
||||
v->setObject(object, true);
|
||||
object->decRef();
|
||||
}
|
||||
else if (jsval.isNull())
|
||||
{
|
||||
v->setNull();
|
||||
}
|
||||
else if (jsval.isUndefined())
|
||||
{
|
||||
v->setUndefined();
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void setReturnValue(JSContext* cx, const Value& data, const JS::CallArgs& argv)
|
||||
{
|
||||
JS::RootedValue rval(cx);
|
||||
seToJsValue(cx, data, &rval);
|
||||
argv.rval().set(rval);
|
||||
}
|
||||
|
||||
const char* KEY_PRIVATE_DATA = "__cc_private_data";
|
||||
|
||||
bool hasPrivate(JSContext* cx, JS::HandleObject obj)
|
||||
{
|
||||
bool found = false;
|
||||
const JSClass* cls = JS_GetClass(obj);
|
||||
found = !!(cls->flags & JSCLASS_HAS_PRIVATE);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
JS::RootedObject jsobj(cx, obj);
|
||||
if (JS_HasProperty(cx, jsobj, KEY_PRIVATE_DATA, &found) && found)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void* getPrivate(JSContext* cx, JS::HandleObject obj)
|
||||
{
|
||||
bool found = false;
|
||||
const JSClass* cls = JS_GetClass(obj);
|
||||
found = !!(cls->flags & JSCLASS_HAS_PRIVATE);
|
||||
|
||||
if (found)
|
||||
{
|
||||
return JS_GetPrivate(obj);
|
||||
}
|
||||
|
||||
if (JS_HasProperty(cx, obj, KEY_PRIVATE_DATA, &found) && found)
|
||||
{
|
||||
JS::RootedValue jsData(cx);
|
||||
if (JS_GetProperty(cx, obj, KEY_PRIVATE_DATA, &jsData))
|
||||
{
|
||||
PrivateData* privateData = (PrivateData*)JS_GetPrivate(jsData.toObjectOrNull());
|
||||
return privateData->data;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setPrivate(JSContext* cx, JS::HandleObject obj, void* data, JSFinalizeOp finalizeCb)
|
||||
{
|
||||
bool found = false;
|
||||
const JSClass* jsCls = JS_GetClass(obj);
|
||||
found = !!(jsCls->flags & JSCLASS_HAS_PRIVATE);
|
||||
|
||||
if (found)
|
||||
{
|
||||
JS_SetPrivate(obj, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(finalizeCb);
|
||||
Object* privateObj = Object::createObjectWithClass(__jsb_CCPrivateData_class);
|
||||
PrivateData* privateData = (PrivateData*)malloc(sizeof(PrivateData));
|
||||
privateData->data = data;
|
||||
privateData->finalizeCb = finalizeCb;
|
||||
JS_SetPrivate(privateObj->_getJSObject(), privateData);
|
||||
|
||||
JS::RootedValue privateVal(cx, JS::ObjectValue(*privateObj->_getJSObject()));
|
||||
JS_SetProperty(cx, obj, KEY_PRIVATE_DATA, privateVal);
|
||||
privateObj->decRef();
|
||||
}
|
||||
}
|
||||
|
||||
void clearPrivate(JSContext* cx, JS::HandleObject obj)
|
||||
{
|
||||
bool found = false;
|
||||
const JSClass* cls = JS_GetClass(obj);
|
||||
found = !!(cls->flags & JSCLASS_HAS_PRIVATE);
|
||||
|
||||
if (found)
|
||||
{
|
||||
JS_SetPrivate(obj, nullptr);
|
||||
}
|
||||
else if (JS_HasProperty(cx, obj, KEY_PRIVATE_DATA, &found) && found)
|
||||
{
|
||||
JS::RootedValue jsData(cx);
|
||||
assert(JS_GetProperty(cx, obj, KEY_PRIVATE_DATA, &jsData));
|
||||
|
||||
PrivateData* privateData = (PrivateData*)JS_GetPrivate(jsData.toObjectOrNull());
|
||||
free(privateData);
|
||||
JS_SetPrivate(jsData.toObjectOrNull(), nullptr);
|
||||
bool ok = JS_DeleteProperty(cx, obj, KEY_PRIVATE_DATA);
|
||||
assert(ok);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace se { namespace internal {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
66
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Utils.hpp
Normal file
66
cocos2d-x/cocos/scripting/js-bindings/jswrapper/sm/Utils.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
#include "../Value.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Class;
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct PrivateData
|
||||
{
|
||||
void* data;
|
||||
JSFinalizeOp finalizeCb;
|
||||
};
|
||||
|
||||
void forceConvertJsValueToStdString(JSContext* cx, JS::HandleValue jsval, std::string* ret);
|
||||
std::string jsToStdString(JSContext* cx, JS::HandleString jsStr);
|
||||
|
||||
void jsToSeArgs(JSContext* cx, int argc, const JS::CallArgs& argv, ValueArray* outArr);
|
||||
void jsToSeValue(JSContext *cx, JS::HandleValue jsval, Value* v);
|
||||
void seToJsArgs(JSContext* cx, const ValueArray& args, JS::AutoValueVector* outArr);
|
||||
void seToJsValue(JSContext* cx, const Value& v, JS::MutableHandleValue outVal);
|
||||
|
||||
void setReturnValue(JSContext* cx, const Value& data, const JS::CallArgs& argv);
|
||||
|
||||
bool hasPrivate(JSContext* cx, JS::HandleObject obj);
|
||||
void* getPrivate(JSContext* cx, JS::HandleObject obj);
|
||||
void setPrivate(JSContext* cx, JS::HandleObject obj, void* data, JSFinalizeOp finalizeCb);
|
||||
void clearPrivate(JSContext* cx, JS::HandleObject obj);
|
||||
|
||||
} // namespace internal {
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
49
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Base.h
Normal file
49
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Base.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/****************************************************************************
|
||||
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 "libplatform/libplatform.h"
|
||||
|
||||
//#define V8_DEPRECATION_WARNINGS 1
|
||||
//#define V8_IMMINENT_DEPRECATION_WARNINGS 1
|
||||
//#define V8_HAS_ATTRIBUTE_DEPRECATED_MESSAGE 1
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include <string>
|
||||
#include <string.h> // Resolves that memset, memcpy aren't found while APP_PLATFORM >= 22 on Android
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <algorithm> // for std::find
|
||||
#include <chrono>
|
||||
#include <assert.h>
|
||||
|
||||
#include "HelperMacros.h"
|
||||
|
||||
|
||||
namespace se {
|
||||
using V8FinalizeFunc = void (*)(void* nativeObj);
|
||||
}
|
||||
246
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Class.cpp
Normal file
246
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Class.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
/****************************************************************************
|
||||
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_V8
|
||||
|
||||
#include "Object.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
|
||||
namespace se {
|
||||
// ------------------------------------------------------- Object
|
||||
|
||||
namespace {
|
||||
// std::unordered_map<std::string, Class *> __clsMap;
|
||||
v8::Isolate* __isolate = nullptr;
|
||||
std::vector<Class*> __allClasses;
|
||||
}
|
||||
|
||||
Class::Class()
|
||||
: _parent(nullptr)
|
||||
, _parentProto(nullptr)
|
||||
, _proto(nullptr)
|
||||
, _ctor(nullptr)
|
||||
, _finalizeFunc(nullptr)
|
||||
, _createProto(true)
|
||||
{
|
||||
__allClasses.push_back(this);
|
||||
}
|
||||
|
||||
Class::~Class()
|
||||
{
|
||||
}
|
||||
|
||||
/* static */
|
||||
Class* Class::create(const std::string& clsName, se::Object* parent, Object* parentProto, v8::FunctionCallback ctor)
|
||||
{
|
||||
Class* cls = new Class();
|
||||
if (cls != nullptr && !cls->init(clsName, parent, parentProto, ctor))
|
||||
{
|
||||
delete cls;
|
||||
cls = nullptr;
|
||||
}
|
||||
return cls;
|
||||
}
|
||||
|
||||
bool Class::init(const std::string& clsName, Object* parent, Object* parentProto, v8::FunctionCallback ctor)
|
||||
{
|
||||
_name = clsName;
|
||||
|
||||
_parent = parent;
|
||||
if (_parent != nullptr)
|
||||
_parent->incRef();
|
||||
|
||||
_parentProto = parentProto;
|
||||
if (_parentProto != nullptr)
|
||||
_parentProto->incRef();
|
||||
|
||||
_ctor = ctor;
|
||||
|
||||
_ctorTemplate.Reset(__isolate, v8::FunctionTemplate::New(__isolate, _ctor));
|
||||
v8::MaybeLocal<v8::String> jsNameVal = v8::String::NewFromUtf8(__isolate, _name.c_str(), v8::NewStringType::kNormal);
|
||||
if (jsNameVal.IsEmpty())
|
||||
return false;
|
||||
|
||||
_ctorTemplate.Get(__isolate)->SetClassName(jsNameVal.ToLocalChecked());
|
||||
_ctorTemplate.Get(__isolate)->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Class::destroy()
|
||||
{
|
||||
SAFE_DEC_REF(_parent);
|
||||
SAFE_DEC_REF(_proto);
|
||||
SAFE_DEC_REF(_parentProto);
|
||||
_ctorTemplate.Reset();
|
||||
}
|
||||
|
||||
void Class::cleanup()
|
||||
{
|
||||
for (auto cls : __allClasses)
|
||||
{
|
||||
cls->destroy();
|
||||
}
|
||||
|
||||
se::ScriptEngine::getInstance()->addAfterCleanupHook([](){
|
||||
for (auto cls : __allClasses)
|
||||
{
|
||||
delete cls;
|
||||
}
|
||||
__allClasses.clear();
|
||||
});
|
||||
}
|
||||
|
||||
void Class::setCreateProto(bool createProto)
|
||||
{
|
||||
_createProto = createProto;
|
||||
}
|
||||
|
||||
bool Class::install()
|
||||
{
|
||||
// assert(__clsMap.find(_name) == __clsMap.end());
|
||||
//
|
||||
// __clsMap.emplace(_name, this);
|
||||
|
||||
if (_parentProto != nullptr)
|
||||
{
|
||||
_ctorTemplate.Get(__isolate)->Inherit(_parentProto->_getClass()->_ctorTemplate.Get(__isolate));
|
||||
}
|
||||
|
||||
v8::Local<v8::Context> context = __isolate->GetCurrentContext();
|
||||
v8::MaybeLocal<v8::Function> ctor = _ctorTemplate.Get(__isolate)->GetFunction(context);
|
||||
if (ctor.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Local<v8::Function> ctorChecked = ctor.ToLocalChecked();
|
||||
v8::MaybeLocal<v8::String> name = v8::String::NewFromUtf8(__isolate, _name.c_str(), v8::NewStringType::kNormal);
|
||||
if (name.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Maybe<bool> result = _parent->_getJSObject()->Set(context, name.ToLocalChecked(), ctorChecked);
|
||||
if (result.IsNothing())
|
||||
return false;
|
||||
|
||||
v8::MaybeLocal<v8::String> prototypeName = v8::String::NewFromUtf8(__isolate, "prototype", v8::NewStringType::kNormal);
|
||||
if (prototypeName.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::MaybeLocal<v8::Value> prototypeObj = ctorChecked->Get(context, prototypeName.ToLocalChecked());
|
||||
if (prototypeObj.IsEmpty())
|
||||
return false;
|
||||
|
||||
if (_createProto)
|
||||
{
|
||||
// Proto object is released in Class::destroy.
|
||||
_proto = Object::_createJSObject(this, v8::Local<v8::Object>::Cast(prototypeObj.ToLocalChecked()));
|
||||
_proto->root();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineFunction(const char *name, v8::FunctionCallback func)
|
||||
{
|
||||
v8::MaybeLocal<v8::String> jsName = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
|
||||
if (jsName.IsEmpty())
|
||||
return false;
|
||||
_ctorTemplate.Get(__isolate)->PrototypeTemplate()->Set(jsName.ToLocalChecked(), v8::FunctionTemplate::New(__isolate, func));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineProperty(const char *name, v8::AccessorNameGetterCallback getter, v8::AccessorNameSetterCallback setter)
|
||||
{
|
||||
v8::MaybeLocal<v8::String> jsName = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
|
||||
if (jsName.IsEmpty())
|
||||
return false;
|
||||
_ctorTemplate.Get(__isolate)->PrototypeTemplate()->SetAccessor(jsName.ToLocalChecked(), getter, setter);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticFunction(const char *name, v8::FunctionCallback func)
|
||||
{
|
||||
v8::MaybeLocal<v8::String> jsName = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
|
||||
if (jsName.IsEmpty())
|
||||
return false;
|
||||
_ctorTemplate.Get(__isolate)->Set(jsName.ToLocalChecked(), v8::FunctionTemplate::New(__isolate, func));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticProperty(const char *name, v8::AccessorNameGetterCallback getter, v8::AccessorNameSetterCallback setter)
|
||||
{
|
||||
v8::MaybeLocal<v8::String> jsName = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
|
||||
if (jsName.IsEmpty())
|
||||
return false;
|
||||
_ctorTemplate.Get(__isolate)->SetNativeDataProperty(jsName.ToLocalChecked(), getter, setter);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineFinalizeFunction(V8FinalizeFunc finalizeFunc)
|
||||
{
|
||||
assert(finalizeFunc != nullptr);
|
||||
_finalizeFunc = finalizeFunc;
|
||||
return true;
|
||||
}
|
||||
|
||||
// v8::Local<v8::Object> Class::_createJSObject(const std::string &clsName, Class** outCls)
|
||||
// {
|
||||
// auto iter = __clsMap.find(clsName);
|
||||
// if (iter == __clsMap.end())
|
||||
// {
|
||||
// *outCls = nullptr;
|
||||
// return v8::Local<v8::Object>::Cast(v8::Undefined(__isolate));
|
||||
// }
|
||||
//
|
||||
// *outCls = iter->second;
|
||||
// return _createJSObjectWithClass(iter->second);
|
||||
// }
|
||||
|
||||
v8::Local<v8::Object> Class::_createJSObjectWithClass(Class* cls)
|
||||
{
|
||||
v8::MaybeLocal<v8::Object> ret = cls->_ctorTemplate.Get(__isolate)->InstanceTemplate()->NewInstance(__isolate->GetCurrentContext());
|
||||
assert(!ret.IsEmpty());
|
||||
return ret.ToLocalChecked();
|
||||
}
|
||||
|
||||
Object* Class::getProto() const
|
||||
{
|
||||
return _proto;
|
||||
}
|
||||
|
||||
V8FinalizeFunc Class::_getFinalizeFunction() const
|
||||
{
|
||||
return _finalizeFunc;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void Class::setIsolate(v8::Isolate* isolate)
|
||||
{
|
||||
__isolate = isolate;
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
147
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Class.hpp
Normal file
147
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Class.hpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/****************************************************************************
|
||||
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_V8
|
||||
|
||||
#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, v8::FunctionCallback 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, v8::FunctionCallback 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, v8::AccessorNameGetterCallback getter, v8::AccessorNameSetterCallback 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, v8::FunctionCallback 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, v8::AccessorNameGetterCallback getter, v8::AccessorNameSetterCallback 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(V8FinalizeFunc 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() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the class name.
|
||||
* @return The class name.
|
||||
*/
|
||||
const char* getName() const { return _name.c_str(); }
|
||||
|
||||
// Private API used in wrapper
|
||||
V8FinalizeFunc _getFinalizeFunction() const;
|
||||
private:
|
||||
Class();
|
||||
~Class();
|
||||
|
||||
void setCreateProto(bool createProto);
|
||||
|
||||
bool init(const std::string& clsName, Object* parent, Object* parentProto, v8::FunctionCallback ctor);
|
||||
void destroy();
|
||||
|
||||
static void cleanup();
|
||||
// static v8::Local<v8::Object> _createJSObject(const std::string &clsName, Class** outCls);
|
||||
static v8::Local<v8::Object> _createJSObjectWithClass(Class* cls);
|
||||
static void setIsolate(v8::Isolate* isolate);
|
||||
|
||||
std::string _name;
|
||||
Object* _parent;
|
||||
Object* _parentProto;
|
||||
Object* _proto;
|
||||
|
||||
v8::FunctionCallback _ctor;
|
||||
v8::UniquePersistent<v8::FunctionTemplate> _ctorTemplate;
|
||||
V8FinalizeFunc _finalizeFunc;
|
||||
bool _createProto;
|
||||
|
||||
friend class ScriptEngine;
|
||||
friend class Object;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
@@ -0,0 +1,179 @@
|
||||
/****************************************************************************
|
||||
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_V8
|
||||
|
||||
extern uint32_t __jsbInvocationCount;
|
||||
|
||||
#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) \
|
||||
void funcName##Registry(const v8::FunctionCallbackInfo<v8::Value>& v8args)
|
||||
|
||||
|
||||
#define SE_BIND_FUNC(funcName) \
|
||||
void funcName##Registry(const v8::FunctionCallbackInfo<v8::Value>& _v8args) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
bool ret = false; \
|
||||
v8::Isolate* _isolate = _v8args.GetIsolate(); \
|
||||
v8::HandleScope _hs(_isolate); \
|
||||
SE_UNUSED unsigned argc = (unsigned)_v8args.Length(); \
|
||||
se::ValueArray args; \
|
||||
args.reserve(10); \
|
||||
se::internal::jsToSeArgs(_v8args, &args); \
|
||||
void* nativeThisObject = se::internal::getPrivate(_isolate, _v8args.This()); \
|
||||
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::setReturnValue(state.rval(), _v8args); \
|
||||
}
|
||||
|
||||
#define SE_BIND_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(void* nativeThisObject) \
|
||||
{ \
|
||||
if (nativeThisObject == nullptr) \
|
||||
return; \
|
||||
auto se = se::ScriptEngine::getInstance(); \
|
||||
se->_setGarbageCollecting(true); \
|
||||
se::State state(nativeThisObject); \
|
||||
bool ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
se->_setGarbageCollecting(false); \
|
||||
}
|
||||
|
||||
#define SE_DECLARE_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(void* nativeThisObject);
|
||||
|
||||
// v8 doesn't need to create a new JSObject in SE_BIND_CTOR while SpiderMonkey needs.
|
||||
#define SE_BIND_CTOR(funcName, cls, finalizeCb) \
|
||||
void funcName##Registry(const v8::FunctionCallbackInfo<v8::Value>& _v8args) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
v8::Isolate* _isolate = _v8args.GetIsolate(); \
|
||||
v8::HandleScope _hs(_isolate); \
|
||||
bool ret = true; \
|
||||
se::ValueArray args; \
|
||||
args.reserve(10); \
|
||||
se::internal::jsToSeArgs(_v8args, &args); \
|
||||
se::Object* thisObject = se::Object::_createJSObject(cls, _v8args.This()); \
|
||||
thisObject->_setFinalizeCallback(_SE(finalizeCb)); \
|
||||
se::State state(thisObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
se::Value _property; \
|
||||
bool _found = false; \
|
||||
_found = thisObject->getProperty("_ctor", &_property); \
|
||||
if (_found) _property.toObject()->call(args, thisObject); \
|
||||
}
|
||||
|
||||
#define SE_BIND_SUB_CLS_CTOR SE_BIND_CTOR
|
||||
|
||||
|
||||
#define SE_BIND_PROP_GET(funcName) \
|
||||
void funcName##Registry(v8::Local<v8::Name> _property, const v8::PropertyCallbackInfo<v8::Value>& _v8args) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
v8::Isolate* _isolate = _v8args.GetIsolate(); \
|
||||
v8::HandleScope _hs(_isolate); \
|
||||
bool ret = true; \
|
||||
void* nativeThisObject = se::internal::getPrivate(_isolate, _v8args.This()); \
|
||||
se::State state(nativeThisObject); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
se::internal::setReturnValue(state.rval(), _v8args); \
|
||||
}
|
||||
|
||||
|
||||
#define SE_BIND_PROP_SET(funcName) \
|
||||
void funcName##Registry(v8::Local<v8::Name> _property, v8::Local<v8::Value> _value, const v8::PropertyCallbackInfo<void>& _v8args) \
|
||||
{ \
|
||||
++__jsbInvocationCount; \
|
||||
v8::Isolate* _isolate = _v8args.GetIsolate(); \
|
||||
v8::HandleScope _hs(_isolate); \
|
||||
bool ret = true; \
|
||||
void* nativeThisObject = se::internal::getPrivate(_isolate, _v8args.This()); \
|
||||
se::Value data; \
|
||||
se::internal::jsToSeValue(_isolate, _value, &data); \
|
||||
se::ValueArray args; \
|
||||
args.reserve(10); \
|
||||
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__); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#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_V8
|
||||
807
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Object.cpp
Normal file
807
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Object.cpp
Normal file
@@ -0,0 +1,807 @@
|
||||
/****************************************************************************
|
||||
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 "Object.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
#include "Utils.hpp"
|
||||
#include "Class.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
#include "../MappingUtils.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace se {
|
||||
|
||||
|
||||
std::unique_ptr<std::unordered_map<Object*, void*>> __objectMap; // Currently, the value `void*` is always nullptr
|
||||
|
||||
namespace {
|
||||
v8::Isolate* __isolate = nullptr;
|
||||
}
|
||||
|
||||
Object::Object()
|
||||
: _cls(nullptr)
|
||||
, _rootCount(0)
|
||||
, _privateData(nullptr)
|
||||
, _finalizeCb(nullptr)
|
||||
, _internalData(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Object::~Object()
|
||||
{
|
||||
if (_rootCount > 0)
|
||||
{
|
||||
_obj.unref();
|
||||
}
|
||||
|
||||
if(__objectMap){
|
||||
__objectMap->erase(this);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/
|
||||
void Object::nativeObjectFinalizeHook(void* nativeObj)
|
||||
{
|
||||
if (nativeObj == nullptr)
|
||||
return;
|
||||
|
||||
auto iter = NativePtrToObjectMap::find(nativeObj);
|
||||
if (iter != NativePtrToObjectMap::end())
|
||||
{
|
||||
Object* obj = iter->second;
|
||||
if (obj->_finalizeCb != nullptr)
|
||||
{
|
||||
obj->_finalizeCb(nativeObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(obj->_getClass() != nullptr);
|
||||
if (obj->_getClass()->_finalizeFunc != nullptr)
|
||||
obj->_getClass()->_finalizeFunc(nativeObj);
|
||||
}
|
||||
obj->decRef();
|
||||
NativePtrToObjectMap::erase(iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void Object::setIsolate(v8::Isolate* isolate)
|
||||
{
|
||||
__isolate = isolate;
|
||||
}
|
||||
|
||||
void Object::setup()
|
||||
{
|
||||
__objectMap.reset(new std::unordered_map<Object*, void*>());
|
||||
}
|
||||
|
||||
/* static */
|
||||
void Object::cleanup()
|
||||
{
|
||||
void* nativeObj = nullptr;
|
||||
Object* obj = nullptr;
|
||||
Class* cls = nullptr;
|
||||
|
||||
const auto& nativePtrToObjectMap = NativePtrToObjectMap::instance();
|
||||
for (const auto& e : nativePtrToObjectMap)
|
||||
{
|
||||
nativeObj = e.first;
|
||||
obj = e.second;
|
||||
|
||||
if (obj->_finalizeCb != nullptr)
|
||||
{
|
||||
obj->_finalizeCb(nativeObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (obj->_getClass() != nullptr)
|
||||
{
|
||||
if (obj->_getClass()->_finalizeFunc != nullptr)
|
||||
{
|
||||
obj->_getClass()->_finalizeFunc(nativeObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
// internal data should only be freed in Object::cleanup, since in other case, it is freed in ScriptEngine::privateDataFinalize
|
||||
if (obj->_internalData != nullptr)
|
||||
{
|
||||
free(obj->_internalData);
|
||||
obj->_internalData = nullptr;
|
||||
}
|
||||
obj->decRef();
|
||||
}
|
||||
|
||||
NativePtrToObjectMap::clear();
|
||||
NonRefNativePtrCreatedByCtorMap::clear();
|
||||
|
||||
if(__objectMap){
|
||||
std::vector<Object*> toReleaseObjects;
|
||||
for (const auto& e : *__objectMap)
|
||||
{
|
||||
obj = e.first;
|
||||
cls = obj->_getClass();
|
||||
obj->_obj.persistent().Reset();
|
||||
obj->_rootCount = 0;
|
||||
|
||||
if (cls != nullptr && cls->_name == "__PrivateData")
|
||||
{
|
||||
toReleaseObjects.push_back(obj);
|
||||
}
|
||||
}
|
||||
for (auto e : toReleaseObjects)
|
||||
{
|
||||
e->decRef();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
__objectMap.reset();
|
||||
__isolate = nullptr;
|
||||
}
|
||||
|
||||
Object* Object::createPlainObject()
|
||||
{
|
||||
v8::Local<v8::Object> jsobj = v8::Object::New(__isolate);
|
||||
Object* obj = _createJSObject(nullptr, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::getObjectWithPtr(void* ptr)
|
||||
{
|
||||
Object* obj = nullptr;
|
||||
auto iter = NativePtrToObjectMap::find(ptr);
|
||||
if (iter != NativePtrToObjectMap::end())
|
||||
{
|
||||
obj = iter->second;
|
||||
obj->incRef();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::_createJSObject(Class* cls, v8::Local<v8::Object> obj)
|
||||
{
|
||||
Object* ret = new Object();
|
||||
if (!ret->init(cls, obj))
|
||||
{
|
||||
delete ret;
|
||||
ret = nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Object* Object::createObjectWithClass(Class* cls)
|
||||
{
|
||||
v8::Local<v8::Object> jsobj = Class::_createJSObjectWithClass(cls);
|
||||
Object* obj = Object::_createJSObject(cls, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createArrayObject(size_t length)
|
||||
{
|
||||
v8::Local<v8::Array> jsobj = v8::Array::New(__isolate, (int)length);
|
||||
Object* obj = Object::_createJSObject(nullptr, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createArrayBufferObject(void* data, size_t byteLength)
|
||||
{
|
||||
v8::Local<v8::ArrayBuffer> jsobj = v8::ArrayBuffer::New(__isolate, byteLength);
|
||||
if (data)
|
||||
{
|
||||
memcpy(jsobj->GetContents().Data(), data, byteLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(jsobj->GetContents().Data(), 0, byteLength);
|
||||
}
|
||||
Object* obj = Object::_createJSObject(nullptr, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createTypedArray(TypedArrayType type, void* data, size_t byteLength)
|
||||
{
|
||||
if (type == TypedArrayType::NONE)
|
||||
{
|
||||
SE_LOGE("Don't pass se::Object::TypedArrayType::NONE to createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type == TypedArrayType::UINT8_CLAMPED)
|
||||
{
|
||||
SE_LOGE("Doesn't support to create Uint8ClampedArray with Object::createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
v8::Local<v8::ArrayBuffer> jsobj = v8::ArrayBuffer::New(__isolate, byteLength);
|
||||
//If data has content,then will copy data into buffer,or will only clear buffer.
|
||||
if (data) {
|
||||
memcpy(jsobj->GetContents().Data(), data, byteLength);
|
||||
}else{
|
||||
memset(jsobj->GetContents().Data(), 0, byteLength);
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> arr;
|
||||
switch (type) {
|
||||
case TypedArrayType::INT8:
|
||||
arr = v8::Int8Array::New(jsobj, 0, byteLength);
|
||||
break;
|
||||
case TypedArrayType::INT16:
|
||||
arr = v8::Int16Array::New(jsobj, 0, byteLength / 2);
|
||||
break;
|
||||
case TypedArrayType::INT32:
|
||||
arr = v8::Int32Array::New(jsobj, 0, byteLength / 4);
|
||||
break;
|
||||
case TypedArrayType::UINT8:
|
||||
arr = v8::Uint8Array::New(jsobj, 0, byteLength);
|
||||
break;
|
||||
case TypedArrayType::UINT16:
|
||||
arr = v8::Uint16Array::New(jsobj, 0, byteLength / 2);
|
||||
break;
|
||||
case TypedArrayType::UINT32:
|
||||
arr = v8::Uint32Array::New(jsobj, 0, byteLength / 4);
|
||||
break;
|
||||
case TypedArrayType::FLOAT32:
|
||||
arr = v8::Float32Array::New(jsobj, 0, byteLength / 4);
|
||||
break;
|
||||
case TypedArrayType::FLOAT64:
|
||||
arr = v8::Float64Array::New(jsobj, 0, byteLength / 8);
|
||||
break;
|
||||
default:
|
||||
assert(false); // Should never go here.
|
||||
break;
|
||||
}
|
||||
|
||||
Object* obj = Object::_createJSObject(nullptr, arr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createUint8TypedArray(uint8_t* data, size_t dataCount)
|
||||
{
|
||||
return createTypedArray(TypedArrayType::UINT8, data, dataCount);
|
||||
}
|
||||
|
||||
Object* Object::createJSONObject(const std::string& jsonStr)
|
||||
{
|
||||
v8::Local<v8::Context> context = __isolate->GetCurrentContext();
|
||||
Value strVal(jsonStr);
|
||||
v8::Local<v8::Value> jsStr;
|
||||
internal::seToJsValue(__isolate, strVal, &jsStr);
|
||||
v8::Local<v8::String> v8Str = v8::Local<v8::String>::Cast(jsStr);
|
||||
v8::MaybeLocal<v8::Value> ret = v8::JSON::Parse(context, v8Str);
|
||||
if (ret.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
v8::Local<v8::Object> jsobj = v8::Local<v8::Object>::Cast(ret.ToLocalChecked());
|
||||
return Object::_createJSObject(nullptr, jsobj);
|
||||
}
|
||||
|
||||
bool Object::init(Class* cls, v8::Local<v8::Object> obj)
|
||||
{
|
||||
_cls = cls;
|
||||
|
||||
_obj.init(obj);
|
||||
_obj.setFinalizeCallback(nativeObjectFinalizeHook);
|
||||
|
||||
if(__objectMap){
|
||||
assert(__objectMap->find(this) == __objectMap->end());
|
||||
__objectMap->emplace(this, nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::getProperty(const char *name, Value *data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
data->setUndefined();
|
||||
|
||||
v8::HandleScope handle_scope(__isolate);
|
||||
|
||||
if (_obj.persistent().IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
v8::MaybeLocal<v8::String> nameValue = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
|
||||
if (nameValue.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Local<v8::String> nameValToLocal = nameValue.ToLocalChecked();
|
||||
v8::Local<v8::Context> context = __isolate->GetCurrentContext();
|
||||
v8::Maybe<bool> maybeExist = _obj.handle(__isolate)->Has(context, nameValToLocal);
|
||||
if (maybeExist.IsNothing())
|
||||
return false;
|
||||
|
||||
|
||||
if (!maybeExist.FromJust())
|
||||
return false;
|
||||
|
||||
v8::MaybeLocal<v8::Value> result = _obj.handle(__isolate)->Get(context, nameValToLocal);
|
||||
if (result.IsEmpty())
|
||||
return false;
|
||||
|
||||
internal::jsToSeValue(__isolate, result.ToLocalChecked(), data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::deleteProperty(const char *name)
|
||||
{
|
||||
|
||||
v8::HandleScope handle_scope(__isolate);
|
||||
|
||||
if (_obj.persistent().IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
v8::MaybeLocal<v8::String> nameValue = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
|
||||
if (nameValue.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Local<v8::String> nameValToLocal = nameValue.ToLocalChecked();
|
||||
v8::Local<v8::Context> context = __isolate->GetCurrentContext();
|
||||
v8::Maybe<bool> maybeExist = _obj.handle(__isolate)->Delete(context, nameValToLocal);
|
||||
if (maybeExist.IsNothing())
|
||||
return false;
|
||||
|
||||
if (!maybeExist.FromJust())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Object::setProperty(const char *name, const Value& data)
|
||||
{
|
||||
v8::MaybeLocal<v8::String> nameValue = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
|
||||
if (nameValue.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Local<v8::Value> value;
|
||||
internal::seToJsValue(__isolate, data, &value);
|
||||
v8::Maybe<bool> ret = _obj.handle(__isolate)->Set(__isolate->GetCurrentContext(), nameValue.ToLocalChecked(), value);
|
||||
if (ret.IsNothing())
|
||||
{
|
||||
SE_LOGD("ERROR: %s, Set return nothing ...\n", __FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::defineProperty(const char *name, v8::AccessorNameGetterCallback getter, v8::AccessorNameSetterCallback setter)
|
||||
{
|
||||
v8::MaybeLocal<v8::String> nameValue = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
|
||||
if (nameValue.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Local<v8::String> nameValChecked = nameValue.ToLocalChecked();
|
||||
v8::Local<v8::Name> jsName = v8::Local<v8::Name>::Cast(nameValChecked);
|
||||
v8::Maybe<bool> ret = _obj.handle(__isolate)->SetAccessor(__isolate->GetCurrentContext(), jsName, getter, setter);
|
||||
return ret.IsJust() && ret.FromJust();
|
||||
}
|
||||
|
||||
bool Object::isFunction() const
|
||||
{
|
||||
return const_cast<Object*>(this)->_obj.handle(__isolate)->IsCallable();
|
||||
}
|
||||
|
||||
bool Object::_isNativeFunction() const
|
||||
{
|
||||
if (isFunction())
|
||||
{
|
||||
std::string info = toString();
|
||||
if (info.find("[native code]") != std::string::npos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::isTypedArray() const
|
||||
{
|
||||
return const_cast<Object*>(this)->_obj.handle(__isolate)->IsTypedArray();
|
||||
}
|
||||
|
||||
Object::TypedArrayType Object::getTypedArrayType() const
|
||||
{
|
||||
v8::Local<v8::Value> value = const_cast<Object*>(this)->_obj.handle(__isolate);
|
||||
TypedArrayType ret = TypedArrayType::NONE;
|
||||
if (value->IsInt8Array())
|
||||
ret = TypedArrayType::INT8;
|
||||
else if (value->IsInt16Array())
|
||||
ret = TypedArrayType::INT16;
|
||||
else if (value->IsInt32Array())
|
||||
ret = TypedArrayType::INT32;
|
||||
else if (value->IsUint8Array())
|
||||
ret = TypedArrayType::UINT8;
|
||||
else if (value->IsUint8ClampedArray())
|
||||
ret = TypedArrayType::UINT8_CLAMPED;
|
||||
else if (value->IsUint16Array())
|
||||
ret = TypedArrayType::UINT16;
|
||||
else if (value->IsUint32Array())
|
||||
ret = TypedArrayType::UINT32;
|
||||
else if (value->IsFloat32Array())
|
||||
ret = TypedArrayType::FLOAT32;
|
||||
else if (value->IsFloat64Array())
|
||||
ret = TypedArrayType::FLOAT64;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Object::getTypedArrayData(uint8_t** ptr, size_t* length) const
|
||||
{
|
||||
assert(isTypedArray());
|
||||
v8::Local<v8::Object> obj = const_cast<Object*>(this)->_obj.handle(__isolate);
|
||||
v8::Local<v8::TypedArray> arr = v8::Local<v8::TypedArray>::Cast(obj);
|
||||
v8::ArrayBuffer::Contents content = arr->Buffer()->GetContents();
|
||||
*ptr = (uint8_t*)content.Data() + arr->ByteOffset();
|
||||
*length = arr->ByteLength();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::isArrayBuffer() const
|
||||
{
|
||||
v8::Local<v8::Object> obj = const_cast<Object*>(this)->_obj.handle(__isolate);
|
||||
return obj->IsArrayBuffer();
|
||||
}
|
||||
|
||||
bool Object::getArrayBufferData(uint8_t** ptr, size_t* length) const
|
||||
{
|
||||
assert(isArrayBuffer());
|
||||
v8::Local<v8::Object> obj = const_cast<Object*>(this)->_obj.handle(__isolate);
|
||||
v8::Local<v8::ArrayBuffer> arrBuf = v8::Local<v8::ArrayBuffer>::Cast(obj);
|
||||
v8::ArrayBuffer::Contents content = arrBuf->GetContents();
|
||||
*ptr = (uint8_t*)content.Data();
|
||||
*length = content.ByteLength();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Object::setPrivateData(void* data)
|
||||
{
|
||||
assert(_privateData == nullptr);
|
||||
assert(NativePtrToObjectMap::find(data) == NativePtrToObjectMap::end());
|
||||
internal::setPrivate(__isolate, _obj, data, &_internalData);
|
||||
NativePtrToObjectMap::emplace(data, this);
|
||||
_privateData = data;
|
||||
}
|
||||
|
||||
void* Object::getPrivateData() const
|
||||
{
|
||||
if (_privateData == nullptr)
|
||||
{
|
||||
const_cast<Object*>(this)->_privateData = internal::getPrivate(__isolate, const_cast<Object*>(this)->_obj.handle(__isolate));
|
||||
}
|
||||
return _privateData;
|
||||
}
|
||||
|
||||
void Object::clearPrivateData(bool clearMapping)
|
||||
{
|
||||
if (_privateData != nullptr)
|
||||
{
|
||||
if (clearMapping)
|
||||
NativePtrToObjectMap::erase(_privateData);
|
||||
internal::clearPrivate(__isolate, _obj);
|
||||
_privateData = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
v8::Local<v8::Object> Object::_getJSObject() const
|
||||
{
|
||||
return const_cast<Object*>(this)->_obj.handle(__isolate);
|
||||
}
|
||||
|
||||
ObjectWrap& Object::_getWrap()
|
||||
{
|
||||
return _obj;
|
||||
}
|
||||
|
||||
bool Object::call(const ValueArray& args, Object* thisObject, Value* rval/* = nullptr*/)
|
||||
{
|
||||
if (_obj.persistent().IsEmpty())
|
||||
{
|
||||
SE_LOGD("Function object is released!\n");
|
||||
return false;
|
||||
}
|
||||
size_t argc = 0;
|
||||
std::vector<v8::Local<v8::Value>> argv;
|
||||
argv.reserve(10);
|
||||
argc = args.size();
|
||||
internal::seToJsArgs(__isolate, args, &argv);
|
||||
|
||||
v8::Local<v8::Object> thiz = v8::Local<v8::Object>::Cast(v8::Undefined(__isolate));
|
||||
if (thisObject != nullptr)
|
||||
{
|
||||
if (thisObject->_obj.persistent().IsEmpty())
|
||||
{
|
||||
SE_LOGD("This object is released!\n");
|
||||
return false;
|
||||
}
|
||||
thiz = thisObject->_obj.handle(__isolate);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < argc; ++i)
|
||||
{
|
||||
if (argv[i].IsEmpty())
|
||||
{
|
||||
SE_LOGD("%s argv[%d] is released!\n", __FUNCTION__, (int)i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
v8::Local<v8::Context> context = se::ScriptEngine::getInstance()->_getContext();
|
||||
v8::MaybeLocal<v8::Value> result = _obj.handle(__isolate)->CallAsFunction(context, thiz, (int)argc, argv.data());
|
||||
|
||||
if (!result.IsEmpty())
|
||||
{
|
||||
if (rval != nullptr)
|
||||
{
|
||||
internal::jsToSeValue(__isolate, result.ToLocalChecked(), rval);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SE_REPORT_ERROR("Invoking function (%p) failed!", this);
|
||||
se::ScriptEngine::getInstance()->clearException();
|
||||
}
|
||||
|
||||
// assert(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::defineFunction(const char *funcName, void (*func)(const v8::FunctionCallbackInfo<v8::Value> &args))
|
||||
{
|
||||
v8::MaybeLocal<v8::String> maybeFuncName = v8::String::NewFromUtf8(__isolate, funcName, v8::NewStringType::kNormal);
|
||||
if (maybeFuncName.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Local<v8::Context> context = __isolate->GetCurrentContext();
|
||||
v8::MaybeLocal<v8::Function> maybeFunc = v8::FunctionTemplate::New(__isolate, func)->GetFunction(context);
|
||||
if (maybeFunc.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Maybe<bool> ret = _obj.handle(__isolate)->Set(context,
|
||||
v8::Local<v8::Name>::Cast(maybeFuncName.ToLocalChecked()),
|
||||
maybeFunc.ToLocalChecked());
|
||||
|
||||
return ret.IsJust() && ret.FromJust();
|
||||
}
|
||||
|
||||
bool Object::isArray() const
|
||||
{
|
||||
return const_cast<Object*>(this)->_obj.handle(__isolate)->IsArray();
|
||||
}
|
||||
|
||||
bool Object::getArrayLength(uint32_t* length) const
|
||||
{
|
||||
assert(isArray());
|
||||
assert(length != nullptr);
|
||||
Object* thiz = const_cast<Object*>(this);
|
||||
|
||||
v8::MaybeLocal<v8::String> lengthStr = v8::String::NewFromUtf8(__isolate, "length", v8::NewStringType::kNormal);
|
||||
if (lengthStr.IsEmpty())
|
||||
{
|
||||
*length = 0;
|
||||
return false;
|
||||
}
|
||||
v8::Local<v8::Context> context = __isolate->GetCurrentContext();
|
||||
|
||||
v8::MaybeLocal<v8::Value> val = thiz->_obj.handle(__isolate)->Get(context, lengthStr.ToLocalChecked());
|
||||
if (val.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::MaybeLocal<v8::Object> obj = val.ToLocalChecked()->ToObject(context);
|
||||
if (obj.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Maybe<uint32_t> mbLen= obj.ToLocalChecked()->Uint32Value(context);
|
||||
if (mbLen.IsNothing())
|
||||
return false;
|
||||
|
||||
*length = mbLen.FromJust();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::getArrayElement(uint32_t index, Value* data) const
|
||||
{
|
||||
assert(isArray());
|
||||
assert(data != nullptr);
|
||||
Object* thiz = const_cast<Object*>(this);
|
||||
v8::MaybeLocal<v8::Value> result = thiz->_obj.handle(__isolate)->Get(__isolate->GetCurrentContext(), index);
|
||||
|
||||
if (result.IsEmpty())
|
||||
return false;
|
||||
|
||||
internal::jsToSeValue(__isolate, result.ToLocalChecked(), data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::setArrayElement(uint32_t index, const Value& data)
|
||||
{
|
||||
assert(isArray());
|
||||
|
||||
v8::Local<v8::Value> jsval;
|
||||
internal::seToJsValue(__isolate, data, &jsval);
|
||||
v8::Maybe<bool> ret = _obj.handle(__isolate)->Set(__isolate->GetCurrentContext(), index, jsval);
|
||||
|
||||
return ret.IsJust() && ret.FromJust();
|
||||
}
|
||||
|
||||
bool Object::getAllKeys(std::vector<std::string>* allKeys) const
|
||||
{
|
||||
assert(allKeys != nullptr);
|
||||
Object* thiz = const_cast<Object*>(this);
|
||||
v8::Local<v8::Context> context = __isolate->GetCurrentContext();
|
||||
v8::MaybeLocal<v8::Array> keys = thiz->_obj.handle(__isolate)->GetOwnPropertyNames(context);
|
||||
if (keys.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Local<v8::Array> keysChecked = keys.ToLocalChecked();
|
||||
Value keyVal;
|
||||
for (uint32_t i = 0, len = keysChecked->Length(); i < len; ++i)
|
||||
{
|
||||
v8::MaybeLocal<v8::Value> key = keysChecked->Get(context, i);
|
||||
if (key.IsEmpty())
|
||||
{
|
||||
allKeys->clear();
|
||||
return false;
|
||||
}
|
||||
internal::jsToSeValue(__isolate, key.ToLocalChecked(), &keyVal);
|
||||
if (keyVal.isString())
|
||||
{
|
||||
allKeys->push_back(keyVal.toString());
|
||||
}
|
||||
else if (keyVal.isNumber())
|
||||
{
|
||||
char buf[50] = {0};
|
||||
snprintf(buf, sizeof(buf), "%d", keyVal.toInt32());
|
||||
allKeys->push_back(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Class* Object::_getClass() const
|
||||
{
|
||||
return _cls;
|
||||
}
|
||||
|
||||
void Object::_setFinalizeCallback(V8FinalizeFunc finalizeCb)
|
||||
{
|
||||
assert(finalizeCb != nullptr);
|
||||
_finalizeCb = finalizeCb;
|
||||
}
|
||||
|
||||
void Object::root()
|
||||
{
|
||||
if (_rootCount == 0)
|
||||
{
|
||||
_obj.ref();
|
||||
}
|
||||
++_rootCount;
|
||||
}
|
||||
|
||||
void Object::unroot()
|
||||
{
|
||||
if (_rootCount > 0)
|
||||
{
|
||||
--_rootCount;
|
||||
if (_rootCount == 0)
|
||||
{
|
||||
_obj.unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Object::isRooted() const
|
||||
{
|
||||
return _rootCount > 0;
|
||||
}
|
||||
|
||||
bool Object::strictEquals(Object *o) const
|
||||
{
|
||||
Object* a = const_cast<Object*>(this);
|
||||
return a->_obj.handle(__isolate) == o->_obj.handle(__isolate);
|
||||
}
|
||||
|
||||
bool Object::attachObject(Object* obj)
|
||||
{
|
||||
assert(obj);
|
||||
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal))
|
||||
return false;
|
||||
Object* jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("registerNativeRef", &func))
|
||||
return false;
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::detachObject(Object* obj)
|
||||
{
|
||||
assert(obj);
|
||||
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal))
|
||||
return false;
|
||||
Object* jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("unregisterNativeRef", &func))
|
||||
return false;
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Object::toString() const
|
||||
{
|
||||
std::string ret;
|
||||
if (isFunction() || isArray() || isTypedArray())
|
||||
{
|
||||
v8::String::Utf8Value utf8(__isolate, const_cast<Object*>(this)->_obj.handle(__isolate));
|
||||
ret = *utf8;
|
||||
}
|
||||
else if (isArrayBuffer())
|
||||
{
|
||||
ret = "[object ArrayBuffer]";
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = "[object Object]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
407
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Object.hpp
Normal file
407
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Object.hpp
Normal file
@@ -0,0 +1,407 @@
|
||||
/****************************************************************************
|
||||
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_V8
|
||||
|
||||
#include "Base.h"
|
||||
#include "../RefCounter.hpp"
|
||||
#include "../Value.hpp"
|
||||
#include "ObjectWrap.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace se {
|
||||
|
||||
class Class;
|
||||
|
||||
namespace internal {
|
||||
struct PrivateData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, v8::AccessorNameGetterCallback getter, v8::AccessorNameSetterCallback 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, v8::FunctionCallback 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, v8::Local<v8::Object> obj);
|
||||
v8::Local<v8::Object> _getJSObject() const;
|
||||
ObjectWrap& _getWrap();
|
||||
Class* _getClass() const;
|
||||
|
||||
void _setFinalizeCallback(V8FinalizeFunc finalizeCb);
|
||||
bool _isNativeFunction() const;
|
||||
//
|
||||
|
||||
private:
|
||||
static void nativeObjectFinalizeHook(void* nativeObj);
|
||||
static void setIsolate(v8::Isolate* isolate);
|
||||
static void cleanup();
|
||||
static void setup();
|
||||
|
||||
Object();
|
||||
virtual ~Object();
|
||||
|
||||
bool init(Class* cls, v8::Local<v8::Object> obj);
|
||||
|
||||
Class* _cls;
|
||||
ObjectWrap _obj;
|
||||
uint32_t _rootCount;
|
||||
|
||||
void* _privateData;
|
||||
V8FinalizeFunc _finalizeCb;
|
||||
internal::PrivateData* _internalData;
|
||||
|
||||
friend class ScriptEngine;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<std::unordered_map<Object*, void*>> __objectMap; // Currently, the value `void*` is always nullptr
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 "ObjectWrap.h"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
|
||||
namespace se {
|
||||
|
||||
ObjectWrap::ObjectWrap() {
|
||||
refs_ = 0;
|
||||
_nativeObj = nullptr;
|
||||
_finalizeCb = nullptr;
|
||||
}
|
||||
|
||||
bool ObjectWrap::init(v8::Local<v8::Object> handle) {
|
||||
assert(persistent().IsEmpty());
|
||||
persistent().Reset(v8::Isolate::GetCurrent(), handle);
|
||||
makeWeak();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ObjectWrap::setFinalizeCallback(V8FinalizeFunc finalizeCb) {
|
||||
_finalizeCb = finalizeCb;
|
||||
}
|
||||
|
||||
ObjectWrap::~ObjectWrap() {
|
||||
if (persistent().IsEmpty())
|
||||
return;
|
||||
//cjh assert(persistent().IsNearDeath());
|
||||
persistent().ClearWeak();
|
||||
persistent().Reset();
|
||||
}
|
||||
|
||||
|
||||
/*static*/
|
||||
void *ObjectWrap::unwrap(v8::Local<v8::Object> handle) {
|
||||
assert(!handle.IsEmpty());
|
||||
assert(handle->InternalFieldCount() > 0);
|
||||
return handle->GetAlignedPointerFromInternalField(0);
|
||||
}
|
||||
|
||||
|
||||
v8::Local<v8::Object> ObjectWrap::handle() {
|
||||
return handle(v8::Isolate::GetCurrent());
|
||||
}
|
||||
|
||||
|
||||
v8::Local<v8::Object> ObjectWrap::handle(v8::Isolate *isolate) {
|
||||
return v8::Local<v8::Object>::New(isolate, persistent());
|
||||
}
|
||||
|
||||
|
||||
v8::Persistent<v8::Object> &ObjectWrap::persistent() {
|
||||
return handle_;
|
||||
}
|
||||
|
||||
void ObjectWrap::wrap(void *nativeObj) {
|
||||
assert(handle()->InternalFieldCount() > 0);
|
||||
_nativeObj = nativeObj;
|
||||
handle()->SetAlignedPointerInInternalField(0, nativeObj);
|
||||
}
|
||||
|
||||
void ObjectWrap::makeWeak() {
|
||||
persistent().SetWeak(this, weakCallback, v8::WeakCallbackType::kFinalizer);
|
||||
// persistent().MarkIndependent();
|
||||
}
|
||||
|
||||
|
||||
void ObjectWrap::ref() {
|
||||
assert(!persistent().IsEmpty());
|
||||
persistent().ClearWeak();
|
||||
refs_++;
|
||||
}
|
||||
|
||||
void ObjectWrap::unref() {
|
||||
assert(!persistent().IsEmpty());
|
||||
assert(!persistent().IsWeak());
|
||||
assert(refs_ > 0);
|
||||
if (--refs_ == 0)
|
||||
makeWeak();
|
||||
}
|
||||
|
||||
/*static*/
|
||||
void ObjectWrap::weakCallback(const v8::WeakCallbackInfo<ObjectWrap> &data) {
|
||||
ObjectWrap *wrap = data.GetParameter();
|
||||
// SE_LOGD("weakCallback: %p, nativeObj = %p, finalize: %p\n", wrap, wrap->_nativeObj, wrap->_finalizeCb);
|
||||
assert(wrap->refs_ == 0);
|
||||
wrap->handle_.Reset();
|
||||
if (wrap->_finalizeCb != nullptr)
|
||||
{
|
||||
wrap->_finalizeCb(wrap->_nativeObj); // wrap will be destroyed in wrap->_finalizeCb, should not use any wrap object after this line.
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef SRC_NODE_OBJECT_WRAP_H_
|
||||
#define SRC_NODE_OBJECT_WRAP_H_
|
||||
|
||||
#include "../config.hpp"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class ObjectWrap {
|
||||
public:
|
||||
ObjectWrap();
|
||||
~ObjectWrap();
|
||||
|
||||
bool init(v8::Local<v8::Object> handle);
|
||||
void setFinalizeCallback(V8FinalizeFunc finalizeCb);
|
||||
|
||||
v8::Local<v8::Object> handle();
|
||||
v8::Local<v8::Object> handle(v8::Isolate *isolate);
|
||||
v8::Persistent<v8::Object> &persistent();
|
||||
|
||||
void wrap(void *nativeObj);
|
||||
static void* unwrap(v8::Local<v8::Object> handle);
|
||||
|
||||
/* Ref() marks the object as being attached to an event loop.
|
||||
* Refed objects will not be garbage collected, even if
|
||||
* all references are lost.
|
||||
*/
|
||||
void ref();
|
||||
|
||||
/* Unref() marks an object as detached from the event loop. This is its
|
||||
* default state. When an object with a "weak" reference changes from
|
||||
* attached to detached state it will be freed. Be careful not to access
|
||||
* the object after making this call as it might be gone!
|
||||
* (A "weak reference" means an object that only has a
|
||||
* persistent handle.)
|
||||
*
|
||||
* DO NOT CALL THIS FROM DESTRUCTOR
|
||||
*/
|
||||
void unref();
|
||||
|
||||
private:
|
||||
static void weakCallback(const v8::WeakCallbackInfo<ObjectWrap> &data);
|
||||
void makeWeak();
|
||||
|
||||
int refs_; // ro
|
||||
v8::Persistent<v8::Object> handle_;
|
||||
void *_nativeObj;
|
||||
V8FinalizeFunc _finalizeCb;
|
||||
};
|
||||
|
||||
} // namespace se
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
|
||||
#endif // SRC_NODE_OBJECT_WRAP_H_
|
||||
@@ -0,0 +1,2 @@
|
||||
V8 inspector from node commit (4e8bc7181c1f2491e187477798d433a4488f43d4)
|
||||
|
||||
@@ -0,0 +1,905 @@
|
||||
/****************************************************************************
|
||||
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"
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
#include "base/ccConfig.h"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
|
||||
#include "Object.hpp"
|
||||
#include "Class.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "../State.hpp"
|
||||
#include "../MappingUtils.hpp"
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <mach/machine.h>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
#if SE_ENABLE_INSPECTOR
|
||||
#include "debugger/inspector_agent.h"
|
||||
#include "debugger/env.h"
|
||||
#include "debugger/node.h"
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#define EXPOSE_GC "__jsb_gc__"
|
||||
|
||||
uint32_t __jsbInvocationCount = 0;
|
||||
uint32_t __jsbStackFrameLimit = 20;
|
||||
|
||||
#define RETRUN_VAL_IF_FAIL(cond, val) \
|
||||
if (!(cond)) return val
|
||||
|
||||
namespace se {
|
||||
|
||||
Class* __jsb_CCPrivateData_class = nullptr;
|
||||
|
||||
namespace {
|
||||
ScriptEngine* __instance = nullptr;
|
||||
|
||||
void __log(const v8::FunctionCallbackInfo<v8::Value>& info)
|
||||
{
|
||||
if (info[0]->IsString())
|
||||
{
|
||||
v8::String::Utf8Value utf8(v8::Isolate::GetCurrent(), info[0]);
|
||||
SE_LOGD("JS: %s\n", *utf8);
|
||||
}
|
||||
}
|
||||
|
||||
void __forceGC(const v8::FunctionCallbackInfo<v8::Value>& info)
|
||||
{
|
||||
ScriptEngine::getInstance()->garbageCollect();
|
||||
}
|
||||
|
||||
std::string stackTraceToString(v8::Local<v8::StackTrace> stack)
|
||||
{
|
||||
std::string stackStr;
|
||||
if (stack.IsEmpty())
|
||||
return stackStr;
|
||||
|
||||
char tmp[100] = {0};
|
||||
for (int i = 0, e = stack->GetFrameCount(); i < e; ++i)
|
||||
{
|
||||
v8::Local<v8::StackFrame> frame = stack->GetFrame(v8::Isolate::GetCurrent(), i);
|
||||
v8::Local<v8::String> script = frame->GetScriptName();
|
||||
std::string scriptName;
|
||||
if (!script.IsEmpty())
|
||||
{
|
||||
scriptName = *v8::String::Utf8Value(v8::Isolate::GetCurrent(), script);
|
||||
}
|
||||
|
||||
v8::Local<v8::String> func = frame->GetFunctionName();
|
||||
std::string funcName;
|
||||
if (!func.IsEmpty())
|
||||
{
|
||||
funcName = *v8::String::Utf8Value(v8::Isolate::GetCurrent(), func);
|
||||
}
|
||||
|
||||
stackStr += "[";
|
||||
snprintf(tmp, sizeof(tmp), "%d", i);
|
||||
stackStr += tmp;
|
||||
stackStr += "]";
|
||||
stackStr += (funcName.empty() ? "anonymous" : funcName.c_str());
|
||||
stackStr += "@";
|
||||
stackStr += (scriptName.empty() ? "(no filename)" : scriptName.c_str());
|
||||
stackStr += ":";
|
||||
snprintf(tmp, sizeof(tmp), "%d", frame->GetLineNumber());
|
||||
stackStr += tmp;
|
||||
|
||||
if (i < (e-1))
|
||||
{
|
||||
stackStr += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return stackStr;
|
||||
}
|
||||
|
||||
se::Value __oldConsoleLog;
|
||||
se::Value __oldConsoleDebug;
|
||||
se::Value __oldConsoleInfo;
|
||||
se::Value __oldConsoleWarn;
|
||||
se::Value __oldConsoleError;
|
||||
se::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;
|
||||
}
|
||||
|
||||
bool JSB_console_log(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "");
|
||||
__oldConsoleLog.toObject()->call(s.args(), s.thisObject());
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_log)
|
||||
|
||||
bool JSB_console_debug(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[DEBUG]: ");
|
||||
__oldConsoleDebug.toObject()->call(s.args(), s.thisObject());
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_debug)
|
||||
|
||||
bool JSB_console_info(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[INFO]: ");
|
||||
__oldConsoleInfo.toObject()->call(s.args(), s.thisObject());
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_info)
|
||||
|
||||
bool JSB_console_warn(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[WARN]: ");
|
||||
__oldConsoleWarn.toObject()->call(s.args(), s.thisObject());
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_warn)
|
||||
|
||||
bool JSB_console_error(State& s)
|
||||
{
|
||||
JSB_console_format_log(s, "[ERROR]: ");
|
||||
__oldConsoleError.toObject()->call(s.args(), s.thisObject());
|
||||
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);
|
||||
__oldConsoleAssert.toObject()->call(s.args(), s.thisObject());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(JSB_console_assert)
|
||||
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
|
||||
/**
|
||||
* JIT is enabled on iOS 14.2+ & chipset A12+
|
||||
* ref https://github.com/flutter/engine/pull/22377
|
||||
*/
|
||||
bool jitSupported() {
|
||||
#if CC_IOS_FORCE_DISABLE_JIT
|
||||
return false;
|
||||
#elif TARGET_CPU_X86 || TARGET_CPU_X86_64
|
||||
return true;
|
||||
#else
|
||||
|
||||
// Check for arm64e.
|
||||
cpu_type_t cpuType = 0;
|
||||
size_t cpuTypeSize = sizeof(cpu_type_t);
|
||||
if (::sysctlbyname("hw.cputype", &cpuType, &cpuTypeSize, nullptr, 0) < 0) {
|
||||
SE_LOGD("Could not execute sysctl() to get CPU type: %s", strerror(errno));
|
||||
}
|
||||
|
||||
cpu_subtype_t cpuSubType = 0;
|
||||
if (::sysctlbyname("hw.cpusubtype", &cpuSubType, &cpuTypeSize, nullptr, 0) < 0) {
|
||||
SE_LOGD("Could not execute sysctl() to get CPU subtype: %s", strerror(errno));
|
||||
}
|
||||
|
||||
// Tracing is necessary unless the device is arm64e (A12 chip or higher).
|
||||
if (cpuType != CPU_TYPE_ARM64 || cpuSubType != CPU_SUBTYPE_ARM64E) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for iOS 14.2 and higher.
|
||||
size_t osVersionSize;
|
||||
::sysctlbyname("kern.osversion", NULL, &osVersionSize, NULL, 0);
|
||||
char osversionBuffer[osVersionSize];
|
||||
|
||||
if (::sysctlbyname("kern.osversion", osversionBuffer, &osVersionSize, NULL, 0) < 0) {
|
||||
SE_LOGD("Could not execute sysctl() to get current OS version: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
int majorVersion = 0;
|
||||
char minorLetter = 'Z';
|
||||
|
||||
for (size_t index = 0; index < osVersionSize; index++) {
|
||||
char version_char = osversionBuffer[index];
|
||||
// Find the minor version build letter.
|
||||
if (isalpha(version_char)) {
|
||||
majorVersion = atoi((const char*)osversionBuffer);
|
||||
minorLetter = toupper(version_char);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 18B92 is iOS 14.2 beta release candidate where tracing became unnecessary.
|
||||
return majorVersion > 18 || (majorVersion == 18 && minorLetter >= 'B');
|
||||
#endif //TARGET_CPU_X86 || TARGET_CPU_X86_64
|
||||
}
|
||||
#endif //CC_TARGET_PLATFORM == CC_PLATFORM_IOS
|
||||
|
||||
} // namespace {
|
||||
|
||||
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::onFatalErrorCallback(const char* location, const char* message)
|
||||
{
|
||||
std::string errorStr = "[FATAL ERROR] location: ";
|
||||
errorStr += location;
|
||||
errorStr += ", message: ";
|
||||
errorStr += message;
|
||||
|
||||
SE_LOGE("%s\n", errorStr.c_str());
|
||||
|
||||
getInstance()->callExceptionCallback(location, message, "(no stack information)");
|
||||
}
|
||||
|
||||
void ScriptEngine::onOOMErrorCallback(const char* location, bool is_heap_oom)
|
||||
{
|
||||
std::string errorStr = "[OOM ERROR] location: ";
|
||||
errorStr += location;
|
||||
std::string message;
|
||||
message = "is heap out of memory: ";
|
||||
if (is_heap_oom)
|
||||
message += "true";
|
||||
else
|
||||
message += "false";
|
||||
|
||||
errorStr += ", " + message;
|
||||
SE_LOGE("%s\n", errorStr.c_str());
|
||||
getInstance()->callExceptionCallback(location, message.c_str(), "(no stack information)");
|
||||
|
||||
}
|
||||
|
||||
void ScriptEngine::onMessageCallback(v8::Local<v8::Message> message, v8::Local<v8::Value> data)
|
||||
{
|
||||
ScriptEngine* thiz = getInstance();
|
||||
v8::Local<v8::String> msg = message->Get();
|
||||
Value msgVal;
|
||||
internal::jsToSeValue(v8::Isolate::GetCurrent(), msg, &msgVal);
|
||||
assert(msgVal.isString());
|
||||
v8::ScriptOrigin origin = message->GetScriptOrigin();
|
||||
Value resouceNameVal;
|
||||
internal::jsToSeValue(v8::Isolate::GetCurrent(), origin.ResourceName(), &resouceNameVal);
|
||||
Value line;
|
||||
internal::jsToSeValue(v8::Isolate::GetCurrent(), origin.ResourceLineOffset(), &line);
|
||||
Value column;
|
||||
internal::jsToSeValue(v8::Isolate::GetCurrent(), origin.ResourceColumnOffset(), &column);
|
||||
|
||||
std::string location = resouceNameVal.toStringForce() + ":" + line.toStringForce() + ":" + column.toStringForce();
|
||||
|
||||
std::string errorStr = msgVal.toString() + ", location: " + location;
|
||||
std::string stackStr = stackTraceToString(message->GetStackTrace());
|
||||
if (!stackStr.empty())
|
||||
{
|
||||
if (line.toInt32() == 0)
|
||||
{
|
||||
location = "(see stack)";
|
||||
}
|
||||
errorStr += "\nSTACK:\n" + stackStr;
|
||||
}
|
||||
SE_LOGE("ERROR: %s\n", errorStr.c_str());
|
||||
|
||||
thiz->callExceptionCallback(location.c_str(), msgVal.toString().c_str(), stackStr.c_str());
|
||||
|
||||
if (!thiz->_isErrorHandleWorking)
|
||||
{
|
||||
thiz->_isErrorHandleWorking = true;
|
||||
|
||||
Value errorHandler;
|
||||
if (thiz->_globalObj && thiz->_globalObj->getProperty("__errorHandler", &errorHandler) && errorHandler.isObject() && errorHandler.toObject()->isFunction())
|
||||
{
|
||||
ValueArray args;
|
||||
args.push_back(resouceNameVal);
|
||||
args.push_back(line);
|
||||
args.push_back(msgVal);
|
||||
args.push_back(Value(stackStr));
|
||||
errorHandler.toObject()->call(args, thiz->_globalObj);
|
||||
}
|
||||
|
||||
thiz->_isErrorHandleWorking = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SE_LOGE("ERROR: __errorHandler has exception\n");
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::onPromiseRejectCallback(v8::PromiseRejectMessage msg)
|
||||
{
|
||||
v8::Isolate *isolate = getInstance()->_isolate;
|
||||
v8::HandleScope scope(isolate);
|
||||
std::stringstream ss;
|
||||
auto event = msg.GetEvent();
|
||||
auto value = msg.GetValue();
|
||||
const char *eventName = "[invalidatePromiseEvent]";
|
||||
|
||||
if(event == v8::kPromiseRejectWithNoHandler) {
|
||||
eventName = "unhandledRejectedPromise";
|
||||
}else if(event == v8::kPromiseHandlerAddedAfterReject) {
|
||||
eventName = "handlerAddedAfterPromiseRejected";
|
||||
}else if(event == v8::kPromiseRejectAfterResolved) {
|
||||
eventName = "rejectAfterPromiseResolved";
|
||||
}else if( event == v8::kPromiseResolveAfterResolved) {
|
||||
eventName = "resolveAfterPromiseResolved";
|
||||
}
|
||||
|
||||
if(!value.IsEmpty()) {
|
||||
// prepend error object to stack message
|
||||
v8::Local<v8::String> str = value->ToString(isolate->GetCurrentContext()).ToLocalChecked();
|
||||
v8::String::Utf8Value valueUtf8(isolate, str);
|
||||
ss << *valueUtf8 << std::endl;
|
||||
}
|
||||
|
||||
auto stackStr = getInstance()->getCurrentStackTrace();
|
||||
ss << "stacktrace: " << std::endl;
|
||||
ss << stackStr << std::endl;
|
||||
getInstance()->callExceptionCallback("", eventName, ss.str().c_str());
|
||||
|
||||
}
|
||||
|
||||
void ScriptEngine::privateDataFinalize(void* nativeObj)
|
||||
{
|
||||
internal::PrivateData* p = (internal::PrivateData*)nativeObj;
|
||||
|
||||
Object::nativeObjectFinalizeHook(p->data);
|
||||
|
||||
assert(p->seObj->getRefCount() == 1);
|
||||
|
||||
p->seObj->decRef();
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
ScriptEngine *ScriptEngine::getInstance()
|
||||
{
|
||||
if (__instance == nullptr)
|
||||
{
|
||||
__instance = new ScriptEngine();
|
||||
}
|
||||
|
||||
return __instance;
|
||||
}
|
||||
|
||||
void ScriptEngine::destroyInstance()
|
||||
{
|
||||
delete __instance;
|
||||
__instance = nullptr;
|
||||
}
|
||||
|
||||
ScriptEngine::ScriptEngine()
|
||||
: _platform(nullptr)
|
||||
, _isolate(nullptr)
|
||||
, _handleScope(nullptr)
|
||||
, _globalObj(nullptr)
|
||||
#if SE_ENABLE_INSPECTOR
|
||||
, _env(nullptr)
|
||||
, _isolateData(nullptr)
|
||||
#endif
|
||||
, _debuggerServerPort(0)
|
||||
, _vmId(0)
|
||||
, _isValid(false)
|
||||
, _isGarbageCollecting(false)
|
||||
, _isInCleanup(false)
|
||||
, _isErrorHandleWorking(false)
|
||||
{
|
||||
_platform = v8::platform::NewDefaultPlatform().release();
|
||||
v8::V8::InitializePlatform(_platform);
|
||||
|
||||
std::string flags;
|
||||
//NOTICE: spaces are required between flags
|
||||
flags.append(" --expose-gc-as=" EXPOSE_GC);
|
||||
// flags.append(" --trace-gc"); // v8 trace gc
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
||||
if(!jitSupported()) {
|
||||
flags.append(" --jitless");
|
||||
}
|
||||
#endif
|
||||
if(!flags.empty())
|
||||
{
|
||||
v8::V8::SetFlagsFromString(flags.c_str(), (int)flags.length());
|
||||
}
|
||||
|
||||
bool ok = v8::V8::Initialize();
|
||||
assert(ok);
|
||||
}
|
||||
|
||||
ScriptEngine::~ScriptEngine()
|
||||
{
|
||||
cleanup();
|
||||
v8::V8::Dispose();
|
||||
v8::V8::ShutdownPlatform();
|
||||
delete _platform;
|
||||
}
|
||||
|
||||
bool ScriptEngine::init()
|
||||
{
|
||||
cleanup();
|
||||
SE_LOGD("Initializing V8, version: %s\n", v8::V8::GetVersion());
|
||||
++_vmId;
|
||||
|
||||
_engineThreadId = std::this_thread::get_id();
|
||||
|
||||
for (const auto& hook : _beforeInitHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_beforeInitHookArray.clear();
|
||||
v8::Isolate::CreateParams create_params;
|
||||
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
|
||||
_isolate = v8::Isolate::New(create_params);
|
||||
v8::HandleScope hs(_isolate);
|
||||
_isolate->Enter();
|
||||
|
||||
_isolate->SetCaptureStackTraceForUncaughtExceptions(true, __jsbStackFrameLimit, v8::StackTrace::kOverview);
|
||||
|
||||
_isolate->SetFatalErrorHandler(onFatalErrorCallback);
|
||||
_isolate->SetOOMErrorHandler(onOOMErrorCallback);
|
||||
_isolate->AddMessageListener(onMessageCallback);
|
||||
_isolate->SetPromiseRejectCallback(onPromiseRejectCallback);
|
||||
|
||||
_context.Reset(_isolate, v8::Context::New(_isolate));
|
||||
_context.Get(_isolate)->Enter();
|
||||
|
||||
NativePtrToObjectMap::init();
|
||||
NonRefNativePtrCreatedByCtorMap::init();
|
||||
|
||||
Object::setup();
|
||||
Class::setIsolate(_isolate);
|
||||
Object::setIsolate(_isolate);
|
||||
|
||||
_globalObj = Object::_createJSObject(nullptr, _context.Get(_isolate)->Global());
|
||||
_globalObj->root();
|
||||
_globalObj->setProperty("window", Value(_globalObj));
|
||||
|
||||
se::Value consoleVal;
|
||||
if (_globalObj->getProperty("console", &consoleVal) && consoleVal.isObject())
|
||||
{
|
||||
consoleVal.toObject()->getProperty("log", &__oldConsoleLog);
|
||||
consoleVal.toObject()->defineFunction("log", _SE(JSB_console_log));
|
||||
|
||||
consoleVal.toObject()->getProperty("debug", &__oldConsoleDebug);
|
||||
consoleVal.toObject()->defineFunction("debug", _SE(JSB_console_debug));
|
||||
|
||||
consoleVal.toObject()->getProperty("info", &__oldConsoleInfo);
|
||||
consoleVal.toObject()->defineFunction("info", _SE(JSB_console_info));
|
||||
|
||||
consoleVal.toObject()->getProperty("warn", &__oldConsoleWarn);
|
||||
consoleVal.toObject()->defineFunction("warn", _SE(JSB_console_warn));
|
||||
|
||||
consoleVal.toObject()->getProperty("error", &__oldConsoleError);
|
||||
consoleVal.toObject()->defineFunction("error", _SE(JSB_console_error));
|
||||
|
||||
consoleVal.toObject()->getProperty("assert", &__oldConsoleAssert);
|
||||
consoleVal.toObject()->defineFunction("assert", _SE(JSB_console_assert));
|
||||
}
|
||||
|
||||
_globalObj->setProperty("scriptEngineType", se::Value("V8"));
|
||||
|
||||
_globalObj->defineFunction("log", __log);
|
||||
_globalObj->defineFunction("forceGC", __forceGC);
|
||||
|
||||
|
||||
_globalObj->getProperty(EXPOSE_GC, &_gcFuncValue);
|
||||
if(_gcFuncValue.isObject() && _gcFuncValue.toObject()->isFunction()) {
|
||||
_gcFunc = _gcFuncValue.toObject();
|
||||
} else {
|
||||
_gcFunc = nullptr;
|
||||
}
|
||||
|
||||
|
||||
__jsb_CCPrivateData_class = Class::create("__PrivateData", _globalObj, nullptr, nullptr);
|
||||
__jsb_CCPrivateData_class->defineFinalizeFunction(privateDataFinalize);
|
||||
__jsb_CCPrivateData_class->setCreateProto(false);
|
||||
__jsb_CCPrivateData_class->install();
|
||||
|
||||
_isValid = true;
|
||||
|
||||
for (const auto& hook : _afterInitHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_afterInitHookArray.clear();
|
||||
|
||||
return _isValid;
|
||||
}
|
||||
|
||||
void ScriptEngine::cleanup()
|
||||
{
|
||||
if (!_isValid)
|
||||
return;
|
||||
|
||||
SE_LOGD("ScriptEngine::cleanup begin ...\n");
|
||||
_isInCleanup = true;
|
||||
|
||||
{
|
||||
AutoHandleScope hs;
|
||||
for (const auto& hook : _beforeCleanupHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_beforeCleanupHookArray.clear();
|
||||
|
||||
SAFE_DEC_REF(_globalObj);
|
||||
Object::cleanup();
|
||||
Class::cleanup();
|
||||
garbageCollect();
|
||||
|
||||
__oldConsoleLog.setUndefined();
|
||||
__oldConsoleDebug.setUndefined();
|
||||
__oldConsoleInfo.setUndefined();
|
||||
__oldConsoleWarn.setUndefined();
|
||||
__oldConsoleError.setUndefined();
|
||||
__oldConsoleAssert.setUndefined();
|
||||
|
||||
#if SE_ENABLE_INSPECTOR
|
||||
if (_isolateData != nullptr)
|
||||
{
|
||||
node::FreeIsolateData(_isolateData);
|
||||
_isolateData = nullptr;
|
||||
}
|
||||
|
||||
if (_env != nullptr)
|
||||
{
|
||||
_env->inspector_agent()->Stop();
|
||||
_env->CleanupHandles();
|
||||
node::FreeEnvironment(_env);
|
||||
_env = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
_context.Get(_isolate)->Exit();
|
||||
_context.Reset();
|
||||
_isolate->Exit();
|
||||
}
|
||||
_isolate->Dispose();
|
||||
|
||||
_isolate = nullptr;
|
||||
_globalObj = nullptr;
|
||||
_isValid = false;
|
||||
|
||||
_registerCallbackArray.clear();
|
||||
|
||||
for (const auto& hook : _afterCleanupHookArray)
|
||||
{
|
||||
hook();
|
||||
}
|
||||
_afterCleanupHookArray.clear();
|
||||
|
||||
_isInCleanup = false;
|
||||
NativePtrToObjectMap::destroy();
|
||||
NonRefNativePtrCreatedByCtorMap::destroy();
|
||||
_gcFunc = nullptr;
|
||||
SE_LOGD("ScriptEngine::cleanup end ...\n");
|
||||
}
|
||||
|
||||
Object* ScriptEngine::getGlobalObject() const
|
||||
{
|
||||
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;
|
||||
|
||||
se::AutoHandleScope hs;
|
||||
|
||||
// debugger
|
||||
if (isDebuggerEnabled())
|
||||
{
|
||||
#if SE_ENABLE_INSPECTOR
|
||||
// V8 inspector stuff, most code are taken from NodeJS.
|
||||
_isolateData = node::CreateIsolateData(_isolate, uv_default_loop());
|
||||
_env = node::CreateEnvironment(_isolateData, _context.Get(_isolate), 0, nullptr, 0, nullptr);
|
||||
|
||||
node::DebugOptions options;
|
||||
options.set_wait_for_connect(_isWaitForConnect);// the program will be hung up until debug attach if _isWaitForConnect = true
|
||||
options.set_inspector_enabled(true);
|
||||
options.set_port((int)_debuggerServerPort);
|
||||
options.set_host_name(_debuggerServerAddr.c_str());
|
||||
bool ok = _env->inspector_agent()->Start(_platform, "", options);
|
||||
assert(ok);
|
||||
#endif
|
||||
}
|
||||
//
|
||||
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()
|
||||
{
|
||||
int objSize = __objectMap ? (int)__objectMap->size() : -1;
|
||||
SE_LOGD("GC begin ..., (js->native map) size: %d, all objects: %d\n", (int)NativePtrToObjectMap::size(), objSize);
|
||||
|
||||
if(_gcFunc == nullptr)
|
||||
{
|
||||
const double kLongIdlePauseInSeconds = 1.0;
|
||||
_isolate->ContextDisposedNotification();
|
||||
_isolate->IdleNotificationDeadline(_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
|
||||
// By sending a low memory notifications, we will try hard to collect all
|
||||
// garbage and will therefore also invoke all weak callbacks of actually
|
||||
// unreachable persistent handles.
|
||||
_isolate->LowMemoryNotification();
|
||||
}
|
||||
else
|
||||
{
|
||||
_gcFunc->call({}, nullptr);
|
||||
}
|
||||
objSize = __objectMap ? (int)__objectMap->size() : -1;
|
||||
|
||||
SE_LOGD("GC end ..., (js->native map) size: %d, all objects: %d\n", (int)NativePtrToObjectMap::size(), objSize);
|
||||
}
|
||||
|
||||
bool ScriptEngine::isGarbageCollecting()
|
||||
{
|
||||
return _isGarbageCollecting;
|
||||
}
|
||||
|
||||
void ScriptEngine::_setGarbageCollecting(bool isGarbageCollecting)
|
||||
{
|
||||
_isGarbageCollecting = isGarbageCollecting;
|
||||
}
|
||||
|
||||
bool ScriptEngine::isValid() const
|
||||
{
|
||||
return _isValid;
|
||||
}
|
||||
|
||||
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 Chrome 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());
|
||||
}
|
||||
|
||||
// It is needed, or will crash if invoked from non C++ context, such as invoked from objective-c context(for example, handler of UIKit).
|
||||
v8::HandleScope handle_scope(_isolate);
|
||||
|
||||
std::string scriptStr(script, length);
|
||||
v8::MaybeLocal<v8::String> source = v8::String::NewFromUtf8(_isolate, scriptStr.c_str(), v8::NewStringType::kNormal);
|
||||
if (source.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::MaybeLocal<v8::String> originStr = v8::String::NewFromUtf8(_isolate, sourceUrl.c_str(), v8::NewStringType::kNormal);
|
||||
if (originStr.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::ScriptOrigin origin(originStr.ToLocalChecked());
|
||||
v8::MaybeLocal<v8::Script> maybeScript = v8::Script::Compile(_context.Get(_isolate), source.ToLocalChecked(), &origin);
|
||||
|
||||
bool success = false;
|
||||
|
||||
if (!maybeScript.IsEmpty())
|
||||
{
|
||||
v8::TryCatch block(_isolate);
|
||||
|
||||
v8::Local<v8::Script> v8Script = maybeScript.ToLocalChecked();
|
||||
v8::MaybeLocal<v8::Value> maybeResult = v8Script->Run(_context.Get(_isolate));
|
||||
|
||||
if (!maybeResult.IsEmpty())
|
||||
{
|
||||
v8::Local<v8::Value> result = maybeResult.ToLocalChecked();
|
||||
|
||||
if (!result->IsUndefined() && ret != nullptr)
|
||||
{
|
||||
internal::jsToSeValue(_isolate, result, ret);
|
||||
}
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (block.HasCaught()) {
|
||||
v8::Local<v8::Message> message = block.Message();
|
||||
SE_LOGE("ScriptEngine::evalString catch exception:\n");
|
||||
onMessageCallback(message, v8::Undefined(_isolate));
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
SE_LOGE("ScriptEngine::evalString script %s, failed!\n", fileName);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
std::string ScriptEngine::getCurrentStackTrace()
|
||||
{
|
||||
if (!_isValid)
|
||||
return std::string();
|
||||
|
||||
v8::HandleScope hs(_isolate);
|
||||
v8::Local<v8::StackTrace> stack = v8::StackTrace::CurrentStackTrace(_isolate, __jsbStackFrameLimit, v8::StackTrace::kOverview);
|
||||
return stackTraceToString(stack);
|
||||
}
|
||||
|
||||
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::setExceptionCallback(const ExceptionCallback& cb)
|
||||
{
|
||||
_nativeExceptionCallback = cb;
|
||||
}
|
||||
|
||||
void ScriptEngine::setJSExceptionCallback(const ExceptionCallback& cb)
|
||||
{
|
||||
_jsExceptionCallback = cb;
|
||||
}
|
||||
|
||||
v8::Local<v8::Context> ScriptEngine::_getContext() const
|
||||
{
|
||||
return _context.Get(_isolate);
|
||||
}
|
||||
|
||||
void ScriptEngine::enableDebugger(const std::string& serverAddr, uint32_t port, bool isWait)
|
||||
{
|
||||
_debuggerServerAddr = serverAddr;
|
||||
_debuggerServerPort = port;
|
||||
_isWaitForConnect = isWait;
|
||||
}
|
||||
|
||||
bool ScriptEngine::isDebuggerEnabled() const
|
||||
{
|
||||
return !_debuggerServerAddr.empty() && _debuggerServerPort > 0;
|
||||
}
|
||||
|
||||
void ScriptEngine::mainLoopUpdate()
|
||||
{
|
||||
// empty implementation
|
||||
}
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
@@ -0,0 +1,355 @@
|
||||
/****************************************************************************
|
||||
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_V8
|
||||
|
||||
#include "Base.h"
|
||||
#include "../Value.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#if SE_ENABLE_INSPECTOR
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
class Agent;
|
||||
}
|
||||
|
||||
class Environment;
|
||||
class IsolateData;
|
||||
}
|
||||
#endif
|
||||
|
||||
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()
|
||||
: _handleScope(v8::Isolate::GetCurrent())
|
||||
{
|
||||
}
|
||||
~AutoHandleScope()
|
||||
{
|
||||
}
|
||||
private:
|
||||
v8::HandleScope _handleScope;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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() const;
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Grab a snapshot of the current JavaScript execution stack.
|
||||
* @return current stack trace string
|
||||
*/
|
||||
std::string getCurrentStackTrace();
|
||||
|
||||
/**
|
||||
* 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() const;
|
||||
|
||||
/**
|
||||
* @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] 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 _retainScriptObject(void* owner, void* target);
|
||||
void _releaseScriptObject(void* owner, void* target);
|
||||
v8::Local<v8::Context> _getContext() const;
|
||||
void _setGarbageCollecting(bool isGarbageCollecting);
|
||||
//
|
||||
private:
|
||||
ScriptEngine();
|
||||
~ScriptEngine();
|
||||
|
||||
static void privateDataFinalize(void* nativeObj);
|
||||
|
||||
static void onFatalErrorCallback(const char* location, const char* message);
|
||||
static void onOOMErrorCallback(const char* location, bool is_heap_oom);
|
||||
static void onMessageCallback(v8::Local<v8::Message> message, v8::Local<v8::Value> data);
|
||||
static void onPromiseRejectCallback(v8::PromiseRejectMessage msg);
|
||||
|
||||
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;
|
||||
|
||||
v8::Persistent<v8::Context> _context;
|
||||
|
||||
v8::Platform* _platform;
|
||||
v8::Isolate* _isolate;
|
||||
v8::HandleScope* _handleScope;
|
||||
Object* _globalObj;
|
||||
Value _gcFuncValue;
|
||||
Object *_gcFunc = nullptr;
|
||||
|
||||
FileOperationDelegate _fileOperationDelegate;
|
||||
ExceptionCallback _nativeExceptionCallback = nullptr;
|
||||
ExceptionCallback _jsExceptionCallback = nullptr;
|
||||
|
||||
#if SE_ENABLE_INSPECTOR
|
||||
node::Environment* _env;
|
||||
node::IsolateData* _isolateData;
|
||||
#endif
|
||||
|
||||
std::thread::id _engineThreadId;
|
||||
|
||||
std::string _debuggerServerAddr;
|
||||
uint32_t _debuggerServerPort;
|
||||
bool _isWaitForConnect;
|
||||
|
||||
uint32_t _vmId;
|
||||
|
||||
bool _isValid;
|
||||
bool _isGarbageCollecting;
|
||||
bool _isInCleanup;
|
||||
bool _isErrorHandleWorking;
|
||||
};
|
||||
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
31
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/SeApi.h
Normal file
31
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/SeApi.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/****************************************************************************
|
||||
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 "Utils.hpp"
|
||||
#include "HelperMacros.h"
|
||||
306
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Utils.cpp
Normal file
306
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Utils.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
/****************************************************************************
|
||||
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_V8
|
||||
|
||||
#include "Object.hpp"
|
||||
#include "Class.hpp"
|
||||
#include "ScriptEngine.hpp"
|
||||
|
||||
namespace se {
|
||||
|
||||
namespace internal {
|
||||
|
||||
void jsToSeArgs(const v8::FunctionCallbackInfo<v8::Value>& v8args, ValueArray* outArr)
|
||||
{
|
||||
assert(outArr != nullptr);
|
||||
v8::Isolate* isolate = v8args.GetIsolate();
|
||||
for (int i = 0; i < v8args.Length(); i++)
|
||||
{
|
||||
Value v;
|
||||
jsToSeValue(isolate, v8args[i], &v);
|
||||
outArr->push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsArgs(v8::Isolate* isolate, const ValueArray& args, std::vector<v8::Local<v8::Value>>* outArr)
|
||||
{
|
||||
assert(outArr != nullptr);
|
||||
for (const auto& data : args)
|
||||
{
|
||||
v8::Local<v8::Value> jsval;
|
||||
seToJsValue(isolate, data, &jsval);
|
||||
outArr->push_back(jsval);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsValue(v8::Isolate* isolate, const Value& v, v8::Local<v8::Value>* outJsVal)
|
||||
{
|
||||
assert(outJsVal != nullptr);
|
||||
switch (v.getType())
|
||||
{
|
||||
case Value::Type::Number:
|
||||
*outJsVal = v8::Number::New(isolate, v.toNumber());
|
||||
break;
|
||||
case Value::Type::String:
|
||||
{
|
||||
v8::MaybeLocal<v8::String> str = v8::String::NewFromUtf8(isolate, v.toString().data(), v8::NewStringType::kNormal, (int) v.toString().length());
|
||||
if (!str.IsEmpty())
|
||||
*outJsVal = str.ToLocalChecked();
|
||||
else
|
||||
outJsVal->Clear();
|
||||
}
|
||||
break;
|
||||
case Value::Type::Boolean:
|
||||
*outJsVal = v8::Boolean::New(isolate, v.toBoolean());
|
||||
break;
|
||||
case Value::Type::Object:
|
||||
*outJsVal = v.toObject()->_getJSObject();
|
||||
break;
|
||||
case Value::Type::Null:
|
||||
*outJsVal = v8::Null(isolate);
|
||||
break;
|
||||
case Value::Type::Undefined:
|
||||
*outJsVal = v8::Undefined(isolate);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void jsToSeValue(v8::Isolate* isolate, v8::Local<v8::Value> jsval, Value* v)
|
||||
{
|
||||
assert(v != nullptr);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
if (jsval->IsUndefined()) {
|
||||
v->setUndefined();
|
||||
} else if (jsval->IsNull()) {
|
||||
v->setNull();
|
||||
} else if (jsval->IsNumber()) {
|
||||
v8::MaybeLocal<v8::Number> jsNumber = jsval->ToNumber(isolate->GetCurrentContext());
|
||||
if (!jsNumber.IsEmpty())
|
||||
v->setNumber(jsNumber.ToLocalChecked()->Value());
|
||||
else
|
||||
v->setUndefined();
|
||||
} else if (jsval->IsString()) {
|
||||
v8::String::Utf8Value utf8(isolate, jsval);
|
||||
v->setString(std::string(*utf8, utf8.length()));
|
||||
} else if (jsval->IsBoolean()) {
|
||||
v8::MaybeLocal<v8::Boolean> jsBoolean = jsval->ToBoolean(isolate);
|
||||
if (!jsBoolean.IsEmpty())
|
||||
v->setBoolean(jsBoolean.ToLocalChecked()->Value());
|
||||
else
|
||||
v->setUndefined();
|
||||
} else if (jsval->IsObject()) {
|
||||
v8::MaybeLocal<v8::Object> jsObj = jsval->ToObject(isolate->GetCurrentContext());
|
||||
if (!jsObj.IsEmpty())
|
||||
{
|
||||
void* nativePtr = internal::getPrivate(isolate, jsObj.ToLocalChecked());
|
||||
Object* obj = nullptr;
|
||||
if (nativePtr != nullptr)
|
||||
{
|
||||
obj = Object::getObjectWithPtr(nativePtr);
|
||||
}
|
||||
|
||||
if (obj == nullptr)
|
||||
{
|
||||
obj = Object::_createJSObject(nullptr, jsObj.ToLocalChecked());
|
||||
}
|
||||
v->setObject(obj, true);
|
||||
obj->decRef();
|
||||
}
|
||||
else
|
||||
{
|
||||
v->setUndefined();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void _setReturnValue(const Value& data, const T& argv)
|
||||
{
|
||||
if (data.getType() == Value::Type::Undefined) {
|
||||
argv.GetReturnValue().Set(v8::Undefined(argv.GetIsolate()));
|
||||
} else if (data.getType() == Value::Type::Null) {
|
||||
argv.GetReturnValue().Set(v8::Null(argv.GetIsolate()));
|
||||
} else if (data.getType() == Value::Type::Number) {
|
||||
argv.GetReturnValue().Set(v8::Number::New(argv.GetIsolate(), data.toNumber()));
|
||||
} else if (data.getType() == Value::Type::String) {
|
||||
v8::MaybeLocal<v8::String> value = v8::String::NewFromUtf8(argv.GetIsolate(), data.toString().c_str(), v8::NewStringType::kNormal);
|
||||
assert(!value.IsEmpty());
|
||||
argv.GetReturnValue().Set(value.ToLocalChecked());
|
||||
} else if (data.getType() == Value::Type::Boolean) {
|
||||
argv.GetReturnValue().Set(v8::Boolean::New(argv.GetIsolate(), data.toBoolean()));
|
||||
} else if (data.getType() == Value::Type::Object) {
|
||||
argv.GetReturnValue().Set(data.toObject()->_getJSObject());
|
||||
}
|
||||
}
|
||||
|
||||
void setReturnValue(const Value& data, const v8::FunctionCallbackInfo<v8::Value>& argv)
|
||||
{
|
||||
_setReturnValue(data, argv);
|
||||
}
|
||||
|
||||
void setReturnValue(const Value& data, const v8::PropertyCallbackInfo<v8::Value>& argv)
|
||||
{
|
||||
_setReturnValue(data, argv);
|
||||
}
|
||||
|
||||
const char* KEY_PRIVATE_DATA = "__cc_private_data";
|
||||
|
||||
bool hasPrivate(v8::Isolate* isolate, v8::Local<v8::Value> value)
|
||||
{
|
||||
v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(value);
|
||||
int c = obj->InternalFieldCount();
|
||||
if (c > 0)
|
||||
return true;
|
||||
|
||||
// Pure JS subclass object doesn't have a internal field
|
||||
v8::MaybeLocal<v8::String> key = v8::String::NewFromUtf8(isolate, KEY_PRIVATE_DATA, v8::NewStringType::kNormal);
|
||||
if (key.IsEmpty())
|
||||
return false;
|
||||
|
||||
v8::Maybe<bool> ret = obj->Has(isolate->GetCurrentContext(), key.ToLocalChecked());
|
||||
return ret.IsJust() && ret.FromJust();
|
||||
}
|
||||
|
||||
void setPrivate(v8::Isolate* isolate, ObjectWrap& wrap, void* data, PrivateData** outInternalData)
|
||||
{
|
||||
v8::Local<v8::Object> obj = wrap.handle(isolate);
|
||||
int c = obj->InternalFieldCount();
|
||||
if (c > 0)
|
||||
{
|
||||
wrap.wrap(data);
|
||||
// SE_LOGD("setPrivate1: %p\n", data);
|
||||
if (outInternalData != nullptr)
|
||||
*outInternalData = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Object* privateObj = Object::createObjectWithClass(__jsb_CCPrivateData_class);
|
||||
PrivateData* privateData = (PrivateData*)malloc(sizeof(PrivateData));
|
||||
privateData->data = data;
|
||||
privateData->seObj = privateObj;
|
||||
|
||||
privateObj->_getWrap().setFinalizeCallback(__jsb_CCPrivateData_class->_getFinalizeFunction());
|
||||
privateObj->_getWrap().wrap(privateData);
|
||||
|
||||
v8::MaybeLocal<v8::String> key = v8::String::NewFromUtf8(isolate, KEY_PRIVATE_DATA, v8::NewStringType::kNormal);
|
||||
assert(!key.IsEmpty());
|
||||
v8::Maybe<bool> ret = obj->Set(isolate->GetCurrentContext(), key.ToLocalChecked(), privateObj->_getJSObject());
|
||||
assert(!ret.IsNothing());
|
||||
// SE_LOGD("setPrivate: native data: %p\n", privateData);
|
||||
// privateObj->decRef(); // NOTE: it's released in ScriptEngine::privateDataFinalize
|
||||
|
||||
if (outInternalData != nullptr)
|
||||
*outInternalData = privateData;
|
||||
}
|
||||
}
|
||||
|
||||
void* getPrivate(v8::Isolate* isolate, v8::Local<v8::Value> value)
|
||||
{
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
v8::MaybeLocal<v8::Object> obj = value->ToObject(context);
|
||||
if (obj.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
v8::Local<v8::Object> objChecked = obj.ToLocalChecked();
|
||||
int c = objChecked->InternalFieldCount();
|
||||
if (c > 0)
|
||||
{
|
||||
void* nativeObj = ObjectWrap::unwrap(objChecked);
|
||||
// SE_LOGD("getPrivate1: %p\n", nativeObj);
|
||||
return nativeObj;
|
||||
}
|
||||
|
||||
// Pure JS subclass object doesn't have a internal field
|
||||
v8::MaybeLocal<v8::String> key = v8::String::NewFromUtf8(isolate, KEY_PRIVATE_DATA, v8::NewStringType::kNormal);
|
||||
if (key.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
v8::Local<v8::String> keyChecked = key.ToLocalChecked();
|
||||
v8::Maybe<bool> mbHas = objChecked->Has(context, keyChecked);
|
||||
if (mbHas.IsNothing() || !mbHas.FromJust())
|
||||
return nullptr;
|
||||
|
||||
v8::MaybeLocal<v8::Value> mbVal = objChecked->Get(context, keyChecked);
|
||||
if (mbVal.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
v8::MaybeLocal<v8::Object> privateObj = mbVal.ToLocalChecked()->ToObject(context);
|
||||
if (privateObj.IsEmpty())
|
||||
return nullptr;
|
||||
PrivateData* privateData = (PrivateData*)ObjectWrap::unwrap(privateObj.ToLocalChecked());
|
||||
// SE_LOGD("getPrivate: native data: %p\n", privateData);
|
||||
return privateData->data;
|
||||
}
|
||||
|
||||
void clearPrivate(v8::Isolate* isolate, ObjectWrap& wrap)
|
||||
{
|
||||
v8::Local<v8::Object> obj = wrap.handle(isolate);
|
||||
int c = obj->InternalFieldCount();
|
||||
if (c > 0)
|
||||
{
|
||||
wrap.wrap(nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
// Pure JS subclass object doesn't have a internal field
|
||||
v8::MaybeLocal<v8::String> key = v8::String::NewFromUtf8(isolate, KEY_PRIVATE_DATA, v8::NewStringType::kNormal);
|
||||
if (key.IsEmpty())
|
||||
return;
|
||||
|
||||
v8::Local<v8::String> keyChecked = key.ToLocalChecked();
|
||||
v8::Maybe<bool> mbHas = obj->Has(context, keyChecked);
|
||||
if (mbHas.IsNothing() || !mbHas.FromJust())
|
||||
return;
|
||||
|
||||
v8::MaybeLocal<v8::Value> mbVal = obj->Get(context, keyChecked);
|
||||
if (mbVal.IsEmpty())
|
||||
return;
|
||||
|
||||
v8::MaybeLocal<v8::Object> privateObj = mbVal.ToLocalChecked()->ToObject(context);
|
||||
if (privateObj.IsEmpty())
|
||||
return;
|
||||
|
||||
PrivateData* privateData = (PrivateData*)ObjectWrap::unwrap(privateObj.ToLocalChecked());
|
||||
free(privateData);
|
||||
v8::Maybe<bool> ok = obj->Delete(context, keyChecked);
|
||||
if (ok.IsNothing())
|
||||
return;
|
||||
|
||||
assert(ok.FromJust());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal {
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
61
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Utils.hpp
Normal file
61
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/Utils.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/****************************************************************************
|
||||
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_V8
|
||||
|
||||
#include "Base.h"
|
||||
#include "../Value.hpp"
|
||||
#include "ObjectWrap.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct PrivateData
|
||||
{
|
||||
void* data;
|
||||
Object* seObj;
|
||||
};
|
||||
|
||||
void jsToSeArgs(const v8::FunctionCallbackInfo<v8::Value>& _v8args, ValueArray* outArr);
|
||||
void jsToSeValue(v8::Isolate* isolate, v8::Local<v8::Value> jsval, Value* v);
|
||||
void seToJsArgs(v8::Isolate* isolate, const ValueArray& args, std::vector<v8::Local<v8::Value>>* outArr);
|
||||
void seToJsValue(v8::Isolate* isolate, const Value& v, v8::Local<v8::Value>* outJsVal);
|
||||
|
||||
void setReturnValue(const Value& data, const v8::FunctionCallbackInfo<v8::Value>& argv);
|
||||
void setReturnValue(const Value& data, const v8::PropertyCallbackInfo<v8::Value>& argv);
|
||||
|
||||
bool hasPrivate(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
||||
void setPrivate(v8::Isolate* isolate, ObjectWrap& wrap, void* data, PrivateData** outInternalData);
|
||||
void* getPrivate(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
||||
void clearPrivate(v8::Isolate* isolate, ObjectWrap& wrap);
|
||||
|
||||
} // namespace internal {
|
||||
} // namespace se {
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
@@ -0,0 +1,530 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//#include "mozilla/Assertions.h"
|
||||
//#include "mozilla/EndianUtils.h"
|
||||
#include "SHA1.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# include <stdlib.h>
|
||||
# pragma intrinsic(_byteswap_ushort)
|
||||
# pragma intrinsic(_byteswap_ulong)
|
||||
# pragma intrinsic(_byteswap_uint64)
|
||||
#endif
|
||||
|
||||
#define MOZ_ASSERT(cond, ...) assert(cond)
|
||||
|
||||
//using se::NativeEndian;
|
||||
using se::SHA1Sum;
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(_WIN64)
|
||||
# if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
# else
|
||||
# error "CPU type is unknown"
|
||||
# endif
|
||||
#elif defined(_WIN32)
|
||||
# if defined(_M_IX86)
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
# elif defined(_M_ARM)
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
# else
|
||||
# error "CPU type is unknown"
|
||||
# endif
|
||||
#elif defined(__APPLE__) || defined(__powerpc__) || defined(__ppc__)
|
||||
# if __LITTLE_ENDIAN__
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
# elif __BIG_ENDIAN__
|
||||
# define MOZ_BIG_ENDIAN 1
|
||||
# endif
|
||||
#elif defined(__GNUC__) && \
|
||||
defined(__BYTE_ORDER__) && \
|
||||
defined(__ORDER_LITTLE_ENDIAN__) && \
|
||||
defined(__ORDER_BIG_ENDIAN__)
|
||||
/*
|
||||
* Some versions of GCC provide architecture-independent macros for
|
||||
* this. Yes, there are more than two values for __BYTE_ORDER__.
|
||||
*/
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define MOZ_BIG_ENDIAN 1
|
||||
# else
|
||||
# error "Can't handle mixed-endian architectures"
|
||||
# endif
|
||||
/*
|
||||
* We can't include useful headers like <endian.h> or <sys/isa_defs.h>
|
||||
* here because they're not present on all platforms. Instead we have
|
||||
* this big conditional that ideally will catch all the interesting
|
||||
* cases.
|
||||
*/
|
||||
#elif defined(__sparc) || defined(__sparc__) || \
|
||||
defined(_POWER) || defined(__hppa) || \
|
||||
defined(_MIPSEB) || defined(__ARMEB__) || \
|
||||
defined(__s390__) || defined(__AARCH64EB__) || \
|
||||
(defined(__sh__) && defined(__LITTLE_ENDIAN__)) || \
|
||||
(defined(__ia64) && defined(__BIG_ENDIAN__))
|
||||
# define MOZ_BIG_ENDIAN 1
|
||||
#elif defined(__i386) || defined(__i386__) || \
|
||||
defined(__x86_64) || defined(__x86_64__) || \
|
||||
defined(_MIPSEL) || defined(__ARMEL__) || \
|
||||
defined(__alpha__) || defined(__AARCH64EL__) || \
|
||||
(defined(__sh__) && defined(__BIG_ENDIAN__)) || \
|
||||
(defined(__ia64) && !defined(__BIG_ENDIAN__))
|
||||
# define MOZ_LITTLE_ENDIAN 1
|
||||
#endif
|
||||
|
||||
#if MOZ_BIG_ENDIAN
|
||||
# define MOZ_LITTLE_ENDIAN 0
|
||||
#elif MOZ_LITTLE_ENDIAN
|
||||
# define MOZ_BIG_ENDIAN 0
|
||||
#else
|
||||
# error "Cannot determine endianness"
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
# if __has_builtin(__builtin_bswap16)
|
||||
# define MOZ_HAVE_BUILTIN_BYTESWAP16 __builtin_bswap16
|
||||
# endif
|
||||
#elif defined(__GNUC__)
|
||||
# define MOZ_HAVE_BUILTIN_BYTESWAP16 __builtin_bswap16
|
||||
#elif defined(_MSC_VER)
|
||||
# define MOZ_HAVE_BUILTIN_BYTESWAP16 _byteswap_ushort
|
||||
#endif
|
||||
|
||||
enum Endianness { Little, Big };
|
||||
|
||||
#if MOZ_BIG_ENDIAN
|
||||
# define MOZ_NATIVE_ENDIANNESS Big
|
||||
#else
|
||||
# define MOZ_NATIVE_ENDIANNESS Little
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We need wrappers here because free functions with default template
|
||||
* arguments and/or partial specialization of function templates are not
|
||||
* supported by all the compilers we use.
|
||||
*/
|
||||
template<typename T, size_t Size = sizeof(T)>
|
||||
struct Swapper;
|
||||
|
||||
template<typename T>
|
||||
struct Swapper<T, 2>
|
||||
{
|
||||
static T swap(T aValue)
|
||||
{
|
||||
#if defined(MOZ_HAVE_BUILTIN_BYTESWAP16)
|
||||
return MOZ_HAVE_BUILTIN_BYTESWAP16(aValue);
|
||||
#else
|
||||
return T(((aValue & 0x00ff) << 8) | ((aValue & 0xff00) >> 8));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Swapper<T, 4>
|
||||
{
|
||||
static T swap(T aValue)
|
||||
{
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
return T(__builtin_bswap32(aValue));
|
||||
#elif defined(_MSC_VER)
|
||||
return T(_byteswap_ulong(aValue));
|
||||
#else
|
||||
return T(((aValue & 0x000000ffU) << 24) |
|
||||
((aValue & 0x0000ff00U) << 8) |
|
||||
((aValue & 0x00ff0000U) >> 8) |
|
||||
((aValue & 0xff000000U) >> 24));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Swapper<T, 8>
|
||||
{
|
||||
static inline T swap(T aValue)
|
||||
{
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
return T(__builtin_bswap64(aValue));
|
||||
#elif defined(_MSC_VER)
|
||||
return T(_byteswap_uint64(aValue));
|
||||
#else
|
||||
return T(((aValue & 0x00000000000000ffULL) << 56) |
|
||||
((aValue & 0x000000000000ff00ULL) << 40) |
|
||||
((aValue & 0x0000000000ff0000ULL) << 24) |
|
||||
((aValue & 0x00000000ff000000ULL) << 8) |
|
||||
((aValue & 0x000000ff00000000ULL) >> 8) |
|
||||
((aValue & 0x0000ff0000000000ULL) >> 24) |
|
||||
((aValue & 0x00ff000000000000ULL) >> 40) |
|
||||
((aValue & 0xff00000000000000ULL) >> 56));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
template<Endianness ThisEndian>
|
||||
class Endian
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
static T swapToBigEndian(T aValue)
|
||||
{
|
||||
return maybeSwap<ThisEndian, Big>(aValue);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Return |aValue| converted from SourceEndian encoding to DestEndian
|
||||
* encoding.
|
||||
*/
|
||||
template<Endianness SourceEndian, Endianness DestEndian, typename T>
|
||||
static inline T maybeSwap(T aValue)
|
||||
{
|
||||
if (SourceEndian == DestEndian) {
|
||||
return aValue;
|
||||
}
|
||||
return Swapper<T>::swap(aValue);
|
||||
}
|
||||
};
|
||||
|
||||
class NativeEndian final : public Endian<MOZ_NATIVE_ENDIANNESS>
|
||||
{
|
||||
private:
|
||||
typedef Endian<MOZ_NATIVE_ENDIANNESS> super;
|
||||
|
||||
public:
|
||||
using super::swapToBigEndian;
|
||||
};
|
||||
|
||||
} // namespace {
|
||||
|
||||
static inline uint32_t
|
||||
SHA_ROTL(uint32_t aT, uint32_t aN)
|
||||
{
|
||||
MOZ_ASSERT(aN < 32);
|
||||
return (aT << aN) | (aT >> (32 - aN));
|
||||
}
|
||||
|
||||
static void
|
||||
shaCompress(volatile unsigned* aX, const uint32_t* aBuf);
|
||||
|
||||
#define SHA_F1(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z))
|
||||
#define SHA_F2(X, Y, Z) ((X) ^ (Y) ^ (Z))
|
||||
#define SHA_F3(X, Y, Z) (((X) & (Y)) | ((Z) & ((X) | (Y))))
|
||||
#define SHA_F4(X, Y, Z) ((X) ^ (Y) ^ (Z))
|
||||
|
||||
#define SHA_MIX(n, a, b, c) XW(n) = SHA_ROTL(XW(a) ^ XW(b) ^ XW(c) ^XW(n), 1)
|
||||
|
||||
SHA1Sum::SHA1Sum()
|
||||
: mSize(0), mDone(false)
|
||||
{
|
||||
// Initialize H with constants from FIPS180-1.
|
||||
mH[0] = 0x67452301L;
|
||||
mH[1] = 0xefcdab89L;
|
||||
mH[2] = 0x98badcfeL;
|
||||
mH[3] = 0x10325476L;
|
||||
mH[4] = 0xc3d2e1f0L;
|
||||
}
|
||||
|
||||
/*
|
||||
* Explanation of H array and index values:
|
||||
*
|
||||
* The context's H array is actually the concatenation of two arrays
|
||||
* defined by SHA1, the H array of state variables (5 elements),
|
||||
* and the W array of intermediate values, of which there are 16 elements.
|
||||
* The W array starts at H[5], that is W[0] is H[5].
|
||||
* Although these values are defined as 32-bit values, we use 64-bit
|
||||
* variables to hold them because the AMD64 stores 64 bit values in
|
||||
* memory MUCH faster than it stores any smaller values.
|
||||
*
|
||||
* Rather than passing the context structure to shaCompress, we pass
|
||||
* this combined array of H and W values. We do not pass the address
|
||||
* of the first element of this array, but rather pass the address of an
|
||||
* element in the middle of the array, element X. Presently X[0] is H[11].
|
||||
* So we pass the address of H[11] as the address of array X to shaCompress.
|
||||
* Then shaCompress accesses the members of the array using positive AND
|
||||
* negative indexes.
|
||||
*
|
||||
* Pictorially: (each element is 8 bytes)
|
||||
* H | H0 H1 H2 H3 H4 W0 W1 W2 W3 W4 W5 W6 W7 W8 W9 Wa Wb Wc Wd We Wf |
|
||||
* X |-11-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 |
|
||||
*
|
||||
* The byte offset from X[0] to any member of H and W is always
|
||||
* representable in a signed 8-bit value, which will be encoded
|
||||
* as a single byte offset in the X86-64 instruction set.
|
||||
* If we didn't pass the address of H[11], and instead passed the
|
||||
* address of H[0], the offsets to elements H[16] and above would be
|
||||
* greater than 127, not representable in a signed 8-bit value, and the
|
||||
* x86-64 instruction set would encode every such offset as a 32-bit
|
||||
* signed number in each instruction that accessed element H[16] or
|
||||
* higher. This results in much bigger and slower code.
|
||||
*/
|
||||
#define H2X 11 /* X[0] is H[11], and H[0] is X[-11] */
|
||||
#define W2X 6 /* X[0] is W[6], and W[0] is X[-6] */
|
||||
|
||||
/*
|
||||
* SHA: Add data to context.
|
||||
*/
|
||||
void
|
||||
SHA1Sum::update(const void* aData, uint32_t aLen)
|
||||
{
|
||||
MOZ_ASSERT(!mDone, "SHA1Sum can only be used to compute a single hash.");
|
||||
|
||||
const uint8_t* data = static_cast<const uint8_t*>(aData);
|
||||
|
||||
if (aLen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Accumulate the byte count. */
|
||||
unsigned int lenB = static_cast<unsigned int>(mSize) & 63U;
|
||||
|
||||
mSize += aLen;
|
||||
|
||||
/* Read the data into W and process blocks as they get full. */
|
||||
unsigned int togo;
|
||||
if (lenB > 0) {
|
||||
togo = 64U - lenB;
|
||||
if (aLen < togo) {
|
||||
togo = aLen;
|
||||
}
|
||||
memcpy(mU.mB + lenB, data, togo);
|
||||
aLen -= togo;
|
||||
data += togo;
|
||||
lenB = (lenB + togo) & 63U;
|
||||
if (!lenB) {
|
||||
shaCompress(&mH[H2X], mU.mW);
|
||||
}
|
||||
}
|
||||
|
||||
while (aLen >= 64U) {
|
||||
aLen -= 64U;
|
||||
shaCompress(&mH[H2X], reinterpret_cast<const uint32_t*>(data));
|
||||
data += 64U;
|
||||
}
|
||||
|
||||
if (aLen > 0) {
|
||||
memcpy(mU.mB, data, aLen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SHA: Generate hash value
|
||||
*/
|
||||
void
|
||||
SHA1Sum::finish(SHA1Sum::Hash& aHashOut)
|
||||
{
|
||||
MOZ_ASSERT(!mDone, "SHA1Sum can only be used to compute a single hash.");
|
||||
|
||||
uint64_t size = mSize;
|
||||
uint32_t lenB = uint32_t(size) & 63;
|
||||
|
||||
static const uint8_t bulk_pad[64] =
|
||||
{ 0x80,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
|
||||
|
||||
/* Pad with a binary 1 (e.g. 0x80), then zeroes, then length in bits. */
|
||||
update(bulk_pad, (((55 + 64) - lenB) & 63) + 1);
|
||||
MOZ_ASSERT((uint32_t(mSize) & 63) == 56);
|
||||
|
||||
/* Convert size from bytes to bits. */
|
||||
size <<= 3;
|
||||
mU.mW[14] = NativeEndian::swapToBigEndian(uint32_t(size >> 32));
|
||||
mU.mW[15] = NativeEndian::swapToBigEndian(uint32_t(size));
|
||||
shaCompress(&mH[H2X], mU.mW);
|
||||
|
||||
/* Output hash. */
|
||||
mU.mW[0] = NativeEndian::swapToBigEndian(mH[0]);
|
||||
mU.mW[1] = NativeEndian::swapToBigEndian(mH[1]);
|
||||
mU.mW[2] = NativeEndian::swapToBigEndian(mH[2]);
|
||||
mU.mW[3] = NativeEndian::swapToBigEndian(mH[3]);
|
||||
mU.mW[4] = NativeEndian::swapToBigEndian(mH[4]);
|
||||
memcpy(aHashOut, mU.mW, 20);
|
||||
mDone = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* SHA: Compression function, unrolled.
|
||||
*
|
||||
* Some operations in shaCompress are done as 5 groups of 16 operations.
|
||||
* Others are done as 4 groups of 20 operations.
|
||||
* The code below shows that structure.
|
||||
*
|
||||
* The functions that compute the new values of the 5 state variables
|
||||
* A-E are done in 4 groups of 20 operations (or you may also think
|
||||
* of them as being done in 16 groups of 5 operations). They are
|
||||
* done by the SHA_RNDx macros below, in the right column.
|
||||
*
|
||||
* The functions that set the 16 values of the W array are done in
|
||||
* 5 groups of 16 operations. The first group is done by the
|
||||
* LOAD macros below, the latter 4 groups are done by SHA_MIX below,
|
||||
* in the left column.
|
||||
*
|
||||
* gcc's optimizer observes that each member of the W array is assigned
|
||||
* a value 5 times in this code. It reduces the number of store
|
||||
* operations done to the W array in the context (that is, in the X array)
|
||||
* by creating a W array on the stack, and storing the W values there for
|
||||
* the first 4 groups of operations on W, and storing the values in the
|
||||
* context's W array only in the fifth group. This is undesirable.
|
||||
* It is MUCH bigger code than simply using the context's W array, because
|
||||
* all the offsets to the W array in the stack are 32-bit signed offsets,
|
||||
* and it is no faster than storing the values in the context's W array.
|
||||
*
|
||||
* The original code for sha_fast.c prevented this creation of a separate
|
||||
* W array in the stack by creating a W array of 80 members, each of
|
||||
* whose elements is assigned only once. It also separated the computations
|
||||
* of the W array values and the computations of the values for the 5
|
||||
* state variables into two separate passes, W's, then A-E's so that the
|
||||
* second pass could be done all in registers (except for accessing the W
|
||||
* array) on machines with fewer registers. The method is suboptimal
|
||||
* for machines with enough registers to do it all in one pass, and it
|
||||
* necessitates using many instructions with 32-bit offsets.
|
||||
*
|
||||
* This code eliminates the separate W array on the stack by a completely
|
||||
* different means: by declaring the X array volatile. This prevents
|
||||
* the optimizer from trying to reduce the use of the X array by the
|
||||
* creation of a MORE expensive W array on the stack. The result is
|
||||
* that all instructions use signed 8-bit offsets and not 32-bit offsets.
|
||||
*
|
||||
* The combination of this code and the -O3 optimizer flag on GCC 3.4.3
|
||||
* results in code that is 3 times faster than the previous NSS sha_fast
|
||||
* code on AMD64.
|
||||
*/
|
||||
static void
|
||||
shaCompress(volatile unsigned* aX, const uint32_t* aBuf)
|
||||
{
|
||||
unsigned A, B, C, D, E;
|
||||
|
||||
#define XH(n) aX[n - H2X]
|
||||
#define XW(n) aX[n - W2X]
|
||||
|
||||
#define K0 0x5a827999L
|
||||
#define K1 0x6ed9eba1L
|
||||
#define K2 0x8f1bbcdcL
|
||||
#define K3 0xca62c1d6L
|
||||
|
||||
#define SHA_RND1(a, b, c, d, e, n) \
|
||||
a = SHA_ROTL(b, 5) + SHA_F1(c, d, e) + a + XW(n) + K0; c = SHA_ROTL(c, 30)
|
||||
#define SHA_RND2(a, b, c, d, e, n) \
|
||||
a = SHA_ROTL(b, 5) + SHA_F2(c, d, e) + a + XW(n) + K1; c = SHA_ROTL(c, 30)
|
||||
#define SHA_RND3(a, b, c, d, e, n) \
|
||||
a = SHA_ROTL(b, 5) + SHA_F3(c, d, e) + a + XW(n) + K2; c = SHA_ROTL(c, 30)
|
||||
#define SHA_RND4(a, b, c, d, e, n) \
|
||||
a = SHA_ROTL(b ,5) + SHA_F4(c, d, e) + a + XW(n) + K3; c = SHA_ROTL(c, 30)
|
||||
|
||||
#define LOAD(n) XW(n) = NativeEndian::swapToBigEndian(aBuf[n])
|
||||
|
||||
A = XH(0);
|
||||
B = XH(1);
|
||||
C = XH(2);
|
||||
D = XH(3);
|
||||
E = XH(4);
|
||||
|
||||
LOAD(0); SHA_RND1(E,A,B,C,D, 0);
|
||||
LOAD(1); SHA_RND1(D,E,A,B,C, 1);
|
||||
LOAD(2); SHA_RND1(C,D,E,A,B, 2);
|
||||
LOAD(3); SHA_RND1(B,C,D,E,A, 3);
|
||||
LOAD(4); SHA_RND1(A,B,C,D,E, 4);
|
||||
LOAD(5); SHA_RND1(E,A,B,C,D, 5);
|
||||
LOAD(6); SHA_RND1(D,E,A,B,C, 6);
|
||||
LOAD(7); SHA_RND1(C,D,E,A,B, 7);
|
||||
LOAD(8); SHA_RND1(B,C,D,E,A, 8);
|
||||
LOAD(9); SHA_RND1(A,B,C,D,E, 9);
|
||||
LOAD(10); SHA_RND1(E,A,B,C,D,10);
|
||||
LOAD(11); SHA_RND1(D,E,A,B,C,11);
|
||||
LOAD(12); SHA_RND1(C,D,E,A,B,12);
|
||||
LOAD(13); SHA_RND1(B,C,D,E,A,13);
|
||||
LOAD(14); SHA_RND1(A,B,C,D,E,14);
|
||||
LOAD(15); SHA_RND1(E,A,B,C,D,15);
|
||||
|
||||
SHA_MIX( 0, 13, 8, 2); SHA_RND1(D,E,A,B,C, 0);
|
||||
SHA_MIX( 1, 14, 9, 3); SHA_RND1(C,D,E,A,B, 1);
|
||||
SHA_MIX( 2, 15, 10, 4); SHA_RND1(B,C,D,E,A, 2);
|
||||
SHA_MIX( 3, 0, 11, 5); SHA_RND1(A,B,C,D,E, 3);
|
||||
|
||||
SHA_MIX( 4, 1, 12, 6); SHA_RND2(E,A,B,C,D, 4);
|
||||
SHA_MIX( 5, 2, 13, 7); SHA_RND2(D,E,A,B,C, 5);
|
||||
SHA_MIX( 6, 3, 14, 8); SHA_RND2(C,D,E,A,B, 6);
|
||||
SHA_MIX( 7, 4, 15, 9); SHA_RND2(B,C,D,E,A, 7);
|
||||
SHA_MIX( 8, 5, 0, 10); SHA_RND2(A,B,C,D,E, 8);
|
||||
SHA_MIX( 9, 6, 1, 11); SHA_RND2(E,A,B,C,D, 9);
|
||||
SHA_MIX(10, 7, 2, 12); SHA_RND2(D,E,A,B,C,10);
|
||||
SHA_MIX(11, 8, 3, 13); SHA_RND2(C,D,E,A,B,11);
|
||||
SHA_MIX(12, 9, 4, 14); SHA_RND2(B,C,D,E,A,12);
|
||||
SHA_MIX(13, 10, 5, 15); SHA_RND2(A,B,C,D,E,13);
|
||||
SHA_MIX(14, 11, 6, 0); SHA_RND2(E,A,B,C,D,14);
|
||||
SHA_MIX(15, 12, 7, 1); SHA_RND2(D,E,A,B,C,15);
|
||||
|
||||
SHA_MIX( 0, 13, 8, 2); SHA_RND2(C,D,E,A,B, 0);
|
||||
SHA_MIX( 1, 14, 9, 3); SHA_RND2(B,C,D,E,A, 1);
|
||||
SHA_MIX( 2, 15, 10, 4); SHA_RND2(A,B,C,D,E, 2);
|
||||
SHA_MIX( 3, 0, 11, 5); SHA_RND2(E,A,B,C,D, 3);
|
||||
SHA_MIX( 4, 1, 12, 6); SHA_RND2(D,E,A,B,C, 4);
|
||||
SHA_MIX( 5, 2, 13, 7); SHA_RND2(C,D,E,A,B, 5);
|
||||
SHA_MIX( 6, 3, 14, 8); SHA_RND2(B,C,D,E,A, 6);
|
||||
SHA_MIX( 7, 4, 15, 9); SHA_RND2(A,B,C,D,E, 7);
|
||||
|
||||
SHA_MIX( 8, 5, 0, 10); SHA_RND3(E,A,B,C,D, 8);
|
||||
SHA_MIX( 9, 6, 1, 11); SHA_RND3(D,E,A,B,C, 9);
|
||||
SHA_MIX(10, 7, 2, 12); SHA_RND3(C,D,E,A,B,10);
|
||||
SHA_MIX(11, 8, 3, 13); SHA_RND3(B,C,D,E,A,11);
|
||||
SHA_MIX(12, 9, 4, 14); SHA_RND3(A,B,C,D,E,12);
|
||||
SHA_MIX(13, 10, 5, 15); SHA_RND3(E,A,B,C,D,13);
|
||||
SHA_MIX(14, 11, 6, 0); SHA_RND3(D,E,A,B,C,14);
|
||||
SHA_MIX(15, 12, 7, 1); SHA_RND3(C,D,E,A,B,15);
|
||||
|
||||
SHA_MIX( 0, 13, 8, 2); SHA_RND3(B,C,D,E,A, 0);
|
||||
SHA_MIX( 1, 14, 9, 3); SHA_RND3(A,B,C,D,E, 1);
|
||||
SHA_MIX( 2, 15, 10, 4); SHA_RND3(E,A,B,C,D, 2);
|
||||
SHA_MIX( 3, 0, 11, 5); SHA_RND3(D,E,A,B,C, 3);
|
||||
SHA_MIX( 4, 1, 12, 6); SHA_RND3(C,D,E,A,B, 4);
|
||||
SHA_MIX( 5, 2, 13, 7); SHA_RND3(B,C,D,E,A, 5);
|
||||
SHA_MIX( 6, 3, 14, 8); SHA_RND3(A,B,C,D,E, 6);
|
||||
SHA_MIX( 7, 4, 15, 9); SHA_RND3(E,A,B,C,D, 7);
|
||||
SHA_MIX( 8, 5, 0, 10); SHA_RND3(D,E,A,B,C, 8);
|
||||
SHA_MIX( 9, 6, 1, 11); SHA_RND3(C,D,E,A,B, 9);
|
||||
SHA_MIX(10, 7, 2, 12); SHA_RND3(B,C,D,E,A,10);
|
||||
SHA_MIX(11, 8, 3, 13); SHA_RND3(A,B,C,D,E,11);
|
||||
|
||||
SHA_MIX(12, 9, 4, 14); SHA_RND4(E,A,B,C,D,12);
|
||||
SHA_MIX(13, 10, 5, 15); SHA_RND4(D,E,A,B,C,13);
|
||||
SHA_MIX(14, 11, 6, 0); SHA_RND4(C,D,E,A,B,14);
|
||||
SHA_MIX(15, 12, 7, 1); SHA_RND4(B,C,D,E,A,15);
|
||||
|
||||
SHA_MIX( 0, 13, 8, 2); SHA_RND4(A,B,C,D,E, 0);
|
||||
SHA_MIX( 1, 14, 9, 3); SHA_RND4(E,A,B,C,D, 1);
|
||||
SHA_MIX( 2, 15, 10, 4); SHA_RND4(D,E,A,B,C, 2);
|
||||
SHA_MIX( 3, 0, 11, 5); SHA_RND4(C,D,E,A,B, 3);
|
||||
SHA_MIX( 4, 1, 12, 6); SHA_RND4(B,C,D,E,A, 4);
|
||||
SHA_MIX( 5, 2, 13, 7); SHA_RND4(A,B,C,D,E, 5);
|
||||
SHA_MIX( 6, 3, 14, 8); SHA_RND4(E,A,B,C,D, 6);
|
||||
SHA_MIX( 7, 4, 15, 9); SHA_RND4(D,E,A,B,C, 7);
|
||||
SHA_MIX( 8, 5, 0, 10); SHA_RND4(C,D,E,A,B, 8);
|
||||
SHA_MIX( 9, 6, 1, 11); SHA_RND4(B,C,D,E,A, 9);
|
||||
SHA_MIX(10, 7, 2, 12); SHA_RND4(A,B,C,D,E,10);
|
||||
SHA_MIX(11, 8, 3, 13); SHA_RND4(E,A,B,C,D,11);
|
||||
SHA_MIX(12, 9, 4, 14); SHA_RND4(D,E,A,B,C,12);
|
||||
SHA_MIX(13, 10, 5, 15); SHA_RND4(C,D,E,A,B,13);
|
||||
SHA_MIX(14, 11, 6, 0); SHA_RND4(B,C,D,E,A,14);
|
||||
SHA_MIX(15, 12, 7, 1); SHA_RND4(A,B,C,D,E,15);
|
||||
|
||||
XH(0) += A;
|
||||
XH(1) += B;
|
||||
XH(2) += C;
|
||||
XH(3) += D;
|
||||
XH(4) += E;
|
||||
}
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,72 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Simple class for computing SHA1. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
//#include "mozilla/Types.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef MFBT_API
|
||||
#undef MFBT_API
|
||||
#endif
|
||||
|
||||
#define MFBT_API
|
||||
|
||||
namespace se {
|
||||
|
||||
/**
|
||||
* This class computes the SHA1 hash of a byte sequence, or of the concatenation
|
||||
* of multiple sequences. For example, computing the SHA1 of two sequences of
|
||||
* bytes could be done as follows:
|
||||
*
|
||||
* void SHA1(const uint8_t* buf1, uint32_t size1,
|
||||
* const uint8_t* buf2, uint32_t size2,
|
||||
* SHA1Sum::Hash& hash)
|
||||
* {
|
||||
* SHA1Sum s;
|
||||
* s.update(buf1, size1);
|
||||
* s.update(buf2, size2);
|
||||
* s.finish(hash);
|
||||
* }
|
||||
*
|
||||
* The finish method may only be called once and cannot be followed by calls
|
||||
* to update.
|
||||
*/
|
||||
class SHA1Sum
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t mW[16]; /* input buffer */
|
||||
uint8_t mB[64];
|
||||
} mU;
|
||||
uint64_t mSize; /* count of hashed bytes. */
|
||||
unsigned mH[22]; /* 5 state variables, 16 tmp values, 1 extra */
|
||||
bool mDone;
|
||||
|
||||
public:
|
||||
MFBT_API SHA1Sum();
|
||||
|
||||
static const size_t kHashSize = 20;
|
||||
typedef uint8_t Hash[kHashSize];
|
||||
|
||||
/* Add len bytes of dataIn to the data sequence being hashed. */
|
||||
MFBT_API void update(const void* aData, uint32_t aLength);
|
||||
|
||||
/* Compute the final hash of all data into hashOut. */
|
||||
MFBT_API void finish(SHA1Sum::Hash& aHashOut);
|
||||
};
|
||||
|
||||
} /* namespace se */
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
#ifndef SRC_BASE64_H_
|
||||
#define SRC_BASE64_H_
|
||||
|
||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace node {
|
||||
//// Base 64 ////
|
||||
#define base64_encoded_size(size) ((size + 2 - ((size + 2) % 3)) / 3 * 4)
|
||||
|
||||
|
||||
// Doesn't check for padding at the end. Can be 1-2 bytes over.
|
||||
static inline size_t base64_decoded_size_fast(size_t size) {
|
||||
size_t remainder = size % 4;
|
||||
|
||||
size = (size / 4) * 3;
|
||||
if (remainder) {
|
||||
if (size == 0 && remainder == 1) {
|
||||
// special case: 1-byte input cannot be decoded
|
||||
size = 0;
|
||||
} else {
|
||||
// non-padded input, add 1 or 2 extra bytes
|
||||
size += 1 + (remainder == 3);
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
template <typename TypeName>
|
||||
size_t base64_decoded_size(const TypeName* src, size_t size) {
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
if (src[size - 1] == '=')
|
||||
size--;
|
||||
if (size > 0 && src[size - 1] == '=')
|
||||
size--;
|
||||
|
||||
return base64_decoded_size_fast(size);
|
||||
}
|
||||
|
||||
|
||||
extern const int8_t unbase64_table[256];
|
||||
|
||||
|
||||
#define unbase64(x) \
|
||||
static_cast<uint8_t>(unbase64_table[static_cast<uint8_t>(x)])
|
||||
|
||||
|
||||
template <typename TypeName>
|
||||
bool base64_decode_group_slow(char* const dst, const size_t dstlen,
|
||||
const TypeName* const src, const size_t srclen,
|
||||
size_t* const i, size_t* const k) {
|
||||
uint8_t hi;
|
||||
uint8_t lo;
|
||||
#define V(expr) \
|
||||
for (;;) { \
|
||||
const uint8_t c = src[*i]; \
|
||||
lo = unbase64(c); \
|
||||
*i += 1; \
|
||||
if (lo < 64) \
|
||||
break; /* Legal character. */ \
|
||||
if (c == '=' || *i >= srclen) \
|
||||
return false; /* Stop decoding. */ \
|
||||
} \
|
||||
expr; \
|
||||
if (*i >= srclen) \
|
||||
return false; \
|
||||
if (*k >= dstlen) \
|
||||
return false; \
|
||||
hi = lo;
|
||||
V(/* Nothing. */);
|
||||
V(dst[(*k)++] = ((hi & 0x3F) << 2) | ((lo & 0x30) >> 4));
|
||||
V(dst[(*k)++] = ((hi & 0x0F) << 4) | ((lo & 0x3C) >> 2));
|
||||
V(dst[(*k)++] = ((hi & 0x03) << 6) | ((lo & 0x3F) >> 0));
|
||||
#undef V
|
||||
return true; // Continue decoding.
|
||||
}
|
||||
|
||||
|
||||
template <typename TypeName>
|
||||
size_t base64_decode_fast(char* const dst, const size_t dstlen,
|
||||
const TypeName* const src, const size_t srclen,
|
||||
const size_t decoded_size) {
|
||||
const size_t available = dstlen < decoded_size ? dstlen : decoded_size;
|
||||
const size_t max_k = available / 3 * 3;
|
||||
size_t max_i = srclen / 4 * 4;
|
||||
size_t i = 0;
|
||||
size_t k = 0;
|
||||
while (i < max_i && k < max_k) {
|
||||
const uint32_t v =
|
||||
unbase64(src[i + 0]) << 24 |
|
||||
unbase64(src[i + 1]) << 16 |
|
||||
unbase64(src[i + 2]) << 8 |
|
||||
unbase64(src[i + 3]);
|
||||
// If MSB is set, input contains whitespace or is not valid base64.
|
||||
if (v & 0x80808080) {
|
||||
if (!base64_decode_group_slow(dst, dstlen, src, srclen, &i, &k))
|
||||
return k;
|
||||
max_i = i + (srclen - i) / 4 * 4; // Align max_i again.
|
||||
} else {
|
||||
dst[k + 0] = ((v >> 22) & 0xFC) | ((v >> 20) & 0x03);
|
||||
dst[k + 1] = ((v >> 12) & 0xF0) | ((v >> 10) & 0x0F);
|
||||
dst[k + 2] = ((v >> 2) & 0xC0) | ((v >> 0) & 0x3F);
|
||||
i += 4;
|
||||
k += 3;
|
||||
}
|
||||
}
|
||||
if (i < srclen && k < dstlen) {
|
||||
base64_decode_group_slow(dst, dstlen, src, srclen, &i, &k);
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
|
||||
template <typename TypeName>
|
||||
size_t base64_decode(char* const dst, const size_t dstlen,
|
||||
const TypeName* const src, const size_t srclen) {
|
||||
const size_t decoded_size = base64_decoded_size(src, srclen);
|
||||
return base64_decode_fast(dst, dstlen, src, srclen, decoded_size);
|
||||
}
|
||||
|
||||
static size_t base64_encode(const char* src,
|
||||
size_t slen,
|
||||
char* dst,
|
||||
size_t dlen) {
|
||||
// We know how much we'll write, just make sure that there's space.
|
||||
CHECK(dlen >= base64_encoded_size(slen) &&
|
||||
"not enough space provided for base64 encode");
|
||||
|
||||
dlen = base64_encoded_size(slen);
|
||||
|
||||
unsigned a;
|
||||
unsigned b;
|
||||
unsigned c;
|
||||
unsigned i;
|
||||
unsigned k;
|
||||
unsigned n;
|
||||
|
||||
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
i = 0;
|
||||
k = 0;
|
||||
n = slen / 3 * 3;
|
||||
|
||||
while (i < n) {
|
||||
a = src[i + 0] & 0xff;
|
||||
b = src[i + 1] & 0xff;
|
||||
c = src[i + 2] & 0xff;
|
||||
|
||||
dst[k + 0] = table[a >> 2];
|
||||
dst[k + 1] = table[((a & 3) << 4) | (b >> 4)];
|
||||
dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)];
|
||||
dst[k + 3] = table[c & 0x3f];
|
||||
|
||||
i += 3;
|
||||
k += 4;
|
||||
}
|
||||
|
||||
if (n != slen) {
|
||||
switch (slen - n) {
|
||||
case 1:
|
||||
a = src[i + 0] & 0xff;
|
||||
dst[k + 0] = table[a >> 2];
|
||||
dst[k + 1] = table[(a & 3) << 4];
|
||||
dst[k + 2] = '=';
|
||||
dst[k + 3] = '=';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
a = src[i + 0] & 0xff;
|
||||
b = src[i + 1] & 0xff;
|
||||
dst[k + 0] = table[a >> 2];
|
||||
dst[k + 1] = table[((a & 3) << 4) | (b >> 4)];
|
||||
dst[k + 2] = table[(b & 0x0f) << 2];
|
||||
dst[k + 3] = '=';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dlen;
|
||||
}
|
||||
} // namespace node
|
||||
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#endif // SRC_BASE64_H_
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "env.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "uv.h"
|
||||
|
||||
using namespace v8;
|
||||
|
||||
namespace node {
|
||||
|
||||
void Environment::Start(int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv,
|
||||
bool start_profiler_idle_notifier)
|
||||
{
|
||||
HandleScope handle_scope(isolate());
|
||||
Context::Scope context_scope(context());
|
||||
|
||||
// uv_check_init(event_loop(), immediate_check_handle());
|
||||
// uv_unref(reinterpret_cast<uv_handle_t*>(immediate_check_handle()));
|
||||
//
|
||||
// uv_idle_init(event_loop(), immediate_idle_handle());
|
||||
//
|
||||
// // Inform V8's CPU profiler when we're idle. The profiler is sampling-based
|
||||
// // but not all samples are created equal; mark the wall clock time spent in
|
||||
// // epoll_wait() and friends so profiling tools can filter it out. The samples
|
||||
// // still end up in v8.log but with state=IDLE rather than state=EXTERNAL.
|
||||
// // REFINE(bnoordhuis) Depends on a libuv implementation detail that we should
|
||||
// // probably fortify in the API contract, namely that the last started prepare
|
||||
// // or check watcher runs first. It's not 100% foolproof; if an add-on starts
|
||||
// // a prepare or check watcher after us, any samples attributed to its callback
|
||||
// // will be recorded with state=IDLE.
|
||||
// uv_prepare_init(event_loop(), &idle_prepare_handle_);
|
||||
// uv_check_init(event_loop(), &idle_check_handle_);
|
||||
// uv_unref(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
|
||||
// uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
|
||||
//
|
||||
// uv_timer_init(event_loop(), destroy_ids_timer_handle());
|
||||
//
|
||||
// auto close_and_finish = [](Environment* env, uv_handle_t* handle, void* arg) {
|
||||
// handle->data = env;
|
||||
//
|
||||
// uv_close(handle, [](uv_handle_t* handle) {
|
||||
// static_cast<Environment*>(handle->data)->FinishHandleCleanup(handle);
|
||||
// });
|
||||
// };
|
||||
//
|
||||
// RegisterHandleCleanup(
|
||||
// reinterpret_cast<uv_handle_t*>(immediate_check_handle()),
|
||||
// close_and_finish,
|
||||
// nullptr);
|
||||
// RegisterHandleCleanup(
|
||||
// reinterpret_cast<uv_handle_t*>(immediate_idle_handle()),
|
||||
// close_and_finish,
|
||||
// nullptr);
|
||||
// RegisterHandleCleanup(
|
||||
// reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_),
|
||||
// close_and_finish,
|
||||
// nullptr);
|
||||
// RegisterHandleCleanup(
|
||||
// reinterpret_cast<uv_handle_t*>(&idle_check_handle_),
|
||||
// close_and_finish,
|
||||
// nullptr);
|
||||
|
||||
if (start_profiler_idle_notifier) {
|
||||
StartProfilerIdleNotifier();
|
||||
}
|
||||
|
||||
auto process_template = FunctionTemplate::New(isolate());
|
||||
process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate(), "process"));
|
||||
|
||||
auto process_object =
|
||||
process_template->GetFunction(context()).ToLocalChecked()->NewInstance(context()).ToLocalChecked();
|
||||
set_process_object(process_object);
|
||||
|
||||
SetupProcessObject(this, argc, argv, exec_argc, exec_argv);
|
||||
// LoadAsyncWrapperInfo(this);
|
||||
}
|
||||
|
||||
void Environment::AssignToContext(v8::Local<v8::Context> context)
|
||||
{
|
||||
context->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex, this);
|
||||
}
|
||||
|
||||
void Environment::CleanupHandles()
|
||||
{
|
||||
// while (HandleCleanup* hc = handle_cleanup_queue_.PopFront()) {
|
||||
// handle_cleanup_waiting_++;
|
||||
// hc->cb_(this, hc->handle_, hc->arg_);
|
||||
// delete hc;
|
||||
// }
|
||||
//
|
||||
// while (handle_cleanup_waiting_ != 0)
|
||||
// uv_run(event_loop(), UV_RUN_ONCE);
|
||||
//
|
||||
// while (handle_cleanup_waiting_ != 0)
|
||||
// uv_run(event_loop(), UV_RUN_ONCE);
|
||||
}
|
||||
|
||||
void Environment::StartProfilerIdleNotifier()
|
||||
{
|
||||
// uv_prepare_start(&idle_prepare_handle_, [](uv_prepare_t* handle) {
|
||||
// Environment* env = ContainerOf(&Environment::idle_prepare_handle_, handle);
|
||||
// env->isolate()->GetCpuProfiler()->SetIdle(true);
|
||||
// });
|
||||
//
|
||||
// uv_check_start(&idle_check_handle_, [](uv_check_t* handle) {
|
||||
// Environment* env = ContainerOf(&Environment::idle_check_handle_, handle);
|
||||
// env->isolate()->GetCpuProfiler()->SetIdle(false);
|
||||
// });
|
||||
}
|
||||
|
||||
void Environment::StopProfilerIdleNotifier()
|
||||
{
|
||||
// uv_prepare_stop(&idle_prepare_handle_);
|
||||
// uv_check_stop(&idle_check_handle_);
|
||||
}
|
||||
|
||||
} // namespace node {
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,629 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "inspector_agent.h"
|
||||
|
||||
#include "v8.h"
|
||||
#include "uv.h"
|
||||
#include "util.h"
|
||||
#include "node.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
// Pick an index that's hopefully out of the way when we're embedded inside
|
||||
// another application. Performance-wise or memory-wise it doesn't matter:
|
||||
// Context::SetAlignedPointerInEmbedderData() is backed by a FixedArray,
|
||||
// worst case we pay a one-time penalty for resizing the array.
|
||||
#ifndef NODE_CONTEXT_EMBEDDER_DATA_INDEX
|
||||
#define NODE_CONTEXT_EMBEDDER_DATA_INDEX 32
|
||||
#endif
|
||||
|
||||
|
||||
// PER_ISOLATE_* macros: We have a lot of per-isolate properties
|
||||
// and adding and maintaining their getters and setters by hand would be
|
||||
// difficult so let's make the preprocessor generate them for us.
|
||||
//
|
||||
// In each macro, `V` is expected to be the name of a macro or function which
|
||||
// accepts the number of arguments provided in each tuple in the macro body,
|
||||
// typically two. The named function will be invoked against each tuple.
|
||||
//
|
||||
// Make sure that any macro V defined for use with the PER_ISOLATE_* macros is
|
||||
// undefined again after use.
|
||||
|
||||
// Private symbols are per-isolate primitives but Environment proxies them
|
||||
// for the sake of convenience. Strings should be ASCII-only and have a
|
||||
// "node:" prefix to avoid name clashes with third-party code.
|
||||
#define PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) \
|
||||
V(alpn_buffer_private_symbol, "node:alpnBuffer") \
|
||||
V(arrow_message_private_symbol, "node:arrowMessage") \
|
||||
V(contextify_context_private_symbol, "node:contextify:context") \
|
||||
V(contextify_global_private_symbol, "node:contextify:global") \
|
||||
V(inspector_delegate_private_symbol, "node:inspector:delegate") \
|
||||
V(decorated_private_symbol, "node:decorated") \
|
||||
V(npn_buffer_private_symbol, "node:npnBuffer") \
|
||||
V(processed_private_symbol, "node:processed") \
|
||||
V(selected_npn_buffer_private_symbol, "node:selectedNpnBuffer") \
|
||||
|
||||
// Strings are per-isolate primitives but Environment proxies them
|
||||
// for the sake of convenience. Strings should be ASCII-only.
|
||||
#define PER_ISOLATE_STRING_PROPERTIES(V) \
|
||||
V(address_string, "address") \
|
||||
V(args_string, "args") \
|
||||
V(async, "async") \
|
||||
V(buffer_string, "buffer") \
|
||||
V(bytes_string, "bytes") \
|
||||
V(bytes_parsed_string, "bytesParsed") \
|
||||
V(bytes_read_string, "bytesRead") \
|
||||
V(cached_data_string, "cachedData") \
|
||||
V(cached_data_produced_string, "cachedDataProduced") \
|
||||
V(cached_data_rejected_string, "cachedDataRejected") \
|
||||
V(callback_string, "callback") \
|
||||
V(change_string, "change") \
|
||||
V(channel_string, "channel") \
|
||||
V(oncertcb_string, "oncertcb") \
|
||||
V(onclose_string, "_onclose") \
|
||||
V(code_string, "code") \
|
||||
V(configurable_string, "configurable") \
|
||||
V(cwd_string, "cwd") \
|
||||
V(dest_string, "dest") \
|
||||
V(detached_string, "detached") \
|
||||
V(disposed_string, "_disposed") \
|
||||
V(dns_a_string, "A") \
|
||||
V(dns_aaaa_string, "AAAA") \
|
||||
V(dns_cname_string, "CNAME") \
|
||||
V(dns_mx_string, "MX") \
|
||||
V(dns_naptr_string, "NAPTR") \
|
||||
V(dns_ns_string, "NS") \
|
||||
V(dns_ptr_string, "PTR") \
|
||||
V(dns_soa_string, "SOA") \
|
||||
V(dns_srv_string, "SRV") \
|
||||
V(dns_txt_string, "TXT") \
|
||||
V(domain_string, "domain") \
|
||||
V(emitting_top_level_domain_error_string, "_emittingTopLevelDomainError") \
|
||||
V(exchange_string, "exchange") \
|
||||
V(enumerable_string, "enumerable") \
|
||||
V(idle_string, "idle") \
|
||||
V(irq_string, "irq") \
|
||||
V(encoding_string, "encoding") \
|
||||
V(enter_string, "enter") \
|
||||
V(entries_string, "entries") \
|
||||
V(env_pairs_string, "envPairs") \
|
||||
V(errno_string, "errno") \
|
||||
V(error_string, "error") \
|
||||
V(events_string, "_events") \
|
||||
V(exiting_string, "_exiting") \
|
||||
V(exit_code_string, "exitCode") \
|
||||
V(exit_string, "exit") \
|
||||
V(expire_string, "expire") \
|
||||
V(exponent_string, "exponent") \
|
||||
V(exports_string, "exports") \
|
||||
V(ext_key_usage_string, "ext_key_usage") \
|
||||
V(external_stream_string, "_externalStream") \
|
||||
V(family_string, "family") \
|
||||
V(fatal_exception_string, "_fatalException") \
|
||||
V(fd_string, "fd") \
|
||||
V(file_string, "file") \
|
||||
V(fingerprint_string, "fingerprint") \
|
||||
V(flags_string, "flags") \
|
||||
V(get_string, "get") \
|
||||
V(get_data_clone_error_string, "_getDataCloneError") \
|
||||
V(get_shared_array_buffer_id_string, "_getSharedArrayBufferId") \
|
||||
V(gid_string, "gid") \
|
||||
V(handle_string, "handle") \
|
||||
V(homedir_string, "homedir") \
|
||||
V(hostmaster_string, "hostmaster") \
|
||||
V(ignore_string, "ignore") \
|
||||
V(immediate_callback_string, "_immediateCallback") \
|
||||
V(infoaccess_string, "infoAccess") \
|
||||
V(inherit_string, "inherit") \
|
||||
V(input_string, "input") \
|
||||
V(internal_string, "internal") \
|
||||
V(ipv4_string, "IPv4") \
|
||||
V(ipv6_string, "IPv6") \
|
||||
V(isalive_string, "isAlive") \
|
||||
V(isclosing_string, "isClosing") \
|
||||
V(issuer_string, "issuer") \
|
||||
V(issuercert_string, "issuerCertificate") \
|
||||
V(kill_signal_string, "killSignal") \
|
||||
V(length_string, "length") \
|
||||
V(mac_string, "mac") \
|
||||
V(max_buffer_string, "maxBuffer") \
|
||||
V(message_string, "message") \
|
||||
V(minttl_string, "minttl") \
|
||||
V(model_string, "model") \
|
||||
V(modulus_string, "modulus") \
|
||||
V(name_string, "name") \
|
||||
V(netmask_string, "netmask") \
|
||||
V(nice_string, "nice") \
|
||||
V(nsname_string, "nsname") \
|
||||
V(ocsp_request_string, "OCSPRequest") \
|
||||
V(onchange_string, "onchange") \
|
||||
V(onclienthello_string, "onclienthello") \
|
||||
V(oncomplete_string, "oncomplete") \
|
||||
V(onconnection_string, "onconnection") \
|
||||
V(ondone_string, "ondone") \
|
||||
V(onerror_string, "onerror") \
|
||||
V(onexit_string, "onexit") \
|
||||
V(onhandshakedone_string, "onhandshakedone") \
|
||||
V(onhandshakestart_string, "onhandshakestart") \
|
||||
V(onmessage_string, "onmessage") \
|
||||
V(onnewsession_string, "onnewsession") \
|
||||
V(onnewsessiondone_string, "onnewsessiondone") \
|
||||
V(onocspresponse_string, "onocspresponse") \
|
||||
V(onread_string, "onread") \
|
||||
V(onreadstart_string, "onreadstart") \
|
||||
V(onreadstop_string, "onreadstop") \
|
||||
V(onselect_string, "onselect") \
|
||||
V(onshutdown_string, "onshutdown") \
|
||||
V(onsignal_string, "onsignal") \
|
||||
V(onstop_string, "onstop") \
|
||||
V(onwrite_string, "onwrite") \
|
||||
V(output_string, "output") \
|
||||
V(order_string, "order") \
|
||||
V(owner_string, "owner") \
|
||||
V(parse_error_string, "Parse Error") \
|
||||
V(path_string, "path") \
|
||||
V(pbkdf2_error_string, "PBKDF2 Error") \
|
||||
V(pid_string, "pid") \
|
||||
V(pipe_string, "pipe") \
|
||||
V(port_string, "port") \
|
||||
V(preference_string, "preference") \
|
||||
V(priority_string, "priority") \
|
||||
V(produce_cached_data_string, "produceCachedData") \
|
||||
V(raw_string, "raw") \
|
||||
V(read_host_object_string, "_readHostObject") \
|
||||
V(readable_string, "readable") \
|
||||
V(received_shutdown_string, "receivedShutdown") \
|
||||
V(refresh_string, "refresh") \
|
||||
V(regexp_string, "regexp") \
|
||||
V(rename_string, "rename") \
|
||||
V(replacement_string, "replacement") \
|
||||
V(retry_string, "retry") \
|
||||
V(serial_string, "serial") \
|
||||
V(scopeid_string, "scopeid") \
|
||||
V(sent_shutdown_string, "sentShutdown") \
|
||||
V(serial_number_string, "serialNumber") \
|
||||
V(service_string, "service") \
|
||||
V(servername_string, "servername") \
|
||||
V(session_id_string, "sessionId") \
|
||||
V(set_string, "set") \
|
||||
V(shell_string, "shell") \
|
||||
V(signal_string, "signal") \
|
||||
V(size_string, "size") \
|
||||
V(sni_context_err_string, "Invalid SNI context") \
|
||||
V(sni_context_string, "sni_context") \
|
||||
V(speed_string, "speed") \
|
||||
V(stack_string, "stack") \
|
||||
V(status_string, "status") \
|
||||
V(stdio_string, "stdio") \
|
||||
V(subject_string, "subject") \
|
||||
V(subjectaltname_string, "subjectaltname") \
|
||||
V(sys_string, "sys") \
|
||||
V(syscall_string, "syscall") \
|
||||
V(tick_callback_string, "_tickCallback") \
|
||||
V(tick_domain_cb_string, "_tickDomainCallback") \
|
||||
V(ticketkeycallback_string, "onticketkeycallback") \
|
||||
V(timeout_string, "timeout") \
|
||||
V(times_string, "times") \
|
||||
V(tls_ticket_string, "tlsTicket") \
|
||||
V(ttl_string, "ttl") \
|
||||
V(type_string, "type") \
|
||||
V(uid_string, "uid") \
|
||||
V(unknown_string, "<unknown>") \
|
||||
V(user_string, "user") \
|
||||
V(username_string, "username") \
|
||||
V(valid_from_string, "valid_from") \
|
||||
V(valid_to_string, "valid_to") \
|
||||
V(value_string, "value") \
|
||||
V(verify_error_string, "verifyError") \
|
||||
V(version_string, "version") \
|
||||
V(weight_string, "weight") \
|
||||
V(windows_verbatim_arguments_string, "windowsVerbatimArguments") \
|
||||
V(wrap_string, "wrap") \
|
||||
V(writable_string, "writable") \
|
||||
V(write_host_object_string, "_writeHostObject") \
|
||||
V(write_queue_size_string, "writeQueueSize") \
|
||||
V(x_forwarded_string, "x-forwarded-for") \
|
||||
V(zero_return_string, "ZERO_RETURN") \
|
||||
|
||||
|
||||
|
||||
#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \
|
||||
V(as_external, v8::External) \
|
||||
V(async_hooks_destroy_function, v8::Function) \
|
||||
V(async_hooks_init_function, v8::Function) \
|
||||
V(async_hooks_before_function, v8::Function) \
|
||||
V(async_hooks_after_function, v8::Function) \
|
||||
V(binding_cache_object, v8::Object) \
|
||||
V(buffer_constructor_function, v8::Function) \
|
||||
V(buffer_prototype_object, v8::Object) \
|
||||
V(context, v8::Context) \
|
||||
V(domain_array, v8::Array) \
|
||||
V(domains_stack_array, v8::Array) \
|
||||
V(jsstream_constructor_template, v8::FunctionTemplate) \
|
||||
V(module_load_list_array, v8::Array) \
|
||||
V(pbkdf2_constructor_template, v8::ObjectTemplate) \
|
||||
V(pipe_constructor_template, v8::FunctionTemplate) \
|
||||
V(process_object, v8::Object) \
|
||||
V(promise_reject_function, v8::Function) \
|
||||
V(promise_wrap_template, v8::ObjectTemplate) \
|
||||
V(push_values_to_array_function, v8::Function) \
|
||||
V(randombytes_constructor_template, v8::ObjectTemplate) \
|
||||
V(script_context_constructor_template, v8::FunctionTemplate) \
|
||||
V(script_data_constructor_function, v8::Function) \
|
||||
V(secure_context_constructor_template, v8::FunctionTemplate) \
|
||||
V(tcp_constructor_template, v8::FunctionTemplate) \
|
||||
V(tick_callback_function, v8::Function) \
|
||||
V(tls_wrap_constructor_function, v8::Function) \
|
||||
V(tls_wrap_constructor_template, v8::FunctionTemplate) \
|
||||
V(tty_constructor_template, v8::FunctionTemplate) \
|
||||
V(udp_constructor_function, v8::Function) \
|
||||
V(url_constructor_function, v8::Function) \
|
||||
V(write_wrap_constructor_function, v8::Function) \
|
||||
|
||||
|
||||
class IsolateData {
|
||||
public:
|
||||
inline IsolateData(v8::Isolate* isolate, uv_loop_t* event_loop,
|
||||
uint32_t* zero_fill_field = nullptr);
|
||||
inline uv_loop_t* event_loop() const;
|
||||
inline uint32_t* zero_fill_field() const;
|
||||
|
||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
|
||||
#define V(TypeName, PropertyName) \
|
||||
inline v8::Local<TypeName> PropertyName(v8::Isolate* isolate) const;
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
|
||||
PER_ISOLATE_STRING_PROPERTIES(VS)
|
||||
#undef V
|
||||
#undef VS
|
||||
#undef VP
|
||||
|
||||
private:
|
||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
|
||||
#define V(TypeName, PropertyName) \
|
||||
v8::Eternal<TypeName> PropertyName ## _;
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
|
||||
PER_ISOLATE_STRING_PROPERTIES(VS)
|
||||
#undef V
|
||||
#undef VS
|
||||
#undef VP
|
||||
|
||||
uv_loop_t* const event_loop_;
|
||||
uint32_t* const zero_fill_field_;
|
||||
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(IsolateData);
|
||||
};
|
||||
|
||||
class Environment
|
||||
{
|
||||
public:
|
||||
|
||||
static const int kContextEmbedderDataIndex = NODE_CONTEXT_EMBEDDER_DATA_INDEX;
|
||||
|
||||
static inline Environment* GetCurrent(v8::Isolate* isolate);
|
||||
static inline Environment* GetCurrent(v8::Local<v8::Context> context);
|
||||
static inline Environment* GetCurrent(const v8::FunctionCallbackInfo<v8::Value>& info);
|
||||
|
||||
template <typename T>
|
||||
static inline Environment* GetCurrent(const v8::PropertyCallbackInfo<T>& info);
|
||||
|
||||
inline Environment(IsolateData* isolate_data, v8::Local<v8::Context> context);
|
||||
inline ~Environment();
|
||||
|
||||
void Start(int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv,
|
||||
bool start_profiler_idle_notifier);
|
||||
void AssignToContext(v8::Local<v8::Context> context);
|
||||
void CleanupHandles();
|
||||
|
||||
void StartProfilerIdleNotifier();
|
||||
void StopProfilerIdleNotifier();
|
||||
|
||||
inline v8::Isolate* isolate() const
|
||||
{
|
||||
return isolate_;
|
||||
}
|
||||
|
||||
inline IsolateData* isolate_data() const
|
||||
{
|
||||
return isolate_data_;
|
||||
}
|
||||
|
||||
inline uv_loop_t* event_loop() const
|
||||
{
|
||||
return isolate_data()->event_loop();
|
||||
}
|
||||
|
||||
inline inspector::Agent* inspector_agent() {
|
||||
return &inspector_agent_;
|
||||
}
|
||||
|
||||
// Strings and private symbols are shared across shared contexts
|
||||
// The getters simply proxy to the per-isolate primitive.
|
||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
|
||||
#define V(TypeName, PropertyName) \
|
||||
inline v8::Local<TypeName> PropertyName() const;
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
|
||||
PER_ISOLATE_STRING_PROPERTIES(VS)
|
||||
#undef V
|
||||
#undef VS
|
||||
#undef VP
|
||||
|
||||
|
||||
#define V(PropertyName, TypeName) \
|
||||
inline v8::Local<TypeName> PropertyName() const; \
|
||||
inline void set_ ## PropertyName(v8::Local<TypeName> value);
|
||||
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
||||
#undef V
|
||||
|
||||
inline void ThrowError(const char* errmsg);
|
||||
inline void ThrowTypeError(const char* errmsg);
|
||||
inline void ThrowRangeError(const char* errmsg);
|
||||
inline void ThrowErrnoException(int errorno,
|
||||
const char* syscall = nullptr,
|
||||
const char* message = nullptr,
|
||||
const char* path = nullptr);
|
||||
inline void ThrowUVException(int errorno,
|
||||
const char* syscall = nullptr,
|
||||
const char* message = nullptr,
|
||||
const char* path = nullptr,
|
||||
const char* dest = nullptr);
|
||||
|
||||
inline v8::Local<v8::FunctionTemplate>
|
||||
NewFunctionTemplate(v8::FunctionCallback callback,
|
||||
v8::Local<v8::Signature> signature =
|
||||
v8::Local<v8::Signature>());
|
||||
|
||||
// Convenience methods for NewFunctionTemplate().
|
||||
inline void SetMethod(v8::Local<v8::Object> that,
|
||||
const char* name,
|
||||
v8::FunctionCallback callback);
|
||||
|
||||
class AsyncCallbackScope {
|
||||
public:
|
||||
AsyncCallbackScope() = delete;
|
||||
explicit AsyncCallbackScope(Environment* env);
|
||||
~AsyncCallbackScope();
|
||||
inline bool in_makecallback();
|
||||
|
||||
private:
|
||||
Environment* env_;
|
||||
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(AsyncCallbackScope);
|
||||
};
|
||||
|
||||
private:
|
||||
inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
|
||||
const char* errmsg);
|
||||
|
||||
v8::Isolate* const isolate_;
|
||||
IsolateData* const isolate_data_;
|
||||
|
||||
#define V(PropertyName, TypeName) \
|
||||
v8::Persistent<TypeName> PropertyName ## _;
|
||||
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
||||
#undef V
|
||||
inspector::Agent inspector_agent_;
|
||||
size_t makecallback_cntr_;
|
||||
};
|
||||
|
||||
inline IsolateData::IsolateData(v8::Isolate* isolate, uv_loop_t* event_loop,
|
||||
uint32_t* zero_fill_field) :
|
||||
|
||||
// Create string and private symbol properties as internalized one byte strings.
|
||||
//
|
||||
// Internalized because it makes property lookups a little faster and because
|
||||
// the string is created in the old space straight away. It's going to end up
|
||||
// in the old space sooner or later anyway but now it doesn't go through
|
||||
// v8::Eternal's new space handling first.
|
||||
//
|
||||
// One byte because our strings are ASCII and we can safely skip V8's UTF-8
|
||||
// decoding step. It's a one-time cost, but why pay it when you don't have to?
|
||||
#define V(PropertyName, StringValue) \
|
||||
PropertyName ## _( \
|
||||
isolate, \
|
||||
v8::Private::New( \
|
||||
isolate, \
|
||||
v8::String::NewFromOneByte( \
|
||||
isolate, \
|
||||
reinterpret_cast<const uint8_t*>(StringValue), \
|
||||
v8::NewStringType::kInternalized, \
|
||||
sizeof(StringValue) - 1).ToLocalChecked())),
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
|
||||
#undef V
|
||||
#define V(PropertyName, StringValue) \
|
||||
PropertyName ## _( \
|
||||
isolate, \
|
||||
v8::String::NewFromOneByte( \
|
||||
isolate, \
|
||||
reinterpret_cast<const uint8_t*>(StringValue), \
|
||||
v8::NewStringType::kInternalized, \
|
||||
sizeof(StringValue) - 1).ToLocalChecked()),
|
||||
PER_ISOLATE_STRING_PROPERTIES(V)
|
||||
#undef V
|
||||
event_loop_(event_loop), zero_fill_field_(zero_fill_field) {}
|
||||
|
||||
inline uv_loop_t* IsolateData::event_loop() const {
|
||||
return event_loop_;
|
||||
}
|
||||
|
||||
inline uint32_t* IsolateData::zero_fill_field() const {
|
||||
return zero_fill_field_;
|
||||
}
|
||||
|
||||
|
||||
inline Environment* Environment::GetCurrent(v8::Isolate* isolate) {
|
||||
return GetCurrent(isolate->GetCurrentContext());
|
||||
}
|
||||
|
||||
inline Environment* Environment::GetCurrent(v8::Local<v8::Context> context) {
|
||||
return static_cast<Environment*>(
|
||||
context->GetAlignedPointerFromEmbedderData(kContextEmbedderDataIndex));
|
||||
}
|
||||
|
||||
inline Environment* Environment::GetCurrent(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||
CHECK(info.Data()->IsExternal());
|
||||
return static_cast<Environment*>(info.Data().As<v8::External>()->Value());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Environment* Environment::GetCurrent(
|
||||
const v8::PropertyCallbackInfo<T>& info) {
|
||||
CHECK(info.Data()->IsExternal());
|
||||
// XXX(bnoordhuis) Work around a g++ 4.9.2 template type inferrer bug
|
||||
// when the expression is written as info.Data().As<v8::External>().
|
||||
v8::Local<v8::Value> data = info.Data();
|
||||
return static_cast<Environment*>(data.As<v8::External>()->Value());
|
||||
}
|
||||
|
||||
inline Environment::Environment(IsolateData* isolate_data, v8::Local<v8::Context> context)
|
||||
: isolate_(context->GetIsolate()),
|
||||
isolate_data_(isolate_data),
|
||||
makecallback_cntr_(0),
|
||||
inspector_agent_(this),
|
||||
context_(context->GetIsolate(), context) {
|
||||
// We'll be creating new objects so make sure we've entered the context.
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
v8::Context::Scope context_scope(context);
|
||||
set_as_external(v8::External::New(isolate(), this));
|
||||
set_binding_cache_object(v8::Object::New(isolate()));
|
||||
set_module_load_list_array(v8::Array::New(isolate()));
|
||||
|
||||
AssignToContext(context);
|
||||
|
||||
//cjh destroy_ids_list_.reserve(512);
|
||||
}
|
||||
|
||||
inline Environment::~Environment() {
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
|
||||
context()->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex,
|
||||
nullptr);
|
||||
#define V(PropertyName, TypeName) PropertyName ## _.Reset();
|
||||
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
||||
#undef V
|
||||
|
||||
// delete[] heap_statistics_buffer_;
|
||||
// delete[] heap_space_statistics_buffer_;
|
||||
// delete[] http_parser_buffer_;
|
||||
}
|
||||
|
||||
inline void Environment::SetMethod(v8::Local<v8::Object> that,
|
||||
const char* name,
|
||||
v8::FunctionCallback callback) {
|
||||
v8::Local<v8::Function> function =
|
||||
NewFunctionTemplate(callback)->GetFunction(context()).ToLocalChecked();
|
||||
// kInternalized strings are created in the old space.
|
||||
const v8::NewStringType type = v8::NewStringType::kInternalized;
|
||||
v8::Local<v8::String> name_string =
|
||||
v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked();
|
||||
that->Set(context(), name_string, function);
|
||||
function->SetName(name_string); // NODE_SET_METHOD() compatibility.
|
||||
}
|
||||
|
||||
inline void Environment::ThrowError(const char* errmsg) {
|
||||
ThrowError(v8::Exception::Error, errmsg);
|
||||
}
|
||||
|
||||
inline void Environment::ThrowTypeError(const char* errmsg) {
|
||||
ThrowError(v8::Exception::TypeError, errmsg);
|
||||
}
|
||||
|
||||
inline void Environment::ThrowRangeError(const char* errmsg) {
|
||||
ThrowError(v8::Exception::RangeError, errmsg);
|
||||
}
|
||||
|
||||
inline void Environment::ThrowError(
|
||||
v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
|
||||
const char* errmsg) {
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
isolate()->ThrowException(fun(OneByteString(isolate(), errmsg)));
|
||||
}
|
||||
|
||||
inline void Environment::ThrowErrnoException(int errorno,
|
||||
const char* syscall,
|
||||
const char* message,
|
||||
const char* path) {
|
||||
isolate()->ThrowException(
|
||||
ErrnoException(isolate(), errorno, syscall, message, path));
|
||||
}
|
||||
|
||||
inline void Environment::ThrowUVException(int errorno,
|
||||
const char* syscall,
|
||||
const char* message,
|
||||
const char* path,
|
||||
const char* dest) {
|
||||
isolate()->ThrowException(
|
||||
UVException(isolate(), errorno, syscall, message, path, dest));
|
||||
}
|
||||
|
||||
inline v8::Local<v8::FunctionTemplate>
|
||||
Environment::NewFunctionTemplate(v8::FunctionCallback callback,
|
||||
v8::Local<v8::Signature> signature) {
|
||||
v8::Local<v8::External> external = as_external();
|
||||
return v8::FunctionTemplate::New(isolate(), callback, external, signature);
|
||||
}
|
||||
|
||||
|
||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
|
||||
#define V(TypeName, PropertyName) \
|
||||
inline \
|
||||
v8::Local<TypeName> IsolateData::PropertyName(v8::Isolate* isolate) const { \
|
||||
/* Strings are immutable so casting away const-ness here is okay. */ \
|
||||
return const_cast<IsolateData*>(this)->PropertyName ## _.Get(isolate); \
|
||||
}
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
|
||||
PER_ISOLATE_STRING_PROPERTIES(VS)
|
||||
#undef V
|
||||
#undef VS
|
||||
#undef VP
|
||||
|
||||
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
|
||||
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
|
||||
#define V(TypeName, PropertyName) \
|
||||
inline v8::Local<TypeName> Environment::PropertyName() const { \
|
||||
return isolate_data()->PropertyName(isolate()); \
|
||||
}
|
||||
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
|
||||
PER_ISOLATE_STRING_PROPERTIES(VS)
|
||||
#undef V
|
||||
#undef VS
|
||||
#undef VP
|
||||
|
||||
#define V(PropertyName, TypeName) \
|
||||
inline v8::Local<TypeName> Environment::PropertyName() const { \
|
||||
return StrongPersistentToLocal(PropertyName ## _); \
|
||||
} \
|
||||
inline void Environment::set_ ## PropertyName(v8::Local<TypeName> value) { \
|
||||
PropertyName ## _.Reset(isolate(), value); \
|
||||
}
|
||||
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
|
||||
#undef V
|
||||
|
||||
inline Environment::AsyncCallbackScope::AsyncCallbackScope(Environment* env)
|
||||
: env_(env) {
|
||||
env_->makecallback_cntr_++;
|
||||
}
|
||||
|
||||
inline Environment::AsyncCallbackScope::~AsyncCallbackScope() {
|
||||
env_->makecallback_cntr_--;
|
||||
}
|
||||
|
||||
inline bool Environment::AsyncCallbackScope::in_makecallback() {
|
||||
return env_->makecallback_cntr_ > 1;
|
||||
}
|
||||
|
||||
|
||||
} // namespace node {
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,369 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef http_parser_h
|
||||
#define http_parser_h
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Also update SONAME in the Makefile whenever you change these. */
|
||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||
#define HTTP_PARSER_VERSION_MINOR 7
|
||||
#define HTTP_PARSER_VERSION_PATCH 0
|
||||
|
||||
#include <sys/types.h>
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && \
|
||||
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
|
||||
#include <BaseTsd.h>
|
||||
#include <stddef.h>
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
||||
* faster
|
||||
*/
|
||||
#ifndef HTTP_PARSER_STRICT
|
||||
# define HTTP_PARSER_STRICT 1
|
||||
#endif
|
||||
|
||||
/* Maximium header size allowed. If the macro is not defined
|
||||
* before including this header then the default is used. To
|
||||
* change the maximum header size, define the macro in the build
|
||||
* environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
|
||||
* the effective limit on the size of the header, define the macro
|
||||
* to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
|
||||
*/
|
||||
#ifndef HTTP_MAX_HEADER_SIZE
|
||||
# define HTTP_MAX_HEADER_SIZE (80*1024)
|
||||
#endif
|
||||
|
||||
typedef struct http_parser http_parser;
|
||||
typedef struct http_parser_settings http_parser_settings;
|
||||
|
||||
|
||||
/* Callbacks should return non-zero to indicate an error. The parser will
|
||||
* then halt execution.
|
||||
*
|
||||
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
|
||||
* returning '1' from on_headers_complete will tell the parser that it
|
||||
* should not expect a body. This is used when receiving a response to a
|
||||
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
|
||||
* chunked' headers that indicate the presence of a body.
|
||||
*
|
||||
* Returning `2` from on_headers_complete will tell parser that it should not
|
||||
* expect neither a body nor any futher responses on this connection. This is
|
||||
* useful for handling responses to a CONNECT request which may not contain
|
||||
* `Upgrade` or `Connection: upgrade` headers.
|
||||
*
|
||||
* http_data_cb does not return data chunks. It will be called arbitrarily
|
||||
* many times for each string. E.G. you might get 10 callbacks for "on_url"
|
||||
* each providing just a few characters more data.
|
||||
*/
|
||||
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
||||
typedef int (*http_cb) (http_parser*);
|
||||
|
||||
|
||||
/* Request Methods */
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
/* pathological */ \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
/* WebDAV */ \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
XX(16, BIND, BIND) \
|
||||
XX(17, REBIND, REBIND) \
|
||||
XX(18, UNBIND, UNBIND) \
|
||||
XX(19, ACL, ACL) \
|
||||
/* subversion */ \
|
||||
XX(20, REPORT, REPORT) \
|
||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||
XX(22, CHECKOUT, CHECKOUT) \
|
||||
XX(23, MERGE, MERGE) \
|
||||
/* upnp */ \
|
||||
XX(24, MSEARCH, M-SEARCH) \
|
||||
XX(25, NOTIFY, NOTIFY) \
|
||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
/* RFC-5789 */ \
|
||||
XX(28, PATCH, PATCH) \
|
||||
XX(29, PURGE, PURGE) \
|
||||
/* CalDAV */ \
|
||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||
/* RFC-2068, section 19.6.1.2 */ \
|
||||
XX(31, LINK, LINK) \
|
||||
XX(32, UNLINK, UNLINK) \
|
||||
|
||||
enum http_method
|
||||
{
|
||||
#define XX(num, name, string) HTTP_##name = num,
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
||||
|
||||
|
||||
/* Flag values for http_parser.flags field */
|
||||
enum flags
|
||||
{ F_CHUNKED = 1 << 0
|
||||
, F_CONNECTION_KEEP_ALIVE = 1 << 1
|
||||
, F_CONNECTION_CLOSE = 1 << 2
|
||||
, F_CONNECTION_UPGRADE = 1 << 3
|
||||
, F_TRAILING = 1 << 4
|
||||
, F_UPGRADE = 1 << 5
|
||||
, F_SKIPBODY = 1 << 6
|
||||
, F_CONTENTLENGTH = 1 << 7
|
||||
};
|
||||
|
||||
|
||||
/* Map for errno-related constants
|
||||
*
|
||||
* The provided argument should be a macro that takes 2 arguments.
|
||||
*/
|
||||
#define HTTP_ERRNO_MAP(XX) \
|
||||
/* No error */ \
|
||||
XX(OK, "success") \
|
||||
\
|
||||
/* Callback-related errors */ \
|
||||
XX(CB_message_begin, "the on_message_begin callback failed") \
|
||||
XX(CB_url, "the on_url callback failed") \
|
||||
XX(CB_header_field, "the on_header_field callback failed") \
|
||||
XX(CB_header_value, "the on_header_value callback failed") \
|
||||
XX(CB_headers_complete, "the on_headers_complete callback failed") \
|
||||
XX(CB_body, "the on_body callback failed") \
|
||||
XX(CB_message_complete, "the on_message_complete callback failed") \
|
||||
XX(CB_status, "the on_status callback failed") \
|
||||
XX(CB_chunk_header, "the on_chunk_header callback failed") \
|
||||
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
|
||||
\
|
||||
/* Parsing-related errors */ \
|
||||
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
|
||||
XX(HEADER_OVERFLOW, \
|
||||
"too many header bytes seen; overflow detected") \
|
||||
XX(CLOSED_CONNECTION, \
|
||||
"data received after completed connection: close message") \
|
||||
XX(INVALID_VERSION, "invalid HTTP version") \
|
||||
XX(INVALID_STATUS, "invalid HTTP status code") \
|
||||
XX(INVALID_METHOD, "invalid HTTP method") \
|
||||
XX(INVALID_URL, "invalid URL") \
|
||||
XX(INVALID_HOST, "invalid host") \
|
||||
XX(INVALID_PORT, "invalid port") \
|
||||
XX(INVALID_PATH, "invalid path") \
|
||||
XX(INVALID_QUERY_STRING, "invalid query string") \
|
||||
XX(INVALID_FRAGMENT, "invalid fragment") \
|
||||
XX(LF_EXPECTED, "LF character expected") \
|
||||
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
|
||||
XX(INVALID_CONTENT_LENGTH, \
|
||||
"invalid character in content-length header") \
|
||||
XX(UNEXPECTED_CONTENT_LENGTH, \
|
||||
"unexpected content-length header") \
|
||||
XX(INVALID_CHUNK_SIZE, \
|
||||
"invalid character in chunk size header") \
|
||||
XX(INVALID_CONSTANT, "invalid constant string") \
|
||||
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
|
||||
XX(STRICT, "strict mode assertion failed") \
|
||||
XX(PAUSED, "parser is paused") \
|
||||
XX(UNKNOWN, "an unknown error occurred")
|
||||
|
||||
|
||||
/* Define HPE_* values for each errno value above */
|
||||
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
|
||||
enum http_errno {
|
||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
||||
};
|
||||
#undef HTTP_ERRNO_GEN
|
||||
|
||||
|
||||
/* Get an http_errno value from an http_parser */
|
||||
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
|
||||
|
||||
|
||||
struct http_parser {
|
||||
/** PRIVATE **/
|
||||
unsigned int type : 2; /* enum http_parser_type */
|
||||
unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
|
||||
unsigned int state : 7; /* enum state from http_parser.c */
|
||||
unsigned int header_state : 7; /* enum header_state from http_parser.c */
|
||||
unsigned int index : 7; /* index into current matcher */
|
||||
unsigned int lenient_http_headers : 1;
|
||||
|
||||
uint32_t nread; /* # bytes read in various scenarios */
|
||||
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
|
||||
|
||||
/** READ-ONLY **/
|
||||
unsigned short http_major;
|
||||
unsigned short http_minor;
|
||||
unsigned int status_code : 16; /* responses only */
|
||||
unsigned int method : 8; /* requests only */
|
||||
unsigned int http_errno : 7;
|
||||
|
||||
/* 1 = Upgrade header was present and the parser has exited because of that.
|
||||
* 0 = No upgrade header present.
|
||||
* Should be checked when http_parser_execute() returns in addition to
|
||||
* error checking.
|
||||
*/
|
||||
unsigned int upgrade : 1;
|
||||
|
||||
/** PUBLIC **/
|
||||
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
||||
};
|
||||
|
||||
|
||||
struct http_parser_settings {
|
||||
http_cb on_message_begin;
|
||||
http_data_cb on_url;
|
||||
http_data_cb on_status;
|
||||
http_data_cb on_header_field;
|
||||
http_data_cb on_header_value;
|
||||
http_cb on_headers_complete;
|
||||
http_data_cb on_body;
|
||||
http_cb on_message_complete;
|
||||
/* When on_chunk_header is called, the current chunk length is stored
|
||||
* in parser->content_length.
|
||||
*/
|
||||
http_cb on_chunk_header;
|
||||
http_cb on_chunk_complete;
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_url_fields
|
||||
{ UF_SCHEMA = 0
|
||||
, UF_HOST = 1
|
||||
, UF_PORT = 2
|
||||
, UF_PATH = 3
|
||||
, UF_QUERY = 4
|
||||
, UF_FRAGMENT = 5
|
||||
, UF_USERINFO = 6
|
||||
, UF_MAX = 7
|
||||
};
|
||||
|
||||
|
||||
/* Result structure for http_parser_parse_url().
|
||||
*
|
||||
* Callers should index into field_data[] with UF_* values iff field_set
|
||||
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
|
||||
* because we probably have padding left over), we convert any port to
|
||||
* a uint16_t.
|
||||
*/
|
||||
struct http_parser_url {
|
||||
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
|
||||
uint16_t port; /* Converted UF_PORT string */
|
||||
|
||||
struct {
|
||||
uint16_t off; /* Offset into buffer in which field starts */
|
||||
uint16_t len; /* Length of run in buffer */
|
||||
} field_data[UF_MAX];
|
||||
};
|
||||
|
||||
|
||||
/* Returns the library version. Bits 16-23 contain the major version number,
|
||||
* bits 8-15 the minor version number and bits 0-7 the patch level.
|
||||
* Usage example:
|
||||
*
|
||||
* unsigned long version = http_parser_version();
|
||||
* unsigned major = (version >> 16) & 255;
|
||||
* unsigned minor = (version >> 8) & 255;
|
||||
* unsigned patch = version & 255;
|
||||
* printf("http_parser v%u.%u.%u\n", major, minor, patch);
|
||||
*/
|
||||
unsigned long http_parser_version(void);
|
||||
|
||||
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
||||
|
||||
|
||||
/* Initialize http_parser_settings members to 0
|
||||
*/
|
||||
void http_parser_settings_init(http_parser_settings *settings);
|
||||
|
||||
|
||||
/* Executes the parser. Returns number of parsed bytes. Sets
|
||||
* `parser->http_errno` on error. */
|
||||
size_t http_parser_execute(http_parser *parser,
|
||||
const http_parser_settings *settings,
|
||||
const char *data,
|
||||
size_t len);
|
||||
|
||||
|
||||
/* If http_should_keep_alive() in the on_headers_complete or
|
||||
* on_message_complete callback returns 0, then this should be
|
||||
* the last message on the connection.
|
||||
* If you are the server, respond with the "Connection: close" header.
|
||||
* If you are the client, close the connection.
|
||||
*/
|
||||
int http_should_keep_alive(const http_parser *parser);
|
||||
|
||||
/* Returns a string version of the HTTP method. */
|
||||
const char *http_method_str(enum http_method m);
|
||||
|
||||
/* Return a string name of the given error */
|
||||
const char *http_errno_name(enum http_errno err);
|
||||
|
||||
/* Return a string description of the given error */
|
||||
const char *http_errno_description(enum http_errno err);
|
||||
|
||||
/* Initialize all http_parser_url members to 0 */
|
||||
void http_parser_url_init(struct http_parser_url *u);
|
||||
|
||||
/* Parse a URL; return nonzero on failure */
|
||||
int http_parser_parse_url(const char *buf, size_t buflen,
|
||||
int is_connect,
|
||||
struct http_parser_url *u);
|
||||
|
||||
/* Pause or un-pause the parser; a nonzero value pauses */
|
||||
void http_parser_pause(http_parser *parser, int paused);
|
||||
|
||||
/* Checks if this is the final chunk of the body. */
|
||||
int http_body_is_final(const http_parser *parser);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,815 @@
|
||||
#include "inspector_agent.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "inspector_io.h"
|
||||
#include "env.h"
|
||||
#include "node.h"
|
||||
#include "v8-inspector.h"
|
||||
#include "v8-platform.h"
|
||||
#include "util.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include "libplatform/libplatform.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __POSIX__
|
||||
#include <unistd.h> // setuid, getuid
|
||||
#endif // __POSIX__
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
namespace {
|
||||
using v8::Context;
|
||||
using v8::External;
|
||||
using v8::Function;
|
||||
using v8::FunctionCallbackInfo;
|
||||
using v8::HandleScope;
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::Maybe;
|
||||
using v8::MaybeLocal;
|
||||
using v8::NewStringType;
|
||||
using v8::Object;
|
||||
using v8::Persistent;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
using v8_inspector::StringBuffer;
|
||||
using v8_inspector::StringView;
|
||||
using v8_inspector::V8Inspector;
|
||||
using v8_inspector::V8InspectorClient;
|
||||
|
||||
static uv_sem_t start_io_thread_semaphore;
|
||||
static uv_async_t start_io_thread_async;
|
||||
|
||||
class StartIoTask : public v8::Task {
|
||||
public:
|
||||
explicit StartIoTask(Agent* agent) : agent(agent) {}
|
||||
|
||||
void Run() override {
|
||||
agent->StartIoThread(false);
|
||||
}
|
||||
|
||||
private:
|
||||
Agent* agent;
|
||||
};
|
||||
|
||||
std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
|
||||
Local<Value> value) {
|
||||
TwoByteValue buffer(isolate, value);
|
||||
return StringBuffer::create(StringView(*buffer, buffer.length()));
|
||||
}
|
||||
|
||||
// Called on the main thread.
|
||||
void StartIoThreadAsyncCallback(uv_async_t* handle) {
|
||||
static_cast<Agent*>(handle->data)->StartIoThread(false);
|
||||
}
|
||||
|
||||
void StartIoInterrupt(Isolate* isolate, void* agent) {
|
||||
static_cast<Agent*>(agent)->StartIoThread(false);
|
||||
}
|
||||
|
||||
|
||||
#ifdef __POSIX__
|
||||
|
||||
static void StartIoThreadWakeup(int signo) {
|
||||
uv_sem_post(&start_io_thread_semaphore);
|
||||
}
|
||||
|
||||
inline void* StartIoThreadMain(void* unused) {
|
||||
for (;;) {
|
||||
uv_sem_wait(&start_io_thread_semaphore);
|
||||
Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
|
||||
if (agent != nullptr)
|
||||
agent->RequestIoThreadStart();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int StartDebugSignalHandler() {
|
||||
// Start a watchdog thread for calling v8::Debug::DebugBreak() because
|
||||
// it's not safe to call directly from the signal handler, it can
|
||||
// deadlock with the thread it interrupts.
|
||||
CHECK_EQ(0, uv_sem_init(&start_io_thread_semaphore, 0));
|
||||
pthread_attr_t attr;
|
||||
CHECK_EQ(0, pthread_attr_init(&attr));
|
||||
// Don't shrink the thread's stack on FreeBSD. Said platform decided to
|
||||
// follow the pthreads specification to the letter rather than in spirit:
|
||||
// https://lists.freebsd.org/pipermail/freebsd-current/2014-March/048885.html
|
||||
#ifndef __FreeBSD__
|
||||
CHECK_EQ(0, pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN));
|
||||
#endif // __FreeBSD__
|
||||
CHECK_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
|
||||
sigset_t sigmask;
|
||||
// Mask all signals.
|
||||
sigfillset(&sigmask);
|
||||
CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &sigmask));
|
||||
pthread_t thread;
|
||||
const int err = pthread_create(&thread, &attr,
|
||||
StartIoThreadMain, nullptr);
|
||||
// Restore original mask
|
||||
CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
|
||||
CHECK_EQ(0, pthread_attr_destroy(&attr));
|
||||
if (err != 0) {
|
||||
SE_LOGE("node[%d]: pthread_create: %s\n", getpid(), strerror(err));
|
||||
|
||||
// Leave SIGUSR1 blocked. We don't install a signal handler,
|
||||
// receiving the signal would terminate the process.
|
||||
return -err;
|
||||
}
|
||||
RegisterSignalHandler(SIGUSR1, StartIoThreadWakeup);
|
||||
// Unblock SIGUSR1. A pending SIGUSR1 signal will now be delivered.
|
||||
sigemptyset(&sigmask);
|
||||
sigaddset(&sigmask, SIGUSR1);
|
||||
CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr));
|
||||
return 0;
|
||||
}
|
||||
#endif // __POSIX__
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD WINAPI StartIoThreadProc(void* arg) {
|
||||
Agent* agent = static_cast<Agent*>(start_io_thread_async.data);
|
||||
if (agent != nullptr)
|
||||
agent->RequestIoThreadStart();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf,
|
||||
size_t buf_len) {
|
||||
return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid);
|
||||
}
|
||||
|
||||
static int StartDebugSignalHandler() {
|
||||
wchar_t mapping_name[32];
|
||||
HANDLE mapping_handle;
|
||||
DWORD pid;
|
||||
LPTHREAD_START_ROUTINE* handler;
|
||||
|
||||
pid = GetCurrentProcessId();
|
||||
|
||||
if (GetDebugSignalHandlerMappingName(pid,
|
||||
mapping_name,
|
||||
arraysize(mapping_name)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE,
|
||||
nullptr,
|
||||
PAGE_READWRITE,
|
||||
0,
|
||||
sizeof *handler,
|
||||
mapping_name);
|
||||
if (mapping_handle == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
handler = reinterpret_cast<LPTHREAD_START_ROUTINE*>(
|
||||
MapViewOfFile(mapping_handle,
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
0,
|
||||
0,
|
||||
sizeof *handler));
|
||||
if (handler == nullptr) {
|
||||
CloseHandle(mapping_handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*handler = StartIoThreadProc;
|
||||
|
||||
UnmapViewOfFile(static_cast<void*>(handler));
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
class JsBindingsSessionDelegate : public InspectorSessionDelegate {
|
||||
public:
|
||||
JsBindingsSessionDelegate(Environment* env,
|
||||
Local<Object> session,
|
||||
Local<Object> receiver,
|
||||
Local<Function> callback)
|
||||
: env_(env),
|
||||
session_(env->isolate(), session),
|
||||
receiver_(env->isolate(), receiver),
|
||||
callback_(env->isolate(), callback) {
|
||||
session_.SetWeak(this, JsBindingsSessionDelegate::Release,
|
||||
v8::WeakCallbackType::kParameter);
|
||||
}
|
||||
|
||||
~JsBindingsSessionDelegate() override {
|
||||
session_.Reset();
|
||||
receiver_.Reset();
|
||||
callback_.Reset();
|
||||
}
|
||||
|
||||
bool WaitForFrontendMessageWhilePaused() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
void SendMessageToFrontend(const v8_inspector::StringView& message) override {
|
||||
Isolate* isolate = env_->isolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
Context::Scope context_scope(env_->context());
|
||||
MaybeLocal<String> v8string =
|
||||
String::NewFromTwoByte(isolate, message.characters16(),
|
||||
NewStringType::kNormal, message.length());
|
||||
Local<Value> argument = v8string.ToLocalChecked().As<Value>();
|
||||
Local<Function> callback = callback_.Get(isolate);
|
||||
Local<Object> receiver = receiver_.Get(isolate);
|
||||
callback->Call(env_->context(), receiver, 1, &argument)
|
||||
.FromMaybe(Local<Value>());
|
||||
}
|
||||
|
||||
void Disconnect() {
|
||||
Agent* agent = env_->inspector_agent();
|
||||
if (agent->delegate() == this)
|
||||
agent->Disconnect();
|
||||
}
|
||||
|
||||
private:
|
||||
static void Release(
|
||||
const v8::WeakCallbackInfo<JsBindingsSessionDelegate>& info) {
|
||||
info.SetSecondPassCallback(ReleaseSecondPass);
|
||||
info.GetParameter()->session_.Reset();
|
||||
}
|
||||
|
||||
static void ReleaseSecondPass(
|
||||
const v8::WeakCallbackInfo<JsBindingsSessionDelegate>& info) {
|
||||
JsBindingsSessionDelegate* delegate = info.GetParameter();
|
||||
delegate->Disconnect();
|
||||
delete delegate;
|
||||
}
|
||||
|
||||
Environment* env_;
|
||||
Persistent<Object> session_;
|
||||
Persistent<Object> receiver_;
|
||||
Persistent<Function> callback_;
|
||||
};
|
||||
|
||||
void SetDelegate(Environment* env, Local<Object> inspector,
|
||||
JsBindingsSessionDelegate* delegate) {
|
||||
inspector->SetPrivate(env->context(),
|
||||
env->inspector_delegate_private_symbol(),
|
||||
v8::External::New(env->isolate(), delegate));
|
||||
}
|
||||
|
||||
Maybe<JsBindingsSessionDelegate*> GetDelegate(
|
||||
const FunctionCallbackInfo<Value>& info) {
|
||||
Environment* env = Environment::GetCurrent(info);
|
||||
Local<Value> delegate;
|
||||
MaybeLocal<Value> maybe_delegate =
|
||||
info.This()->GetPrivate(env->context(),
|
||||
env->inspector_delegate_private_symbol());
|
||||
|
||||
if (maybe_delegate.ToLocal(&delegate)) {
|
||||
CHECK(delegate->IsExternal());
|
||||
void* value = delegate.As<External>()->Value();
|
||||
if (value != nullptr) {
|
||||
return v8::Just(static_cast<JsBindingsSessionDelegate*>(value));
|
||||
}
|
||||
}
|
||||
env->ThrowError("Inspector is not connected");
|
||||
return v8::Nothing<JsBindingsSessionDelegate*>();
|
||||
}
|
||||
|
||||
void Dispatch(const FunctionCallbackInfo<Value>& info) {
|
||||
Environment* env = Environment::GetCurrent(info);
|
||||
if (!info[0]->IsString()) {
|
||||
env->ThrowError("Inspector message must be a string");
|
||||
return;
|
||||
}
|
||||
Maybe<JsBindingsSessionDelegate*> maybe_delegate = GetDelegate(info);
|
||||
if (maybe_delegate.IsNothing())
|
||||
return;
|
||||
Agent* inspector = env->inspector_agent();
|
||||
CHECK_EQ(maybe_delegate.ToChecked(), inspector->delegate());
|
||||
inspector->Dispatch(ToProtocolString(env->isolate(), info[0])->string());
|
||||
}
|
||||
|
||||
void Disconnect(const FunctionCallbackInfo<Value>& info) {
|
||||
Environment* env = Environment::GetCurrent(info);
|
||||
Maybe<JsBindingsSessionDelegate*> delegate = GetDelegate(info);
|
||||
if (delegate.IsNothing()) {
|
||||
return;
|
||||
}
|
||||
delegate.ToChecked()->Disconnect();
|
||||
SetDelegate(env, info.This(), nullptr);
|
||||
delete delegate.ToChecked();
|
||||
}
|
||||
|
||||
void ConnectJSBindingsSession(const FunctionCallbackInfo<Value>& info) {
|
||||
Environment* env = Environment::GetCurrent(info);
|
||||
if (!info[0]->IsFunction()) {
|
||||
env->ThrowError("Message callback is required");
|
||||
return;
|
||||
}
|
||||
Agent* inspector = env->inspector_agent();
|
||||
if (inspector->delegate() != nullptr) {
|
||||
env->ThrowError("Session is already attached");
|
||||
return;
|
||||
}
|
||||
Local<Object> session = Object::New(env->isolate());
|
||||
env->SetMethod(session, "dispatch", Dispatch);
|
||||
env->SetMethod(session, "disconnect", Disconnect);
|
||||
info.GetReturnValue().Set(session);
|
||||
|
||||
JsBindingsSessionDelegate* delegate =
|
||||
new JsBindingsSessionDelegate(env, session, info.Holder(),
|
||||
info[0].As<Function>());
|
||||
inspector->Connect(delegate);
|
||||
SetDelegate(env, session, delegate);
|
||||
}
|
||||
|
||||
void InspectorConsoleCall(const v8::FunctionCallbackInfo<Value>& info) {
|
||||
Isolate* isolate = info.GetIsolate();
|
||||
HandleScope handle_scope(isolate);
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
CHECK_LT(2, info.Length());
|
||||
std::vector<Local<Value>> call_args;
|
||||
for (int i = 3; i < info.Length(); ++i) {
|
||||
call_args.push_back(info[i]);
|
||||
}
|
||||
Environment* env = Environment::GetCurrent(isolate);
|
||||
if (env->inspector_agent()->enabled()) {
|
||||
Local<Value> inspector_method = info[0];
|
||||
CHECK(inspector_method->IsFunction());
|
||||
Local<Value> config_value = info[2];
|
||||
CHECK(config_value->IsObject());
|
||||
Local<Object> config_object = config_value.As<Object>();
|
||||
Local<String> in_call_key = FIXED_ONE_BYTE_STRING(isolate, "in_call");
|
||||
if (!config_object->Has(context, in_call_key).FromMaybe(false)) {
|
||||
CHECK(config_object->Set(context,
|
||||
in_call_key,
|
||||
v8::True(isolate)).FromJust());
|
||||
CHECK(!inspector_method.As<Function>()->Call(context,
|
||||
info.Holder(),
|
||||
call_args.size(),
|
||||
call_args.data()).IsEmpty());
|
||||
}
|
||||
CHECK(config_object->Delete(context, in_call_key).FromJust());
|
||||
}
|
||||
|
||||
Local<Value> node_method = info[1];
|
||||
CHECK(node_method->IsFunction());
|
||||
node_method.As<Function>()->Call(context,
|
||||
info.Holder(),
|
||||
call_args.size(),
|
||||
call_args.data()).FromMaybe(Local<Value>());
|
||||
}
|
||||
|
||||
void CallAndPauseOnStart(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
CHECK_GT(args.Length(), 1);
|
||||
CHECK(args[0]->IsFunction());
|
||||
std::vector<v8::Local<v8::Value>> call_args;
|
||||
for (int i = 2; i < args.Length(); i++) {
|
||||
call_args.push_back(args[i]);
|
||||
}
|
||||
|
||||
env->inspector_agent()->PauseOnNextJavascriptStatement("Break on start");
|
||||
v8::MaybeLocal<v8::Value> retval =
|
||||
args[0].As<v8::Function>()->Call(env->context(), args[1],
|
||||
call_args.size(), call_args.data());
|
||||
if (!retval.IsEmpty()) {
|
||||
args.GetReturnValue().Set(retval.ToLocalChecked());
|
||||
}
|
||||
}
|
||||
|
||||
// Used in NodeInspectorClient::currentTimeMS() below.
|
||||
const int NANOS_PER_MSEC = 1000000;
|
||||
const int CONTEXT_GROUP_ID = 1;
|
||||
|
||||
class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
|
||||
public:
|
||||
explicit ChannelImpl(V8Inspector* inspector,
|
||||
InspectorSessionDelegate* delegate)
|
||||
: delegate_(delegate) {
|
||||
session_ = inspector->connect(1, this, StringView());
|
||||
}
|
||||
|
||||
virtual ~ChannelImpl() {}
|
||||
|
||||
void dispatchProtocolMessage(const StringView& message) {
|
||||
session_->dispatchProtocolMessage(message);
|
||||
}
|
||||
|
||||
bool waitForFrontendMessage() {
|
||||
return delegate_->WaitForFrontendMessageWhilePaused();
|
||||
}
|
||||
|
||||
void schedulePauseOnNextStatement(const std::string& reason) {
|
||||
std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
|
||||
session_->schedulePauseOnNextStatement(buffer->string(), buffer->string());
|
||||
}
|
||||
|
||||
InspectorSessionDelegate* delegate() {
|
||||
return delegate_;
|
||||
}
|
||||
|
||||
private:
|
||||
void sendResponse(
|
||||
int callId,
|
||||
std::unique_ptr<v8_inspector::StringBuffer> message) override {
|
||||
sendMessageToFrontend(message->string());
|
||||
}
|
||||
|
||||
void sendNotification(
|
||||
std::unique_ptr<v8_inspector::StringBuffer> message) override {
|
||||
sendMessageToFrontend(message->string());
|
||||
}
|
||||
|
||||
void flushProtocolNotifications() override { }
|
||||
|
||||
void sendMessageToFrontend(const StringView& message) {
|
||||
delegate_->SendMessageToFrontend(message);
|
||||
}
|
||||
|
||||
InspectorSessionDelegate* const delegate_;
|
||||
std::unique_ptr<v8_inspector::V8InspectorSession> session_;
|
||||
};
|
||||
|
||||
class InspectorTimer {
|
||||
public:
|
||||
InspectorTimer(uv_loop_t* loop,
|
||||
double interval_s,
|
||||
V8InspectorClient::TimerCallback callback,
|
||||
void* data) : timer_(),
|
||||
callback_(callback),
|
||||
data_(data) {
|
||||
uv_timer_init(loop, &timer_);
|
||||
int64_t interval_ms = 1000 * interval_s;
|
||||
uv_timer_start(&timer_, OnTimer, interval_ms, interval_ms);
|
||||
}
|
||||
|
||||
InspectorTimer(const InspectorTimer&) = delete;
|
||||
|
||||
void Stop() {
|
||||
uv_timer_stop(&timer_);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&timer_), TimerClosedCb);
|
||||
}
|
||||
|
||||
private:
|
||||
static void OnTimer(uv_timer_t* uvtimer) {
|
||||
InspectorTimer* timer = node::ContainerOf(&InspectorTimer::timer_, uvtimer);
|
||||
timer->callback_(timer->data_);
|
||||
}
|
||||
|
||||
static void TimerClosedCb(uv_handle_t* uvtimer) {
|
||||
InspectorTimer* timer =
|
||||
node::ContainerOf(&InspectorTimer::timer_,
|
||||
reinterpret_cast<uv_timer_t*>(uvtimer));
|
||||
delete timer;
|
||||
}
|
||||
|
||||
~InspectorTimer() {}
|
||||
|
||||
uv_timer_t timer_;
|
||||
V8InspectorClient::TimerCallback callback_;
|
||||
void* data_;
|
||||
};
|
||||
|
||||
class InspectorTimerHandle {
|
||||
public:
|
||||
InspectorTimerHandle(uv_loop_t* loop, double interval_s,
|
||||
V8InspectorClient::TimerCallback callback, void* data) {
|
||||
timer_ = new InspectorTimer(loop, interval_s, callback, data);
|
||||
}
|
||||
|
||||
InspectorTimerHandle(const InspectorTimerHandle&) = delete;
|
||||
|
||||
~InspectorTimerHandle() {
|
||||
CHECK_NE(timer_, nullptr);
|
||||
timer_->Stop();
|
||||
timer_ = nullptr;
|
||||
}
|
||||
private:
|
||||
InspectorTimer* timer_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class NodeInspectorClient : public V8InspectorClient {
|
||||
public:
|
||||
NodeInspectorClient(node::Environment* env,
|
||||
v8::Platform* platform) : env_(env),
|
||||
platform_(platform),
|
||||
terminated_(false),
|
||||
running_nested_loop_(false) {
|
||||
client_ = V8Inspector::create(env->isolate(), this);
|
||||
}
|
||||
|
||||
void runMessageLoopOnPause(int context_group_id) override {
|
||||
CHECK_NE(channel_, nullptr);
|
||||
if (running_nested_loop_)
|
||||
return;
|
||||
terminated_ = false;
|
||||
running_nested_loop_ = true;
|
||||
while (!terminated_ && channel_->waitForFrontendMessage()) {
|
||||
while (v8::platform::PumpMessageLoop(platform_, env_->isolate()))
|
||||
{}
|
||||
}
|
||||
terminated_ = false;
|
||||
running_nested_loop_ = false;
|
||||
}
|
||||
|
||||
double currentTimeMS() override {
|
||||
return uv_hrtime() * 1.0 / NANOS_PER_MSEC;
|
||||
}
|
||||
|
||||
void contextCreated(Local<Context> context, const std::string& name) {
|
||||
std::unique_ptr<StringBuffer> name_buffer = Utf8ToStringView(name);
|
||||
v8_inspector::V8ContextInfo info(context, CONTEXT_GROUP_ID,
|
||||
name_buffer->string());
|
||||
client_->contextCreated(info);
|
||||
}
|
||||
|
||||
void contextDestroyed(Local<Context> context) {
|
||||
client_->contextDestroyed(context);
|
||||
}
|
||||
|
||||
void quitMessageLoopOnPause() override {
|
||||
terminated_ = true;
|
||||
}
|
||||
|
||||
void connectFrontend(InspectorSessionDelegate* delegate) {
|
||||
CHECK_EQ(channel_, nullptr);
|
||||
channel_ = std::unique_ptr<ChannelImpl>(
|
||||
new ChannelImpl(client_.get(), delegate));
|
||||
}
|
||||
|
||||
void disconnectFrontend() {
|
||||
quitMessageLoopOnPause();
|
||||
channel_.reset();
|
||||
}
|
||||
|
||||
void dispatchMessageFromFrontend(const StringView& message) {
|
||||
CHECK_NE(channel_, nullptr);
|
||||
channel_->dispatchProtocolMessage(message);
|
||||
}
|
||||
|
||||
Local<Context> ensureDefaultContextInGroup(int contextGroupId) override {
|
||||
return env_->context();
|
||||
}
|
||||
|
||||
void FatalException(Local<Value> error, Local<v8::Message> message) {
|
||||
Local<Context> context = env_->context();
|
||||
|
||||
int script_id = message->GetScriptOrigin().ScriptID()->Value();
|
||||
|
||||
Local<v8::StackTrace> stack_trace = message->GetStackTrace();
|
||||
|
||||
if (!stack_trace.IsEmpty() &&
|
||||
stack_trace->GetFrameCount() > 0 &&
|
||||
script_id == stack_trace->GetFrame(env_->isolate(), 0)->GetScriptId()) {
|
||||
script_id = 0;
|
||||
}
|
||||
|
||||
const uint8_t DETAILS[] = "Uncaught";
|
||||
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
|
||||
client_->exceptionThrown(
|
||||
context,
|
||||
StringView(DETAILS, sizeof(DETAILS) - 1),
|
||||
error,
|
||||
ToProtocolString(isolate, message->Get())->string(),
|
||||
ToProtocolString(isolate, message->GetScriptResourceName())->string(),
|
||||
message->GetLineNumber(context).FromMaybe(0),
|
||||
message->GetStartColumn(context).FromMaybe(0),
|
||||
client_->createStackTrace(stack_trace),
|
||||
script_id);
|
||||
}
|
||||
|
||||
ChannelImpl* channel() {
|
||||
return channel_.get();
|
||||
}
|
||||
|
||||
void startRepeatingTimer(double interval_s,
|
||||
TimerCallback callback,
|
||||
void* data) override {
|
||||
timers_.emplace(std::piecewise_construct, std::make_tuple(data),
|
||||
std::make_tuple(env_->event_loop(), interval_s, callback,
|
||||
data));
|
||||
}
|
||||
|
||||
void cancelTimer(void* data) override {
|
||||
timers_.erase(data);
|
||||
}
|
||||
|
||||
private:
|
||||
node::Environment* env_;
|
||||
v8::Platform* platform_;
|
||||
bool terminated_;
|
||||
bool running_nested_loop_;
|
||||
std::unique_ptr<V8Inspector> client_;
|
||||
std::unique_ptr<ChannelImpl> channel_;
|
||||
std::unordered_map<void*, InspectorTimerHandle> timers_;
|
||||
};
|
||||
|
||||
Agent::Agent(Environment* env) : parent_env_(env),
|
||||
client_(nullptr),
|
||||
platform_(nullptr),
|
||||
enabled_(false) {}
|
||||
|
||||
// Destructor needs to be defined here in implementation file as the header
|
||||
// does not have full definition of some classes.
|
||||
Agent::~Agent() {
|
||||
}
|
||||
|
||||
bool Agent::Start(v8::Platform* platform, const char* path,
|
||||
const DebugOptions& options) {
|
||||
path_ = path == nullptr ? "" : path;
|
||||
debug_options_ = options;
|
||||
client_ =
|
||||
std::unique_ptr<NodeInspectorClient>(
|
||||
new NodeInspectorClient(parent_env_, platform));
|
||||
client_->contextCreated(parent_env_->context(), "Node.js Main Context");
|
||||
platform_ = platform;
|
||||
CHECK_EQ(0, uv_async_init(uv_default_loop(),
|
||||
&start_io_thread_async,
|
||||
StartIoThreadAsyncCallback));
|
||||
start_io_thread_async.data = this;
|
||||
uv_unref(reinterpret_cast<uv_handle_t*>(&start_io_thread_async));
|
||||
|
||||
// Ignore failure, SIGUSR1 won't work, but that should not block node start.
|
||||
StartDebugSignalHandler();
|
||||
if (options.inspector_enabled()) {
|
||||
// This will return false if listen failed on the inspector port.
|
||||
return StartIoThread(options.wait_for_connect());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Agent::StartIoThread(bool wait_for_connect) {
|
||||
if (io_ != nullptr)
|
||||
return true;
|
||||
|
||||
CHECK_NE(client_, nullptr);
|
||||
|
||||
enabled_ = true;
|
||||
io_ = std::unique_ptr<InspectorIo>(
|
||||
new InspectorIo(parent_env_, platform_, path_, debug_options_,
|
||||
wait_for_connect));
|
||||
if (!io_->Start()) {
|
||||
client_.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
v8::Isolate* isolate = parent_env_->isolate();
|
||||
|
||||
// Send message to enable debug in workers
|
||||
HandleScope handle_scope(isolate);
|
||||
Local<Object> process_object = parent_env_->process_object();
|
||||
Local<Value> emit_fn =
|
||||
process_object->Get(isolate->GetCurrentContext(), FIXED_ONE_BYTE_STRING(isolate, "emit")).ToLocalChecked();
|
||||
// In case the thread started early during the startup
|
||||
if (!emit_fn->IsFunction())
|
||||
return true;
|
||||
|
||||
Local<Object> message = Object::New(isolate);
|
||||
message->Set(parent_env_->context(), FIXED_ONE_BYTE_STRING(isolate, "cmd"),
|
||||
FIXED_ONE_BYTE_STRING(isolate, "NODE_DEBUG_ENABLED"));
|
||||
Local<Value> argv[] = {
|
||||
FIXED_ONE_BYTE_STRING(isolate, "internalMessage"),
|
||||
message
|
||||
};
|
||||
MakeCallback(parent_env_->isolate(), process_object, emit_fn.As<Function>(),
|
||||
arraysize(argv), argv, {0, 0});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Agent::Stop() {
|
||||
if (io_ != nullptr) {
|
||||
io_->Stop();
|
||||
io_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::Connect(InspectorSessionDelegate* delegate) {
|
||||
enabled_ = true;
|
||||
client_->connectFrontend(delegate);
|
||||
}
|
||||
|
||||
bool Agent::IsConnected() {
|
||||
return io_ && io_->IsConnected();
|
||||
}
|
||||
|
||||
void Agent::WaitForDisconnect() {
|
||||
CHECK_NE(client_, nullptr);
|
||||
client_->contextDestroyed(parent_env_->context());
|
||||
if (io_ != nullptr) {
|
||||
io_->WaitForDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void Agent::FatalException(Local<Value> error, Local<v8::Message> message) {
|
||||
if (!IsStarted())
|
||||
return;
|
||||
client_->FatalException(error, message);
|
||||
WaitForDisconnect();
|
||||
}
|
||||
|
||||
void Agent::Dispatch(const StringView& message) {
|
||||
CHECK_NE(client_, nullptr);
|
||||
client_->dispatchMessageFromFrontend(message);
|
||||
}
|
||||
|
||||
void Agent::Disconnect() {
|
||||
CHECK_NE(client_, nullptr);
|
||||
client_->disconnectFrontend();
|
||||
}
|
||||
|
||||
void Agent::RunMessageLoop() {
|
||||
CHECK_NE(client_, nullptr);
|
||||
client_->runMessageLoopOnPause(CONTEXT_GROUP_ID);
|
||||
}
|
||||
|
||||
InspectorSessionDelegate* Agent::delegate() {
|
||||
CHECK_NE(client_, nullptr);
|
||||
ChannelImpl* channel = client_->channel();
|
||||
if (channel == nullptr)
|
||||
return nullptr;
|
||||
return channel->delegate();
|
||||
}
|
||||
|
||||
void Agent::PauseOnNextJavascriptStatement(const std::string& reason) {
|
||||
ChannelImpl* channel = client_->channel();
|
||||
if (channel != nullptr)
|
||||
channel->schedulePauseOnNextStatement(reason);
|
||||
}
|
||||
|
||||
void Open(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
inspector::Agent* agent = env->inspector_agent();
|
||||
bool wait_for_connect = false;
|
||||
|
||||
if (args.Length() > 0 && args[0]->IsUint32()) {
|
||||
uint32_t port = args[0]->Uint32Value(env->context()).ToChecked();
|
||||
agent->options().set_port(static_cast<int>(port));
|
||||
}
|
||||
|
||||
if (args.Length() > 1 && args[1]->IsString()) {
|
||||
node::Utf8Value host(env->isolate(), args[1].As<String>());
|
||||
agent->options().set_host_name(*host);
|
||||
}
|
||||
|
||||
if (args.Length() > 2 && args[2]->IsBoolean()) {
|
||||
wait_for_connect = args[2]->BooleanValue(env->isolate());
|
||||
}
|
||||
|
||||
agent->StartIoThread(wait_for_connect);
|
||||
}
|
||||
|
||||
void Url(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
inspector::Agent* agent = env->inspector_agent();
|
||||
inspector::InspectorIo* io = agent->io();
|
||||
|
||||
if (!io) return;
|
||||
|
||||
std::vector<std::string> ids = io->GetTargetIds();
|
||||
|
||||
if (ids.empty()) return;
|
||||
|
||||
std::string url = FormatWsAddress(io->host(), io->port(), ids[0], true);
|
||||
args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str()));
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
void Agent::InitInspector(Local<Object> target, Local<Value> unused,
|
||||
Local<Context> context, void* priv) {
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
Agent* agent = env->inspector_agent();
|
||||
env->SetMethod(target, "consoleCall", InspectorConsoleCall);
|
||||
if (agent->debug_options_.wait_for_connect())
|
||||
env->SetMethod(target, "callAndPauseOnStart", CallAndPauseOnStart);
|
||||
env->SetMethod(target, "connect", ConnectJSBindingsSession);
|
||||
env->SetMethod(target, "open", Open);
|
||||
env->SetMethod(target, "url", Url);
|
||||
}
|
||||
|
||||
void Agent::RequestIoThreadStart() {
|
||||
// We need to attempt to interrupt V8 flow (in case Node is running
|
||||
// continuous JS code) and to wake up libuv thread (in case Node is waiting
|
||||
// for IO events)
|
||||
uv_async_send(&start_io_thread_async);
|
||||
v8::Isolate* isolate = parent_env_->isolate();
|
||||
platform_->CallOnForegroundThread(isolate, new StartIoTask(this));
|
||||
isolate->RequestInterrupt(StartIoInterrupt, this);
|
||||
uv_async_send(&start_io_thread_async);
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
//cjh NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector,
|
||||
// node::inspector::Agent::InitInspector);
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,118 @@
|
||||
#ifndef SRC_INSPECTOR_AGENT_H_
|
||||
#define SRC_INSPECTOR_AGENT_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if !HAVE_INSPECTOR
|
||||
#error("This header can only be used when inspector is enabled")
|
||||
#endif
|
||||
|
||||
#include "node_debug_options.h"
|
||||
|
||||
// Forward declaration to break recursive dependency chain with src/env.h.
|
||||
namespace node {
|
||||
class Environment;
|
||||
} // namespace node
|
||||
|
||||
namespace v8 {
|
||||
class Context;
|
||||
template <typename V>
|
||||
class FunctionCallbackInfo;
|
||||
template<typename T>
|
||||
class Local;
|
||||
class Message;
|
||||
class Object;
|
||||
class Platform;
|
||||
class Value;
|
||||
} // namespace v8
|
||||
|
||||
namespace v8_inspector {
|
||||
class StringView;
|
||||
} // namespace v8_inspector
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
class InspectorSessionDelegate {
|
||||
public:
|
||||
virtual ~InspectorSessionDelegate() = default;
|
||||
virtual bool WaitForFrontendMessageWhilePaused() = 0;
|
||||
virtual void SendMessageToFrontend(const v8_inspector::StringView& message)
|
||||
= 0;
|
||||
};
|
||||
|
||||
class InspectorIo;
|
||||
class NodeInspectorClient;
|
||||
|
||||
class Agent {
|
||||
public:
|
||||
explicit Agent(node::Environment* env);
|
||||
~Agent();
|
||||
|
||||
// Create client_, may create io_ if option enabled
|
||||
bool Start(v8::Platform* platform, const char* path,
|
||||
const DebugOptions& options);
|
||||
// Stop and destroy io_
|
||||
void Stop();
|
||||
|
||||
bool IsStarted() { return !!client_; }
|
||||
|
||||
// IO thread started, and client connected
|
||||
bool IsConnected();
|
||||
|
||||
|
||||
void WaitForDisconnect();
|
||||
void FatalException(v8::Local<v8::Value> error,
|
||||
v8::Local<v8::Message> message);
|
||||
|
||||
// These methods are called by the WS protocol and JS binding to create
|
||||
// inspector sessions. The inspector responds by using the delegate to send
|
||||
// messages back.
|
||||
void Connect(InspectorSessionDelegate* delegate);
|
||||
void Disconnect();
|
||||
void Dispatch(const v8_inspector::StringView& message);
|
||||
InspectorSessionDelegate* delegate();
|
||||
|
||||
void RunMessageLoop();
|
||||
bool enabled() { return enabled_; }
|
||||
void PauseOnNextJavascriptStatement(const std::string& reason);
|
||||
|
||||
// Initialize 'inspector' module bindings
|
||||
static void InitInspector(v8::Local<v8::Object> target,
|
||||
v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context,
|
||||
void* priv);
|
||||
|
||||
InspectorIo* io() {
|
||||
return io_.get();
|
||||
}
|
||||
|
||||
// Can only be called from the the main thread.
|
||||
bool StartIoThread(bool wait_for_connect);
|
||||
|
||||
// Calls StartIoThread() from off the main thread.
|
||||
void RequestIoThreadStart();
|
||||
|
||||
DebugOptions& options() { return debug_options_; }
|
||||
|
||||
private:
|
||||
node::Environment* parent_env_;
|
||||
std::unique_ptr<NodeInspectorClient> client_;
|
||||
std::unique_ptr<InspectorIo> io_;
|
||||
v8::Platform* platform_;
|
||||
bool enabled_;
|
||||
std::string path_;
|
||||
DebugOptions debug_options_;
|
||||
};
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_INSPECTOR_AGENT_H_
|
||||
@@ -0,0 +1,535 @@
|
||||
#include "inspector_io.h"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "inspector_socket_server.h"
|
||||
#include "env.h"
|
||||
#include "node.h"
|
||||
//cjh #include "node_crypto.h"
|
||||
#include "node_mutex.h"
|
||||
#include "v8-inspector.h"
|
||||
#include "util.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include "libplatform/libplatform.h"
|
||||
|
||||
#include <sstream>
|
||||
//cjh #include <unicode/unistr.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "base/ccUTF8.h" //cjh added
|
||||
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
namespace {
|
||||
using AsyncAndAgent = std::pair<uv_async_t, Agent*>;
|
||||
using v8_inspector::StringBuffer;
|
||||
using v8_inspector::StringView;
|
||||
|
||||
template<typename Transport>
|
||||
using TransportAndIo = std::pair<Transport*, InspectorIo*>;
|
||||
|
||||
std::string GetProcessTitle() {
|
||||
char title[2048];
|
||||
int err = uv_get_process_title(title, sizeof(title));
|
||||
if (err == 0) {
|
||||
return title;
|
||||
} else {
|
||||
// Title is too long, or could not be retrieved.
|
||||
return "Node.js";
|
||||
}
|
||||
}
|
||||
|
||||
std::string ScriptPath(uv_loop_t* loop, const std::string& script_name) {
|
||||
std::string script_path;
|
||||
|
||||
if (!script_name.empty()) {
|
||||
uv_fs_t req;
|
||||
req.ptr = nullptr;
|
||||
if (0 == uv_fs_realpath(loop, &req, script_name.c_str(), nullptr)) {
|
||||
CHECK_NE(req.ptr, nullptr);
|
||||
script_path = std::string(static_cast<char*>(req.ptr));
|
||||
}
|
||||
uv_fs_req_cleanup(&req);
|
||||
}
|
||||
|
||||
return script_path;
|
||||
}
|
||||
|
||||
// UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
|
||||
// Used ver 4 - with numbers
|
||||
std::string GenerateID() {
|
||||
// uint16_t buffer[8];
|
||||
|
||||
//cjh same uuid
|
||||
uint16_t buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||
|
||||
//cjh
|
||||
//cjh CHECK(crypto::EntropySource(reinterpret_cast<unsigned char*>(buffer),
|
||||
// sizeof(buffer)));
|
||||
|
||||
char uuid[256];
|
||||
snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
|
||||
buffer[0], // time_low
|
||||
buffer[1], // time_mid
|
||||
buffer[2], // time_low
|
||||
(buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version
|
||||
(buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low
|
||||
buffer[5], // node
|
||||
buffer[6],
|
||||
buffer[7]);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
std::string StringViewToUtf8(const StringView& view) {
|
||||
if (view.is8Bit()) {
|
||||
return std::string(reinterpret_cast<const char*>(view.characters8()),
|
||||
view.length());
|
||||
}
|
||||
const uint16_t* source = view.characters16();
|
||||
|
||||
std::u16string u16Str((char16_t*)source);
|
||||
std::string ret;
|
||||
cocos2d::StringUtils::UTF16ToUTF8(u16Str, ret);
|
||||
return ret;
|
||||
// const UChar* unicodeSource = reinterpret_cast<const UChar*>(source);
|
||||
// static_assert(sizeof(*source) == sizeof(*unicodeSource),
|
||||
// "sizeof(*source) == sizeof(*unicodeSource)");
|
||||
//
|
||||
// size_t result_length = view.length() * sizeof(*source);
|
||||
// std::string result(result_length, '\0');
|
||||
//cjh UnicodeString utf16(unicodeSource, view.length());
|
||||
// // ICU components for std::string compatibility are not enabled in build...
|
||||
// bool done = false;
|
||||
// while (!done) {
|
||||
// CheckedArrayByteSink sink(&result[0], result_length);
|
||||
// utf16.toUTF8(sink);
|
||||
// result_length = sink.NumberOfBytesAppended();
|
||||
// result.resize(result_length);
|
||||
// done = !sink.Overflowed();
|
||||
// }
|
||||
// return result;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void HandleSyncCloseCb(uv_handle_t* handle) {
|
||||
*static_cast<bool*>(handle->data) = true;
|
||||
}
|
||||
|
||||
int CloseAsyncAndLoop(uv_async_t* async) {
|
||||
bool is_closed = false;
|
||||
async->data = &is_closed;
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(async), HandleSyncCloseCb);
|
||||
while (!is_closed)
|
||||
uv_run(async->loop, UV_RUN_ONCE);
|
||||
async->data = nullptr;
|
||||
return uv_loop_close(async->loop);
|
||||
}
|
||||
|
||||
// Delete main_thread_req_ on async handle close
|
||||
void ReleasePairOnAsyncClose(uv_handle_t* async) {
|
||||
AsyncAndAgent* pair = node::ContainerOf(&AsyncAndAgent::first,
|
||||
reinterpret_cast<uv_async_t*>(async));
|
||||
delete pair;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<StringBuffer> Utf8ToStringView(const std::string& message) {
|
||||
//cjh UnicodeString utf16 =
|
||||
// UnicodeString::fromUTF8(StringPiece(message.data(), message.length()));
|
||||
|
||||
std::u16string u16Str;
|
||||
cocos2d::StringUtils::UTF8ToUTF16(message, u16Str);
|
||||
|
||||
// StringView view(reinterpret_cast<const uint16_t*>(utf16.getBuffer()),
|
||||
// utf16.length());
|
||||
|
||||
StringView view(reinterpret_cast<const uint16_t*>(u16Str.c_str()),
|
||||
u16Str.length());
|
||||
return StringBuffer::create(view);
|
||||
}
|
||||
|
||||
|
||||
class IoSessionDelegate : public InspectorSessionDelegate {
|
||||
public:
|
||||
explicit IoSessionDelegate(InspectorIo* io) : io_(io) { }
|
||||
bool WaitForFrontendMessageWhilePaused() override;
|
||||
void SendMessageToFrontend(const v8_inspector::StringView& message) override;
|
||||
private:
|
||||
InspectorIo* io_;
|
||||
};
|
||||
|
||||
// Passed to InspectorSocketServer to handle WS inspector protocol events,
|
||||
// mostly session start, message received, and session end.
|
||||
class InspectorIoDelegate: public node::inspector::SocketServerDelegate {
|
||||
public:
|
||||
InspectorIoDelegate(InspectorIo* io, const std::string& script_path,
|
||||
const std::string& script_name, bool wait);
|
||||
// Calls PostIncomingMessage() with appropriate InspectorAction:
|
||||
// kStartSession
|
||||
bool StartSession(int session_id, const std::string& target_id) override;
|
||||
// kSendMessage
|
||||
void MessageReceived(int session_id, const std::string& message) override;
|
||||
// kEndSession
|
||||
void EndSession(int session_id) override;
|
||||
|
||||
std::vector<std::string> GetTargetIds() override;
|
||||
std::string GetTargetTitle(const std::string& id) override;
|
||||
std::string GetTargetUrl(const std::string& id) override;
|
||||
bool IsConnected() { return connected_; }
|
||||
void ServerDone() override {
|
||||
io_->ServerDone();
|
||||
}
|
||||
|
||||
private:
|
||||
InspectorIo* io_;
|
||||
bool connected_;
|
||||
int session_id_;
|
||||
const std::string script_name_;
|
||||
const std::string script_path_;
|
||||
const std::string target_id_;
|
||||
bool waiting_;
|
||||
};
|
||||
|
||||
void InterruptCallback(v8::Isolate*, void* agent) {
|
||||
InspectorIo* io = static_cast<Agent*>(agent)->io();
|
||||
if (io != nullptr)
|
||||
io->DispatchMessages();
|
||||
}
|
||||
|
||||
class DispatchMessagesTask : public v8::Task {
|
||||
public:
|
||||
explicit DispatchMessagesTask(Agent* agent) : agent_(agent) {}
|
||||
|
||||
void Run() override {
|
||||
InspectorIo* io = agent_->io();
|
||||
if (io != nullptr)
|
||||
io->DispatchMessages();
|
||||
}
|
||||
|
||||
private:
|
||||
Agent* agent_;
|
||||
};
|
||||
|
||||
InspectorIo::InspectorIo(Environment* env, v8::Platform* platform,
|
||||
const std::string& path, const DebugOptions& options,
|
||||
bool wait_for_connect)
|
||||
: options_(options), thread_(), delegate_(nullptr),
|
||||
state_(State::kNew), parent_env_(env),
|
||||
thread_req_(), platform_(platform),
|
||||
dispatching_messages_(false), session_id_(0),
|
||||
script_name_(path),
|
||||
wait_for_connect_(wait_for_connect), port_(-1) {
|
||||
main_thread_req_ = new AsyncAndAgent({uv_async_t(), env->inspector_agent()});
|
||||
CHECK_EQ(0, uv_async_init(env->event_loop(), &main_thread_req_->first,
|
||||
InspectorIo::MainThreadReqAsyncCb));
|
||||
uv_unref(reinterpret_cast<uv_handle_t*>(&main_thread_req_->first));
|
||||
CHECK_EQ(0, uv_sem_init(&thread_start_sem_, 0));
|
||||
}
|
||||
|
||||
InspectorIo::~InspectorIo() {
|
||||
uv_sem_destroy(&thread_start_sem_);
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&main_thread_req_->first),
|
||||
ReleasePairOnAsyncClose);
|
||||
if (main_thread_req_)
|
||||
{
|
||||
delete main_thread_req_;
|
||||
main_thread_req_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool InspectorIo::Start() {
|
||||
CHECK_EQ(state_, State::kNew);
|
||||
CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain, this), 0);
|
||||
uv_sem_wait(&thread_start_sem_);
|
||||
|
||||
if (state_ == State::kError) {
|
||||
return false;
|
||||
}
|
||||
state_ = State::kAccepting;
|
||||
if (wait_for_connect_) {
|
||||
DispatchMessages();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InspectorIo::Stop() {
|
||||
CHECK(state_ == State::kAccepting || state_ == State::kConnected);
|
||||
Write(TransportAction::kKill, 0, StringView());
|
||||
int err = uv_thread_join(&thread_);
|
||||
CHECK_EQ(err, 0);
|
||||
state_ = State::kShutDown;
|
||||
DispatchMessages();
|
||||
}
|
||||
|
||||
bool InspectorIo::IsConnected() {
|
||||
return delegate_ != nullptr && delegate_->IsConnected();
|
||||
}
|
||||
|
||||
bool InspectorIo::IsStarted() {
|
||||
return platform_ != nullptr;
|
||||
}
|
||||
|
||||
void InspectorIo::WaitForDisconnect() {
|
||||
if (state_ == State::kAccepting)
|
||||
state_ = State::kDone;
|
||||
if (state_ == State::kConnected) {
|
||||
state_ = State::kShutDown;
|
||||
Write(TransportAction::kStop, 0, StringView());
|
||||
SE_LOGD("Waiting for the debugger to disconnect...\n");
|
||||
parent_env_->inspector_agent()->RunMessageLoop();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void InspectorIo::ThreadMain(void* io) {
|
||||
static_cast<InspectorIo*>(io)->ThreadMain<InspectorSocketServer>();
|
||||
}
|
||||
|
||||
// static
|
||||
template <typename Transport>
|
||||
void InspectorIo::IoThreadAsyncCb(uv_async_t* async) {
|
||||
TransportAndIo<Transport>* transport_and_io =
|
||||
static_cast<TransportAndIo<Transport>*>(async->data);
|
||||
if (transport_and_io == nullptr) {
|
||||
return;
|
||||
}
|
||||
Transport* transport = transport_and_io->first;
|
||||
InspectorIo* io = transport_and_io->second;
|
||||
MessageQueue<TransportAction> outgoing_message_queue;
|
||||
io->SwapBehindLock(&io->outgoing_message_queue_, &outgoing_message_queue);
|
||||
for (const auto& outgoing : outgoing_message_queue) {
|
||||
switch (std::get<0>(outgoing)) {
|
||||
case TransportAction::kKill:
|
||||
transport->TerminateConnections();
|
||||
// Fallthrough
|
||||
case TransportAction::kStop:
|
||||
transport->Stop(nullptr);
|
||||
break;
|
||||
case TransportAction::kSendMessage:
|
||||
std::string message = StringViewToUtf8(std::get<2>(outgoing)->string());
|
||||
transport->Send(std::get<1>(outgoing), message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Transport>
|
||||
void InspectorIo::ThreadMain() {
|
||||
uv_loop_t loop;
|
||||
loop.data = nullptr;
|
||||
int err = uv_loop_init(&loop);
|
||||
CHECK_EQ(err, 0);
|
||||
thread_req_.data = nullptr;
|
||||
err = uv_async_init(&loop, &thread_req_, IoThreadAsyncCb<Transport>);
|
||||
CHECK_EQ(err, 0);
|
||||
std::string script_path = ScriptPath(&loop, script_name_);
|
||||
InspectorIoDelegate delegate(this, script_path, script_name_,
|
||||
wait_for_connect_);
|
||||
delegate_ = &delegate;
|
||||
Transport server(&delegate, &loop, options_.host_name(), options_.port());
|
||||
TransportAndIo<Transport> queue_transport(&server, this);
|
||||
thread_req_.data = &queue_transport;
|
||||
if (!server.Start()) {
|
||||
state_ = State::kError; // Safe, main thread is waiting on semaphore
|
||||
CHECK_EQ(0, CloseAsyncAndLoop(&thread_req_));
|
||||
uv_sem_post(&thread_start_sem_);
|
||||
return;
|
||||
}
|
||||
port_ = server.Port(); // Safe, main thread is waiting on semaphore.
|
||||
if (!wait_for_connect_) {
|
||||
uv_sem_post(&thread_start_sem_);
|
||||
}
|
||||
uv_run(&loop, UV_RUN_DEFAULT);
|
||||
thread_req_.data = nullptr;
|
||||
CHECK_EQ(uv_loop_close(&loop), 0);
|
||||
delegate_ = nullptr;
|
||||
}
|
||||
|
||||
template <typename ActionType>
|
||||
bool InspectorIo::AppendMessage(MessageQueue<ActionType>* queue,
|
||||
ActionType action, int session_id,
|
||||
std::unique_ptr<StringBuffer> buffer) {
|
||||
Mutex::ScopedLock scoped_lock(state_lock_);
|
||||
bool trigger_pumping = queue->empty();
|
||||
queue->push_back(std::make_tuple(action, session_id, std::move(buffer)));
|
||||
return trigger_pumping;
|
||||
}
|
||||
|
||||
template <typename ActionType>
|
||||
void InspectorIo::SwapBehindLock(MessageQueue<ActionType>* vector1,
|
||||
MessageQueue<ActionType>* vector2) {
|
||||
Mutex::ScopedLock scoped_lock(state_lock_);
|
||||
vector1->swap(*vector2);
|
||||
}
|
||||
|
||||
void InspectorIo::PostIncomingMessage(InspectorAction action, int session_id,
|
||||
const std::string& message) {
|
||||
if (AppendMessage(&incoming_message_queue_, action, session_id,
|
||||
Utf8ToStringView(message))) {
|
||||
Agent* agent = main_thread_req_->second;
|
||||
v8::Isolate* isolate = parent_env_->isolate();
|
||||
platform_->CallOnForegroundThread(isolate,
|
||||
new DispatchMessagesTask(agent));
|
||||
isolate->RequestInterrupt(InterruptCallback, agent);
|
||||
CHECK_EQ(0, uv_async_send(&main_thread_req_->first));
|
||||
}
|
||||
NotifyMessageReceived();
|
||||
}
|
||||
|
||||
std::vector<std::string> InspectorIo::GetTargetIds() const {
|
||||
return delegate_ ? delegate_->GetTargetIds() : std::vector<std::string>();
|
||||
}
|
||||
|
||||
void InspectorIo::WaitForFrontendMessageWhilePaused() {
|
||||
dispatching_messages_ = false;
|
||||
Mutex::ScopedLock scoped_lock(state_lock_);
|
||||
if (incoming_message_queue_.empty())
|
||||
incoming_message_cond_.Wait(scoped_lock);
|
||||
}
|
||||
|
||||
void InspectorIo::NotifyMessageReceived() {
|
||||
Mutex::ScopedLock scoped_lock(state_lock_);
|
||||
incoming_message_cond_.Broadcast(scoped_lock);
|
||||
}
|
||||
|
||||
void InspectorIo::DispatchMessages() {
|
||||
// This function can be reentered if there was an incoming message while
|
||||
// V8 was processing another inspector request (e.g. if the user is
|
||||
// evaluating a long-running JS code snippet). This can happen only at
|
||||
// specific points (e.g. the lines that call inspector_ methods)
|
||||
if (dispatching_messages_)
|
||||
return;
|
||||
dispatching_messages_ = true;
|
||||
bool had_messages = false;
|
||||
do {
|
||||
if (dispatching_message_queue_.empty())
|
||||
SwapBehindLock(&incoming_message_queue_, &dispatching_message_queue_);
|
||||
had_messages = !dispatching_message_queue_.empty();
|
||||
while (!dispatching_message_queue_.empty()) {
|
||||
MessageQueue<InspectorAction>::value_type task;
|
||||
std::swap(dispatching_message_queue_.front(), task);
|
||||
dispatching_message_queue_.pop_front();
|
||||
StringView message = std::get<2>(task)->string();
|
||||
switch (std::get<0>(task)) {
|
||||
case InspectorAction::kStartSession:
|
||||
CHECK_EQ(session_delegate_, nullptr);
|
||||
session_id_ = std::get<1>(task);
|
||||
state_ = State::kConnected;
|
||||
SE_LOGD("Debugger attached.\n");
|
||||
session_delegate_ = std::unique_ptr<InspectorSessionDelegate>(
|
||||
new IoSessionDelegate(this));
|
||||
parent_env_->inspector_agent()->Connect(session_delegate_.get());
|
||||
break;
|
||||
case InspectorAction::kEndSession:
|
||||
CHECK_NE(session_delegate_, nullptr);
|
||||
if (state_ == State::kShutDown) {
|
||||
state_ = State::kDone;
|
||||
} else {
|
||||
state_ = State::kAccepting;
|
||||
}
|
||||
parent_env_->inspector_agent()->Disconnect();
|
||||
session_delegate_.reset();
|
||||
break;
|
||||
case InspectorAction::kSendMessage:
|
||||
parent_env_->inspector_agent()->Dispatch(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (had_messages);
|
||||
dispatching_messages_ = false;
|
||||
}
|
||||
|
||||
// static
|
||||
void InspectorIo::MainThreadReqAsyncCb(uv_async_t* req) {
|
||||
AsyncAndAgent* pair = node::ContainerOf(&AsyncAndAgent::first, req);
|
||||
// Note that this may be called after io was closed or even after a new
|
||||
// one was created and ran.
|
||||
InspectorIo* io = pair->second->io();
|
||||
if (io != nullptr)
|
||||
io->DispatchMessages();
|
||||
}
|
||||
|
||||
void InspectorIo::Write(TransportAction action, int session_id,
|
||||
const StringView& inspector_message) {
|
||||
AppendMessage(&outgoing_message_queue_, action, session_id,
|
||||
StringBuffer::create(inspector_message));
|
||||
int err = uv_async_send(&thread_req_);
|
||||
CHECK_EQ(0, err);
|
||||
}
|
||||
|
||||
InspectorIoDelegate::InspectorIoDelegate(InspectorIo* io,
|
||||
const std::string& script_path,
|
||||
const std::string& script_name,
|
||||
bool wait)
|
||||
: io_(io),
|
||||
connected_(false),
|
||||
session_id_(0),
|
||||
script_name_(script_name),
|
||||
script_path_(script_path),
|
||||
target_id_(GenerateID()),
|
||||
waiting_(wait) { }
|
||||
|
||||
|
||||
bool InspectorIoDelegate::StartSession(int session_id,
|
||||
const std::string& target_id) {
|
||||
if (connected_)
|
||||
return false;
|
||||
connected_ = true;
|
||||
session_id_++;
|
||||
io_->PostIncomingMessage(InspectorAction::kStartSession, session_id, "");
|
||||
return true;
|
||||
}
|
||||
|
||||
void InspectorIoDelegate::MessageReceived(int session_id,
|
||||
const std::string& message) {
|
||||
// REFINE(pfeldman): Instead of blocking execution while debugger
|
||||
// engages, node should wait for the run callback from the remote client
|
||||
// and initiate its startup. This is a change to node.cc that should be
|
||||
// upstreamed separately.
|
||||
if (waiting_) {
|
||||
if (message.find("\"Runtime.runIfWaitingForDebugger\"") !=
|
||||
std::string::npos) {
|
||||
waiting_ = false;
|
||||
io_->ResumeStartup();
|
||||
}
|
||||
}
|
||||
io_->PostIncomingMessage(InspectorAction::kSendMessage, session_id,
|
||||
message);
|
||||
}
|
||||
|
||||
void InspectorIoDelegate::EndSession(int session_id) {
|
||||
connected_ = false;
|
||||
io_->PostIncomingMessage(InspectorAction::kEndSession, session_id, "");
|
||||
}
|
||||
|
||||
std::vector<std::string> InspectorIoDelegate::GetTargetIds() {
|
||||
return { target_id_ };
|
||||
}
|
||||
|
||||
std::string InspectorIoDelegate::GetTargetTitle(const std::string& id) {
|
||||
return script_name_.empty() ? GetProcessTitle() : script_name_;
|
||||
}
|
||||
|
||||
std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) {
|
||||
return "file://" + script_path_;
|
||||
}
|
||||
|
||||
bool IoSessionDelegate::WaitForFrontendMessageWhilePaused() {
|
||||
io_->WaitForFrontendMessageWhilePaused();
|
||||
return true;
|
||||
}
|
||||
|
||||
void IoSessionDelegate::SendMessageToFrontend(
|
||||
const v8_inspector::StringView& message) {
|
||||
io_->Write(TransportAction::kSendMessage, io_->session_id_, message);
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,180 @@
|
||||
#ifndef SRC_INSPECTOR_IO_H_
|
||||
#define SRC_INSPECTOR_IO_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "inspector_socket_server.h"
|
||||
#include "node_debug_options.h"
|
||||
#include "node_mutex.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <stddef.h>
|
||||
|
||||
#if !HAVE_INSPECTOR
|
||||
#error("This header can only be used when inspector is enabled")
|
||||
#endif
|
||||
|
||||
|
||||
// Forward declaration to break recursive dependency chain with src/env.h.
|
||||
namespace node {
|
||||
class Environment;
|
||||
} // namespace node
|
||||
|
||||
namespace v8_inspector {
|
||||
class StringBuffer;
|
||||
class StringView;
|
||||
} // namespace v8_inspector
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
std::string FormatWsAddress(const std::string& host, int port,
|
||||
const std::string& target_id,
|
||||
bool include_protocol);
|
||||
|
||||
class InspectorIoDelegate;
|
||||
|
||||
enum class InspectorAction {
|
||||
kStartSession,
|
||||
kEndSession,
|
||||
kSendMessage
|
||||
};
|
||||
|
||||
// kKill closes connections and stops the server, kStop only stops the server
|
||||
enum class TransportAction {
|
||||
kKill,
|
||||
kSendMessage,
|
||||
kStop
|
||||
};
|
||||
|
||||
class InspectorIo {
|
||||
public:
|
||||
InspectorIo(node::Environment* env, v8::Platform* platform,
|
||||
const std::string& path, const DebugOptions& options,
|
||||
bool wait_for_connect);
|
||||
|
||||
~InspectorIo();
|
||||
// Start the inspector agent thread, waiting for it to initialize,
|
||||
// and waiting as well for a connection if wait_for_connect.
|
||||
bool Start();
|
||||
// Stop the inspector agent thread.
|
||||
void Stop();
|
||||
|
||||
bool IsStarted();
|
||||
bool IsConnected();
|
||||
|
||||
void WaitForDisconnect();
|
||||
// Called from thread to queue an incoming message and trigger
|
||||
// DispatchMessages() on the main thread.
|
||||
void PostIncomingMessage(InspectorAction action, int session_id,
|
||||
const std::string& message);
|
||||
void ResumeStartup() {
|
||||
uv_sem_post(&thread_start_sem_);
|
||||
}
|
||||
void ServerDone() {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&thread_req_), nullptr);
|
||||
}
|
||||
|
||||
int port() const { return port_; }
|
||||
std::string host() const { return options_.host_name(); }
|
||||
std::vector<std::string> GetTargetIds() const;
|
||||
|
||||
private:
|
||||
template <typename Action>
|
||||
using MessageQueue =
|
||||
std::deque<std::tuple<Action, int,
|
||||
std::unique_ptr<v8_inspector::StringBuffer>>>;
|
||||
enum class State {
|
||||
kNew,
|
||||
kAccepting,
|
||||
kConnected,
|
||||
kDone,
|
||||
kError,
|
||||
kShutDown
|
||||
};
|
||||
|
||||
// Callback for main_thread_req_'s uv_async_t
|
||||
static void MainThreadReqAsyncCb(uv_async_t* req);
|
||||
|
||||
// Wrapper for agent->ThreadMain()
|
||||
static void ThreadMain(void* agent);
|
||||
|
||||
// Runs a uv_loop_t
|
||||
template <typename Transport> void ThreadMain();
|
||||
// Called by ThreadMain's loop when triggered by thread_req_, writes
|
||||
// messages from outgoing_message_queue to the InspectorSockerServer
|
||||
template <typename Transport> static void IoThreadAsyncCb(uv_async_t* async);
|
||||
|
||||
void SetConnected(bool connected);
|
||||
void DispatchMessages();
|
||||
// Write action to outgoing_message_queue, and wake the thread
|
||||
void Write(TransportAction action, int session_id,
|
||||
const v8_inspector::StringView& message);
|
||||
// Thread-safe append of message to a queue. Return true if the queue
|
||||
// used to be empty.
|
||||
template <typename ActionType>
|
||||
bool AppendMessage(MessageQueue<ActionType>* vector, ActionType action,
|
||||
int session_id,
|
||||
std::unique_ptr<v8_inspector::StringBuffer> buffer);
|
||||
// Used as equivalent of a thread-safe "pop" of an entire queue's content.
|
||||
template <typename ActionType>
|
||||
void SwapBehindLock(MessageQueue<ActionType>* vector1,
|
||||
MessageQueue<ActionType>* vector2);
|
||||
// Wait on incoming_message_cond_
|
||||
void WaitForFrontendMessageWhilePaused();
|
||||
// Broadcast incoming_message_cond_
|
||||
void NotifyMessageReceived();
|
||||
|
||||
const DebugOptions options_;
|
||||
|
||||
// The IO thread runs its own uv_loop to implement the TCP server off
|
||||
// the main thread.
|
||||
uv_thread_t thread_;
|
||||
// Used by Start() to wait for thread to initialize, or for it to initialize
|
||||
// and receive a connection if wait_for_connect was requested.
|
||||
uv_sem_t thread_start_sem_;
|
||||
|
||||
InspectorIoDelegate* delegate_;
|
||||
State state_;
|
||||
node::Environment* parent_env_;
|
||||
|
||||
// Attached to the uv_loop in ThreadMain()
|
||||
uv_async_t thread_req_;
|
||||
// Note that this will live while the async is being closed - likely, past
|
||||
// the parent object lifespan
|
||||
std::pair<uv_async_t, Agent*>* main_thread_req_;
|
||||
std::unique_ptr<InspectorSessionDelegate> session_delegate_;
|
||||
v8::Platform* platform_;
|
||||
|
||||
// Message queues
|
||||
ConditionVariable incoming_message_cond_;
|
||||
Mutex state_lock_; // Locked before mutating either queue.
|
||||
MessageQueue<InspectorAction> incoming_message_queue_;
|
||||
MessageQueue<TransportAction> outgoing_message_queue_;
|
||||
MessageQueue<InspectorAction> dispatching_message_queue_;
|
||||
|
||||
bool dispatching_messages_;
|
||||
int session_id_;
|
||||
|
||||
std::string script_name_;
|
||||
std::string script_path_;
|
||||
const bool wait_for_connect_;
|
||||
int port_;
|
||||
|
||||
friend class DispatchMessagesTask;
|
||||
friend class IoSessionDelegate;
|
||||
friend void InterruptCallback(v8::Isolate*, void* agent);
|
||||
};
|
||||
|
||||
std::unique_ptr<v8_inspector::StringBuffer> Utf8ToStringView(
|
||||
const std::string& message);
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_INSPECTOR_IO_H_
|
||||
@@ -0,0 +1,640 @@
|
||||
#include "inspector_socket.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
//#include "openssl/sha.h" // Sha-1 hash
|
||||
#include "SHA1.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#define ACCEPT_KEY_LENGTH base64_encoded_size(20)
|
||||
#define BUFFER_GROWTH_CHUNK_SIZE 1024
|
||||
|
||||
#define DUMP_READS 0
|
||||
#define DUMP_WRITES 0
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
static const char CLOSE_FRAME[] = {'\x88', '\x00'};
|
||||
|
||||
enum ws_decode_result {
|
||||
FRAME_OK, FRAME_INCOMPLETE, FRAME_CLOSE, FRAME_ERROR
|
||||
};
|
||||
|
||||
#if DUMP_READS || DUMP_WRITES
|
||||
static void dump_hex(const char* buf, size_t len) {
|
||||
const char* ptr = buf;
|
||||
const char* end = ptr + len;
|
||||
const char* cptr;
|
||||
char c;
|
||||
int i;
|
||||
|
||||
while (ptr < end) {
|
||||
cptr = ptr;
|
||||
for (i = 0; i < 16 && ptr < end; i++) {
|
||||
printf("%2.2X ", static_cast<unsigned char>(*(ptr++)));
|
||||
}
|
||||
for (i = 72 - (i * 4); i > 0; i--) {
|
||||
printf(" ");
|
||||
}
|
||||
for (i = 0; i < 16 && cptr < end; i++) {
|
||||
c = *(cptr++);
|
||||
printf("%c", (c > 0x19) ? c : '.');
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void remove_from_beginning(std::vector<char>* buffer, size_t count) {
|
||||
buffer->erase(buffer->begin(), buffer->begin() + count);
|
||||
}
|
||||
|
||||
static void dispose_inspector(uv_handle_t* handle) {
|
||||
InspectorSocket* inspector = inspector_from_stream(handle);
|
||||
inspector_cb close =
|
||||
inspector->ws_mode ? inspector->ws_state->close_cb : nullptr;
|
||||
inspector->buffer.clear();
|
||||
delete inspector->ws_state;
|
||||
inspector->ws_state = nullptr;
|
||||
if (close) {
|
||||
close(inspector, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void close_connection(InspectorSocket* inspector) {
|
||||
uv_handle_t* socket = reinterpret_cast<uv_handle_t*>(&inspector->tcp);
|
||||
if (!uv_is_closing(socket)) {
|
||||
uv_read_stop(reinterpret_cast<uv_stream_t*>(socket));
|
||||
uv_close(socket, dispose_inspector);
|
||||
}
|
||||
}
|
||||
|
||||
struct WriteRequest {
|
||||
WriteRequest(InspectorSocket* inspector, const char* data, size_t size)
|
||||
: inspector(inspector)
|
||||
, storage(data, data + size)
|
||||
, buf(uv_buf_init(&storage[0], storage.size())) {}
|
||||
|
||||
static WriteRequest* from_write_req(uv_write_t* req) {
|
||||
return node::ContainerOf(&WriteRequest::req, req);
|
||||
}
|
||||
|
||||
InspectorSocket* const inspector;
|
||||
std::vector<char> storage;
|
||||
uv_write_t req;
|
||||
uv_buf_t buf;
|
||||
};
|
||||
|
||||
// Cleanup
|
||||
static void write_request_cleanup(uv_write_t* req, int status) {
|
||||
delete WriteRequest::from_write_req(req);
|
||||
}
|
||||
|
||||
static int write_to_client(InspectorSocket* inspector,
|
||||
const char* msg,
|
||||
size_t len,
|
||||
uv_write_cb write_cb = write_request_cleanup) {
|
||||
#if DUMP_WRITES
|
||||
printf("%s (%ld bytes):\n", __FUNCTION__, len);
|
||||
dump_hex(msg, len);
|
||||
#endif
|
||||
|
||||
// Freed in write_request_cleanup
|
||||
WriteRequest* wr = new WriteRequest(inspector, msg, len);
|
||||
uv_stream_t* stream = reinterpret_cast<uv_stream_t*>(&inspector->tcp);
|
||||
return uv_write(&wr->req, stream, &wr->buf, 1, write_cb) < 0;
|
||||
}
|
||||
|
||||
// Constants for hybi-10 frame format.
|
||||
|
||||
typedef int OpCode;
|
||||
|
||||
const OpCode kOpCodeContinuation = 0x0;
|
||||
const OpCode kOpCodeText = 0x1;
|
||||
const OpCode kOpCodeBinary = 0x2;
|
||||
const OpCode kOpCodeClose = 0x8;
|
||||
const OpCode kOpCodePing = 0x9;
|
||||
const OpCode kOpCodePong = 0xA;
|
||||
|
||||
const unsigned char kFinalBit = 0x80;
|
||||
const unsigned char kReserved1Bit = 0x40;
|
||||
const unsigned char kReserved2Bit = 0x20;
|
||||
const unsigned char kReserved3Bit = 0x10;
|
||||
const unsigned char kOpCodeMask = 0xF;
|
||||
const unsigned char kMaskBit = 0x80;
|
||||
const unsigned char kPayloadLengthMask = 0x7F;
|
||||
|
||||
const size_t kMaxSingleBytePayloadLength = 125;
|
||||
const size_t kTwoBytePayloadLengthField = 126;
|
||||
const size_t kEightBytePayloadLengthField = 127;
|
||||
const size_t kMaskingKeyWidthInBytes = 4;
|
||||
|
||||
static std::vector<char> encode_frame_hybi17(const char* message,
|
||||
size_t data_length) {
|
||||
std::vector<char> frame;
|
||||
OpCode op_code = kOpCodeText;
|
||||
frame.push_back(kFinalBit | op_code);
|
||||
if (data_length <= kMaxSingleBytePayloadLength) {
|
||||
frame.push_back(static_cast<char>(data_length));
|
||||
} else if (data_length <= 0xFFFF) {
|
||||
frame.push_back(kTwoBytePayloadLengthField);
|
||||
frame.push_back((data_length & 0xFF00) >> 8);
|
||||
frame.push_back(data_length & 0xFF);
|
||||
} else {
|
||||
frame.push_back(kEightBytePayloadLengthField);
|
||||
char extended_payload_length[8];
|
||||
size_t remaining = data_length;
|
||||
// Fill the length into extended_payload_length in the network byte order.
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
extended_payload_length[7 - i] = remaining & 0xFF;
|
||||
remaining >>= 8;
|
||||
}
|
||||
frame.insert(frame.end(), extended_payload_length,
|
||||
extended_payload_length + 8);
|
||||
CHECK_EQ(0, remaining);
|
||||
}
|
||||
frame.insert(frame.end(), message, message + data_length);
|
||||
return frame;
|
||||
}
|
||||
|
||||
static ws_decode_result decode_frame_hybi17(const std::vector<char>& buffer,
|
||||
bool client_frame,
|
||||
int* bytes_consumed,
|
||||
std::vector<char>* output,
|
||||
bool* compressed) {
|
||||
*bytes_consumed = 0;
|
||||
if (buffer.size() < 2)
|
||||
return FRAME_INCOMPLETE;
|
||||
|
||||
auto it = buffer.begin();
|
||||
|
||||
unsigned char first_byte = *it++;
|
||||
unsigned char second_byte = *it++;
|
||||
|
||||
bool final = (first_byte & kFinalBit) != 0;
|
||||
bool reserved1 = (first_byte & kReserved1Bit) != 0;
|
||||
bool reserved2 = (first_byte & kReserved2Bit) != 0;
|
||||
bool reserved3 = (first_byte & kReserved3Bit) != 0;
|
||||
int op_code = first_byte & kOpCodeMask;
|
||||
bool masked = (second_byte & kMaskBit) != 0;
|
||||
*compressed = reserved1;
|
||||
if (!final || reserved2 || reserved3)
|
||||
return FRAME_ERROR; // Only compression extension is supported.
|
||||
|
||||
bool closed = false;
|
||||
switch (op_code) {
|
||||
case kOpCodeClose:
|
||||
closed = true;
|
||||
break;
|
||||
case kOpCodeText:
|
||||
break;
|
||||
case kOpCodeBinary: // We don't support binary frames yet.
|
||||
case kOpCodeContinuation: // We don't support binary frames yet.
|
||||
case kOpCodePing: // We don't support binary frames yet.
|
||||
case kOpCodePong: // We don't support binary frames yet.
|
||||
default:
|
||||
return FRAME_ERROR;
|
||||
}
|
||||
|
||||
// In Hybi-17 spec client MUST mask its frame.
|
||||
if (client_frame && !masked) {
|
||||
return FRAME_ERROR;
|
||||
}
|
||||
|
||||
uint64_t payload_length64 = second_byte & kPayloadLengthMask;
|
||||
if (payload_length64 > kMaxSingleBytePayloadLength) {
|
||||
int extended_payload_length_size;
|
||||
if (payload_length64 == kTwoBytePayloadLengthField) {
|
||||
extended_payload_length_size = 2;
|
||||
} else if (payload_length64 == kEightBytePayloadLengthField) {
|
||||
extended_payload_length_size = 8;
|
||||
} else {
|
||||
return FRAME_ERROR;
|
||||
}
|
||||
if ((buffer.end() - it) < extended_payload_length_size)
|
||||
return FRAME_INCOMPLETE;
|
||||
payload_length64 = 0;
|
||||
for (int i = 0; i < extended_payload_length_size; ++i) {
|
||||
payload_length64 <<= 8;
|
||||
payload_length64 |= static_cast<unsigned char>(*it++);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint64_t max_payload_length = 0x7FFFFFFFFFFFFFFFull;
|
||||
static const size_t max_length = SIZE_MAX;
|
||||
if (payload_length64 > max_payload_length ||
|
||||
payload_length64 > max_length - kMaskingKeyWidthInBytes) {
|
||||
// WebSocket frame length too large.
|
||||
return FRAME_ERROR;
|
||||
}
|
||||
size_t payload_length = static_cast<size_t>(payload_length64);
|
||||
|
||||
if (buffer.size() - kMaskingKeyWidthInBytes < payload_length)
|
||||
return FRAME_INCOMPLETE;
|
||||
|
||||
std::vector<char>::const_iterator masking_key = it;
|
||||
std::vector<char>::const_iterator payload = it + kMaskingKeyWidthInBytes;
|
||||
for (size_t i = 0; i < payload_length; ++i) // Unmask the payload.
|
||||
output->insert(output->end(),
|
||||
payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes]);
|
||||
|
||||
size_t pos = it + kMaskingKeyWidthInBytes + payload_length - buffer.begin();
|
||||
*bytes_consumed = pos;
|
||||
return closed ? FRAME_CLOSE : FRAME_OK;
|
||||
}
|
||||
|
||||
static void invoke_read_callback(InspectorSocket* inspector,
|
||||
int status, const uv_buf_t* buf) {
|
||||
if (inspector->ws_state->read_cb) {
|
||||
inspector->ws_state->read_cb(
|
||||
reinterpret_cast<uv_stream_t*>(&inspector->tcp), status, buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void shutdown_complete(InspectorSocket* inspector) {
|
||||
close_connection(inspector);
|
||||
}
|
||||
|
||||
static void on_close_frame_written(uv_write_t* req, int status) {
|
||||
WriteRequest* wr = WriteRequest::from_write_req(req);
|
||||
InspectorSocket* inspector = wr->inspector;
|
||||
delete wr;
|
||||
inspector->ws_state->close_sent = true;
|
||||
if (inspector->ws_state->received_close) {
|
||||
shutdown_complete(inspector);
|
||||
}
|
||||
}
|
||||
|
||||
static void close_frame_received(InspectorSocket* inspector) {
|
||||
inspector->ws_state->received_close = true;
|
||||
if (!inspector->ws_state->close_sent) {
|
||||
invoke_read_callback(inspector, 0, 0);
|
||||
write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME),
|
||||
on_close_frame_written);
|
||||
} else {
|
||||
shutdown_complete(inspector);
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_ws_frames(InspectorSocket* inspector) {
|
||||
int bytes_consumed = 0;
|
||||
std::vector<char> output;
|
||||
bool compressed = false;
|
||||
|
||||
ws_decode_result r = decode_frame_hybi17(inspector->buffer,
|
||||
true /* client_frame */,
|
||||
&bytes_consumed, &output,
|
||||
&compressed);
|
||||
// Compressed frame means client is ignoring the headers and misbehaves
|
||||
if (compressed || r == FRAME_ERROR) {
|
||||
invoke_read_callback(inspector, UV_EPROTO, nullptr);
|
||||
close_connection(inspector);
|
||||
bytes_consumed = 0;
|
||||
} else if (r == FRAME_CLOSE) {
|
||||
close_frame_received(inspector);
|
||||
bytes_consumed = 0;
|
||||
} else if (r == FRAME_OK && inspector->ws_state->alloc_cb
|
||||
&& inspector->ws_state->read_cb) {
|
||||
uv_buf_t buffer;
|
||||
size_t len = output.size();
|
||||
inspector->ws_state->alloc_cb(
|
||||
reinterpret_cast<uv_handle_t*>(&inspector->tcp),
|
||||
len, &buffer);
|
||||
CHECK_GE(buffer.len, len);
|
||||
memcpy(buffer.base, &output[0], len);
|
||||
invoke_read_callback(inspector, len, &buffer);
|
||||
}
|
||||
return bytes_consumed;
|
||||
}
|
||||
|
||||
static void prepare_buffer(uv_handle_t* stream, size_t len, uv_buf_t* buf) {
|
||||
*buf = uv_buf_init(new char[len], len);
|
||||
}
|
||||
|
||||
static void reclaim_uv_buf(InspectorSocket* inspector, const uv_buf_t* buf,
|
||||
ssize_t read) {
|
||||
if (read > 0) {
|
||||
std::vector<char>& buffer = inspector->buffer;
|
||||
buffer.insert(buffer.end(), buf->base, buf->base + read);
|
||||
}
|
||||
delete[] buf->base;
|
||||
}
|
||||
|
||||
static void websockets_data_cb(uv_stream_t* stream, ssize_t nread,
|
||||
const uv_buf_t* buf) {
|
||||
InspectorSocket* inspector = inspector_from_stream(stream);
|
||||
reclaim_uv_buf(inspector, buf, nread);
|
||||
if (nread < 0 || nread == UV_EOF) {
|
||||
inspector->connection_eof = true;
|
||||
if (!inspector->shutting_down && inspector->ws_state->read_cb) {
|
||||
inspector->ws_state->read_cb(stream, nread, nullptr);
|
||||
}
|
||||
if (inspector->ws_state->close_sent &&
|
||||
!inspector->ws_state->received_close) {
|
||||
shutdown_complete(inspector); // invoke callback
|
||||
}
|
||||
} else {
|
||||
#if DUMP_READS
|
||||
printf("%s read %ld bytes\n", __FUNCTION__, nread);
|
||||
if (nread > 0) {
|
||||
dump_hex(inspector->buffer.data() + inspector->buffer.size() - nread,
|
||||
nread);
|
||||
}
|
||||
#endif
|
||||
// 2. Parse.
|
||||
int processed = 0;
|
||||
do {
|
||||
processed = parse_ws_frames(inspector);
|
||||
// 3. Fix the buffer size & length
|
||||
if (processed > 0) {
|
||||
remove_from_beginning(&inspector->buffer, processed);
|
||||
}
|
||||
} while (processed > 0 && !inspector->buffer.empty());
|
||||
}
|
||||
}
|
||||
|
||||
int inspector_read_start(InspectorSocket* inspector,
|
||||
uv_alloc_cb alloc_cb, uv_read_cb read_cb) {
|
||||
CHECK(inspector->ws_mode);
|
||||
CHECK(!inspector->shutting_down || read_cb == nullptr);
|
||||
inspector->ws_state->close_sent = false;
|
||||
inspector->ws_state->alloc_cb = alloc_cb;
|
||||
inspector->ws_state->read_cb = read_cb;
|
||||
int err =
|
||||
uv_read_start(reinterpret_cast<uv_stream_t*>(&inspector->tcp),
|
||||
prepare_buffer,
|
||||
websockets_data_cb);
|
||||
if (err < 0) {
|
||||
close_connection(inspector);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void inspector_read_stop(InspectorSocket* inspector) {
|
||||
uv_read_stop(reinterpret_cast<uv_stream_t*>(&inspector->tcp));
|
||||
inspector->ws_state->alloc_cb = nullptr;
|
||||
inspector->ws_state->read_cb = nullptr;
|
||||
}
|
||||
|
||||
static void generate_accept_string(const std::string& client_key,
|
||||
char (*buffer)[ACCEPT_KEY_LENGTH]) {
|
||||
// Magic string from websockets spec.
|
||||
static const char ws_magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
std::string input(client_key + ws_magic);
|
||||
// char hash[SHA_DIGEST_LENGTH];
|
||||
// SHA1(reinterpret_cast<const unsigned char*>(&input[0]), input.size(),
|
||||
// reinterpret_cast<unsigned char*>(hash));
|
||||
|
||||
se::SHA1Sum::Hash hash = {0};
|
||||
se::SHA1Sum s;
|
||||
s.update(reinterpret_cast<const unsigned char*>(&input[0]), (uint32_t)input.size());
|
||||
s.finish(hash);
|
||||
|
||||
node::base64_encode((char*)hash, sizeof(hash), *buffer, sizeof(*buffer));
|
||||
}
|
||||
|
||||
static int header_value_cb(http_parser* parser, const char* at, size_t length) {
|
||||
static const char SEC_WEBSOCKET_KEY_HEADER[] = "Sec-WebSocket-Key";
|
||||
auto inspector = static_cast<InspectorSocket*>(parser->data);
|
||||
auto state = inspector->http_parsing_state;
|
||||
state->parsing_value = true;
|
||||
if (state->current_header.size() == sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1 &&
|
||||
node::StringEqualNoCaseN(state->current_header.data(),
|
||||
SEC_WEBSOCKET_KEY_HEADER,
|
||||
sizeof(SEC_WEBSOCKET_KEY_HEADER) - 1)) {
|
||||
state->ws_key.append(at, length);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int header_field_cb(http_parser* parser, const char* at, size_t length) {
|
||||
auto inspector = static_cast<InspectorSocket*>(parser->data);
|
||||
auto state = inspector->http_parsing_state;
|
||||
if (state->parsing_value) {
|
||||
state->parsing_value = false;
|
||||
state->current_header.clear();
|
||||
}
|
||||
state->current_header.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int path_cb(http_parser* parser, const char* at, size_t length) {
|
||||
auto inspector = static_cast<InspectorSocket*>(parser->data);
|
||||
auto state = inspector->http_parsing_state;
|
||||
state->path.append(at, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handshake_complete(InspectorSocket* inspector) {
|
||||
uv_read_stop(reinterpret_cast<uv_stream_t*>(&inspector->tcp));
|
||||
handshake_cb callback = inspector->http_parsing_state->callback;
|
||||
inspector->ws_state = new ws_state_s();
|
||||
inspector->ws_mode = true;
|
||||
callback(inspector, kInspectorHandshakeUpgraded,
|
||||
inspector->http_parsing_state->path);
|
||||
}
|
||||
|
||||
static void cleanup_http_parsing_state(InspectorSocket* inspector) {
|
||||
delete inspector->http_parsing_state;
|
||||
inspector->http_parsing_state = nullptr;
|
||||
}
|
||||
|
||||
static void report_handshake_failure_cb(uv_handle_t* handle) {
|
||||
dispose_inspector(handle);
|
||||
InspectorSocket* inspector = inspector_from_stream(handle);
|
||||
handshake_cb cb = inspector->http_parsing_state->callback;
|
||||
cleanup_http_parsing_state(inspector);
|
||||
cb(inspector, kInspectorHandshakeFailed, std::string());
|
||||
}
|
||||
|
||||
static void close_and_report_handshake_failure(InspectorSocket* inspector) {
|
||||
uv_handle_t* socket = reinterpret_cast<uv_handle_t*>(&inspector->tcp);
|
||||
if (uv_is_closing(socket)) {
|
||||
report_handshake_failure_cb(socket);
|
||||
} else {
|
||||
uv_read_stop(reinterpret_cast<uv_stream_t*>(socket));
|
||||
uv_close(socket, report_handshake_failure_cb);
|
||||
}
|
||||
}
|
||||
|
||||
static void then_close_and_report_failure(uv_write_t* req, int status) {
|
||||
InspectorSocket* inspector = WriteRequest::from_write_req(req)->inspector;
|
||||
write_request_cleanup(req, status);
|
||||
close_and_report_handshake_failure(inspector);
|
||||
}
|
||||
|
||||
static void handshake_failed(InspectorSocket* inspector) {
|
||||
const char HANDSHAKE_FAILED_RESPONSE[] =
|
||||
"HTTP/1.0 400 Bad Request\r\n"
|
||||
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
|
||||
"WebSockets request was expected\r\n";
|
||||
write_to_client(inspector, HANDSHAKE_FAILED_RESPONSE,
|
||||
sizeof(HANDSHAKE_FAILED_RESPONSE) - 1,
|
||||
then_close_and_report_failure);
|
||||
}
|
||||
|
||||
// init_handshake references message_complete_cb
|
||||
static void init_handshake(InspectorSocket* socket);
|
||||
|
||||
static int message_complete_cb(http_parser* parser) {
|
||||
InspectorSocket* inspector = static_cast<InspectorSocket*>(parser->data);
|
||||
struct http_parsing_state_s* state = inspector->http_parsing_state;
|
||||
if (parser->method != HTTP_GET) {
|
||||
handshake_failed(inspector);
|
||||
} else if (!parser->upgrade) {
|
||||
if (state->callback(inspector, kInspectorHandshakeHttpGet, state->path)) {
|
||||
init_handshake(inspector);
|
||||
} else {
|
||||
handshake_failed(inspector);
|
||||
}
|
||||
} else if (state->ws_key.empty()) {
|
||||
handshake_failed(inspector);
|
||||
} else if (state->callback(inspector, kInspectorHandshakeUpgrading,
|
||||
state->path)) {
|
||||
char accept_string[ACCEPT_KEY_LENGTH];
|
||||
generate_accept_string(state->ws_key, &accept_string);
|
||||
const char accept_ws_prefix[] = "HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Upgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Sec-WebSocket-Accept: ";
|
||||
const char accept_ws_suffix[] = "\r\n\r\n";
|
||||
std::string reply(accept_ws_prefix, sizeof(accept_ws_prefix) - 1);
|
||||
reply.append(accept_string, sizeof(accept_string));
|
||||
reply.append(accept_ws_suffix, sizeof(accept_ws_suffix) - 1);
|
||||
if (write_to_client(inspector, &reply[0], reply.size()) >= 0) {
|
||||
handshake_complete(inspector);
|
||||
inspector->http_parsing_state->done = true;
|
||||
} else {
|
||||
close_and_report_handshake_failure(inspector);
|
||||
}
|
||||
} else {
|
||||
handshake_failed(inspector);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void data_received_cb(uv_stream_s* tcp, ssize_t nread,
|
||||
const uv_buf_t* buf) {
|
||||
#if DUMP_READS
|
||||
if (nread >= 0) {
|
||||
printf("%s (%ld bytes)\n", __FUNCTION__, nread);
|
||||
dump_hex(buf->base, nread);
|
||||
} else {
|
||||
printf("[%s:%d] %s\n", __FUNCTION__, __LINE__, uv_err_name(nread));
|
||||
}
|
||||
#endif
|
||||
InspectorSocket* inspector = inspector_from_stream(tcp);
|
||||
reclaim_uv_buf(inspector, buf, nread);
|
||||
if (nread < 0 || nread == UV_EOF) {
|
||||
close_and_report_handshake_failure(inspector);
|
||||
} else {
|
||||
http_parsing_state_s* state = inspector->http_parsing_state;
|
||||
http_parser* parser = &state->parser;
|
||||
http_parser_execute(parser, &state->parser_settings,
|
||||
inspector->buffer.data(), nread);
|
||||
remove_from_beginning(&inspector->buffer, nread);
|
||||
if (parser->http_errno != HPE_OK) {
|
||||
handshake_failed(inspector);
|
||||
}
|
||||
if (inspector->http_parsing_state->done) {
|
||||
cleanup_http_parsing_state(inspector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_handshake(InspectorSocket* socket) {
|
||||
http_parsing_state_s* state = socket->http_parsing_state;
|
||||
CHECK_NE(state, nullptr);
|
||||
state->current_header.clear();
|
||||
state->ws_key.clear();
|
||||
state->path.clear();
|
||||
state->done = false;
|
||||
http_parser_init(&state->parser, HTTP_REQUEST);
|
||||
state->parser.data = socket;
|
||||
http_parser_settings* settings = &state->parser_settings;
|
||||
http_parser_settings_init(settings);
|
||||
settings->on_header_field = header_field_cb;
|
||||
settings->on_header_value = header_value_cb;
|
||||
settings->on_message_complete = message_complete_cb;
|
||||
settings->on_url = path_cb;
|
||||
}
|
||||
|
||||
int inspector_accept(uv_stream_t* server, InspectorSocket* socket,
|
||||
handshake_cb callback) {
|
||||
CHECK_NE(callback, nullptr);
|
||||
CHECK_EQ(socket->http_parsing_state, nullptr);
|
||||
|
||||
socket->http_parsing_state = new http_parsing_state_s();
|
||||
uv_stream_t* tcp = reinterpret_cast<uv_stream_t*>(&socket->tcp);
|
||||
int err = uv_tcp_init(server->loop, &socket->tcp);
|
||||
|
||||
if (err == 0) {
|
||||
err = uv_accept(server, tcp);
|
||||
}
|
||||
if (err == 0) {
|
||||
init_handshake(socket);
|
||||
socket->http_parsing_state->callback = callback;
|
||||
err = uv_read_start(tcp, prepare_buffer,
|
||||
data_received_cb);
|
||||
}
|
||||
if (err != 0) {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(tcp), NULL);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void inspector_write(InspectorSocket* inspector, const char* data,
|
||||
size_t len) {
|
||||
if (inspector->ws_mode) {
|
||||
std::vector<char> output = encode_frame_hybi17(data, len);
|
||||
write_to_client(inspector, &output[0], output.size());
|
||||
} else {
|
||||
write_to_client(inspector, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void inspector_close(InspectorSocket* inspector,
|
||||
inspector_cb callback) {
|
||||
// libuv throws assertions when closing stream that's already closed - we
|
||||
// need to do the same.
|
||||
CHECK(!uv_is_closing(reinterpret_cast<uv_handle_t*>(&inspector->tcp)));
|
||||
CHECK(!inspector->shutting_down);
|
||||
inspector->shutting_down = true;
|
||||
inspector->ws_state->close_cb = callback;
|
||||
if (inspector->connection_eof) {
|
||||
close_connection(inspector);
|
||||
} else {
|
||||
inspector_read_stop(inspector);
|
||||
write_to_client(inspector, CLOSE_FRAME, sizeof(CLOSE_FRAME),
|
||||
on_close_frame_written);
|
||||
inspector_read_start(inspector, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
bool inspector_is_active(const InspectorSocket* inspector) {
|
||||
const uv_handle_t* tcp =
|
||||
reinterpret_cast<const uv_handle_t*>(&inspector->tcp);
|
||||
return !inspector->shutting_down && !uv_is_closing(tcp);
|
||||
}
|
||||
|
||||
void InspectorSocket::reinit() {
|
||||
http_parsing_state = nullptr;
|
||||
ws_state = nullptr;
|
||||
buffer.clear();
|
||||
ws_mode = false;
|
||||
shutting_down = false;
|
||||
connection_eof = false;
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,105 @@
|
||||
#ifndef SRC_INSPECTOR_SOCKET_H_
|
||||
#define SRC_INSPECTOR_SOCKET_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "http_parser.h"
|
||||
#include "util.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
enum inspector_handshake_event {
|
||||
kInspectorHandshakeUpgrading,
|
||||
kInspectorHandshakeUpgraded,
|
||||
kInspectorHandshakeHttpGet,
|
||||
kInspectorHandshakeFailed
|
||||
};
|
||||
|
||||
class InspectorSocket;
|
||||
|
||||
typedef void (*inspector_cb)(InspectorSocket*, int);
|
||||
// Notifies as handshake is progressing. Returning false as a response to
|
||||
// kInspectorHandshakeUpgrading or kInspectorHandshakeHttpGet event will abort
|
||||
// the connection. inspector_write can be used from the callback.
|
||||
typedef bool (*handshake_cb)(InspectorSocket*,
|
||||
enum inspector_handshake_event state,
|
||||
const std::string& path);
|
||||
|
||||
struct http_parsing_state_s {
|
||||
http_parser parser;
|
||||
http_parser_settings parser_settings;
|
||||
handshake_cb callback;
|
||||
bool done;
|
||||
bool parsing_value;
|
||||
std::string ws_key;
|
||||
std::string path;
|
||||
std::string current_header;
|
||||
};
|
||||
|
||||
struct ws_state_s {
|
||||
uv_alloc_cb alloc_cb;
|
||||
uv_read_cb read_cb;
|
||||
inspector_cb close_cb;
|
||||
bool close_sent;
|
||||
bool received_close;
|
||||
};
|
||||
|
||||
// HTTP Wrapper around a uv_tcp_t
|
||||
class InspectorSocket {
|
||||
public:
|
||||
InspectorSocket() : data(nullptr), http_parsing_state(nullptr),
|
||||
ws_state(nullptr), buffer(0), ws_mode(false),
|
||||
shutting_down(false), connection_eof(false) { }
|
||||
void reinit();
|
||||
void* data;
|
||||
struct http_parsing_state_s* http_parsing_state;
|
||||
struct ws_state_s* ws_state;
|
||||
std::vector<char> buffer;
|
||||
uv_tcp_t tcp;
|
||||
bool ws_mode;
|
||||
bool shutting_down;
|
||||
bool connection_eof;
|
||||
|
||||
private:
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(InspectorSocket);
|
||||
};
|
||||
|
||||
int inspector_accept(uv_stream_t* server, InspectorSocket* inspector,
|
||||
handshake_cb callback);
|
||||
|
||||
void inspector_close(InspectorSocket* inspector,
|
||||
inspector_cb callback);
|
||||
|
||||
// Callbacks will receive stream handles. Use inspector_from_stream to get
|
||||
// InspectorSocket* from the stream handle.
|
||||
int inspector_read_start(InspectorSocket* inspector, uv_alloc_cb,
|
||||
uv_read_cb);
|
||||
void inspector_read_stop(InspectorSocket* inspector);
|
||||
void inspector_write(InspectorSocket* inspector,
|
||||
const char* data, size_t len);
|
||||
bool inspector_is_active(const InspectorSocket* inspector);
|
||||
|
||||
inline InspectorSocket* inspector_from_stream(uv_tcp_t* stream) {
|
||||
return node::ContainerOf(&InspectorSocket::tcp, stream);
|
||||
}
|
||||
|
||||
inline InspectorSocket* inspector_from_stream(uv_stream_t* stream) {
|
||||
return inspector_from_stream(reinterpret_cast<uv_tcp_t*>(stream));
|
||||
}
|
||||
|
||||
inline InspectorSocket* inspector_from_stream(uv_handle_t* stream) {
|
||||
return inspector_from_stream(reinterpret_cast<uv_tcp_t*>(stream));
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_INSPECTOR_SOCKET_H_
|
||||
@@ -0,0 +1,652 @@
|
||||
#include "inspector_socket_server.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "node.h"
|
||||
#include "uv.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
// Function is declared in inspector_io.h so the rest of the node does not
|
||||
// depend on inspector_socket_server.h
|
||||
std::string FormatWsAddress(const std::string& host, int port,
|
||||
const std::string& target_id,
|
||||
bool include_protocol) {
|
||||
// Host is valid (socket was bound) so colon means it's a v6 IP address
|
||||
bool v6 = host.find(':') != std::string::npos;
|
||||
std::ostringstream url;
|
||||
if (include_protocol)
|
||||
url << "ws://";
|
||||
if (v6) {
|
||||
url << '[';
|
||||
}
|
||||
url << host;
|
||||
if (v6) {
|
||||
url << ']';
|
||||
}
|
||||
url << ':' << port << '/' << target_id;
|
||||
return url.str();
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
static const uint8_t PROTOCOL_JSON[] = {
|
||||
#include "v8_inspector_protocol_json.h" // NOLINT(build/include_order)
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static std::string to_string(T arg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << arg;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void Escape(std::string* string) {
|
||||
for (char& c : *string) {
|
||||
c = (c == '\"' || c == '\\') ? '_' : c;
|
||||
}
|
||||
}
|
||||
|
||||
std::string MapToString(const std::map<std::string, std::string>& object) {
|
||||
bool first = true;
|
||||
std::ostringstream json;
|
||||
json << "{\n";
|
||||
for (const auto& name_value : object) {
|
||||
if (!first)
|
||||
json << ",\n";
|
||||
first = false;
|
||||
json << " \"" << name_value.first << "\": \"";
|
||||
json << name_value.second << "\"";
|
||||
}
|
||||
json << "\n} ";
|
||||
return json.str();
|
||||
}
|
||||
|
||||
std::string MapsToString(
|
||||
const std::vector<std::map<std::string, std::string>>& array) {
|
||||
bool first = true;
|
||||
std::ostringstream json;
|
||||
json << "[ ";
|
||||
for (const auto& object : array) {
|
||||
if (!first)
|
||||
json << ", ";
|
||||
first = false;
|
||||
json << MapToString(object);
|
||||
}
|
||||
json << "]\n\n";
|
||||
return json.str();
|
||||
}
|
||||
|
||||
const char* MatchPathSegment(const char* path, const char* expected) {
|
||||
size_t len = strlen(expected);
|
||||
if (StringEqualNoCaseN(path, expected, len)) {
|
||||
if (path[len] == '/') return path + len + 1;
|
||||
if (path[len] == '\0') return path + len;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OnBufferAlloc(uv_handle_t* handle, size_t len, uv_buf_t* buf) {
|
||||
buf->base = new char[len];
|
||||
buf->len = len;
|
||||
}
|
||||
|
||||
void PrintDebuggerReadyMessage(const std::string& host,
|
||||
int port,
|
||||
const std::vector<std::string>& ids,
|
||||
FILE* out) {
|
||||
if (out == NULL) {
|
||||
return;
|
||||
}
|
||||
for (const std::string& id : ids) {
|
||||
SE_LOGD("Debugger listening..., visit [ devtools://devtools/bundled/js_app.html?v8only=true&ws=%s ] in chrome browser to debug!\n",
|
||||
FormatWsAddress(host, port, id, false).c_str());
|
||||
}
|
||||
SE_LOGD("For help see %s\n",
|
||||
"https://nodejs.org/en/docs/inspector");
|
||||
}
|
||||
|
||||
void SendHttpResponse(InspectorSocket* socket, const std::string& response) {
|
||||
const char HEADERS[] = "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: application/json; charset=UTF-8\r\n"
|
||||
"Cache-Control: no-cache\r\n"
|
||||
"Content-Length: %zu\r\n"
|
||||
"\r\n";
|
||||
char header[sizeof(HEADERS) + 20];
|
||||
int header_len = snprintf(header, sizeof(header), HEADERS, response.size());
|
||||
inspector_write(socket, header, header_len);
|
||||
inspector_write(socket, response.data(), response.size());
|
||||
}
|
||||
|
||||
void SendVersionResponse(InspectorSocket* socket) {
|
||||
std::map<std::string, std::string> response;
|
||||
response["Browser"] = "Cocos2d-x Games"; //cjh
|
||||
response["Protocol-Version"] = "1.1";
|
||||
SendHttpResponse(socket, MapToString(response));
|
||||
}
|
||||
|
||||
void SendProtocolJson(InspectorSocket* socket) {
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
CHECK_EQ(Z_OK, inflateInit(&strm));
|
||||
static const size_t kDecompressedSize =
|
||||
PROTOCOL_JSON[0] * 0x10000u +
|
||||
PROTOCOL_JSON[1] * 0x100u +
|
||||
PROTOCOL_JSON[2];
|
||||
strm.next_in = const_cast<uint8_t*>(PROTOCOL_JSON + 3);
|
||||
strm.avail_in = sizeof(PROTOCOL_JSON) - 3;
|
||||
std::string data(kDecompressedSize, '\0');
|
||||
strm.next_out = reinterpret_cast<Byte*>(&data[0]);
|
||||
strm.avail_out = data.size();
|
||||
CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH));
|
||||
CHECK_EQ(0, strm.avail_out);
|
||||
CHECK_EQ(Z_OK, inflateEnd(&strm));
|
||||
SendHttpResponse(socket, data);
|
||||
}
|
||||
|
||||
int GetSocketHost(uv_tcp_t* socket, std::string* out_host) {
|
||||
char ip[INET6_ADDRSTRLEN];
|
||||
sockaddr_storage addr;
|
||||
int len = sizeof(addr);
|
||||
int err = uv_tcp_getsockname(socket,
|
||||
reinterpret_cast<struct sockaddr*>(&addr),
|
||||
&len);
|
||||
if (err != 0)
|
||||
return err;
|
||||
if (addr.ss_family == AF_INET6) {
|
||||
const sockaddr_in6* v6 = reinterpret_cast<const sockaddr_in6*>(&addr);
|
||||
err = uv_ip6_name(v6, ip, sizeof(ip));
|
||||
} else {
|
||||
const sockaddr_in* v4 = reinterpret_cast<const sockaddr_in*>(&addr);
|
||||
err = uv_ip4_name(v4, ip, sizeof(ip));
|
||||
}
|
||||
if (err != 0)
|
||||
return err;
|
||||
*out_host = ip;
|
||||
return err;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
class Closer {
|
||||
public:
|
||||
explicit Closer(InspectorSocketServer* server) : server_(server),
|
||||
close_count_(0) { }
|
||||
|
||||
void AddCallback(InspectorSocketServer::ServerCallback callback) {
|
||||
if (callback == nullptr)
|
||||
return;
|
||||
callbacks_.insert(callback);
|
||||
}
|
||||
|
||||
void DecreaseExpectedCount() {
|
||||
--close_count_;
|
||||
NotifyIfDone();
|
||||
}
|
||||
|
||||
void IncreaseExpectedCount() {
|
||||
++close_count_;
|
||||
}
|
||||
|
||||
void NotifyIfDone() {
|
||||
if (close_count_ == 0) {
|
||||
for (auto callback : callbacks_) {
|
||||
callback(server_);
|
||||
}
|
||||
InspectorSocketServer* server = server_;
|
||||
delete server->closer_;
|
||||
server->closer_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
InspectorSocketServer* server_;
|
||||
std::set<InspectorSocketServer::ServerCallback> callbacks_;
|
||||
int close_count_;
|
||||
};
|
||||
|
||||
class SocketSession {
|
||||
public:
|
||||
static int Accept(InspectorSocketServer* server, int server_port,
|
||||
uv_stream_t* server_socket);
|
||||
void Send(const std::string& message);
|
||||
void Close();
|
||||
|
||||
int id() const { return id_; }
|
||||
bool IsForTarget(const std::string& target_id) const {
|
||||
return target_id_ == target_id;
|
||||
}
|
||||
static int ServerPortForClient(InspectorSocket* client) {
|
||||
return From(client)->server_port_;
|
||||
}
|
||||
|
||||
private:
|
||||
SocketSession(InspectorSocketServer* server, int server_port);
|
||||
static SocketSession* From(InspectorSocket* socket) {
|
||||
return node::ContainerOf(&SocketSession::socket_, socket);
|
||||
}
|
||||
|
||||
enum class State { kHttp, kWebSocket, kClosing, kEOF, kDeclined };
|
||||
static bool HandshakeCallback(InspectorSocket* socket,
|
||||
enum inspector_handshake_event state,
|
||||
const std::string& path);
|
||||
static void ReadCallback(uv_stream_t* stream, ssize_t read,
|
||||
const uv_buf_t* buf);
|
||||
static void CloseCallback(InspectorSocket* socket, int code);
|
||||
|
||||
void FrontendConnected();
|
||||
void SetDeclined() { state_ = State::kDeclined; }
|
||||
void SetTargetId(const std::string& target_id) {
|
||||
CHECK(target_id_.empty());
|
||||
target_id_ = target_id;
|
||||
}
|
||||
|
||||
const int id_;
|
||||
InspectorSocket socket_;
|
||||
InspectorSocketServer* server_;
|
||||
std::string target_id_;
|
||||
State state_;
|
||||
const int server_port_;
|
||||
};
|
||||
|
||||
class ServerSocket {
|
||||
public:
|
||||
static int Listen(InspectorSocketServer* inspector_server,
|
||||
sockaddr* addr, uv_loop_t* loop);
|
||||
void Close() {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&tcp_socket_),
|
||||
SocketClosedCallback);
|
||||
}
|
||||
int port() const { return port_; }
|
||||
|
||||
private:
|
||||
explicit ServerSocket(InspectorSocketServer* server)
|
||||
: tcp_socket_(uv_tcp_t()), server_(server), port_(-1) {}
|
||||
template<typename UvHandle>
|
||||
static ServerSocket* FromTcpSocket(UvHandle* socket) {
|
||||
return node::ContainerOf(&ServerSocket::tcp_socket_,
|
||||
reinterpret_cast<uv_tcp_t*>(socket));
|
||||
}
|
||||
|
||||
static void SocketConnectedCallback(uv_stream_t* tcp_socket, int status);
|
||||
static void SocketClosedCallback(uv_handle_t* tcp_socket);
|
||||
static void FreeOnCloseCallback(uv_handle_t* tcp_socket_) {
|
||||
delete FromTcpSocket(tcp_socket_);
|
||||
}
|
||||
int DetectPort();
|
||||
|
||||
uv_tcp_t tcp_socket_;
|
||||
InspectorSocketServer* server_;
|
||||
int port_;
|
||||
};
|
||||
|
||||
InspectorSocketServer::InspectorSocketServer(SocketServerDelegate* delegate,
|
||||
uv_loop_t* loop,
|
||||
const std::string& host,
|
||||
int port,
|
||||
FILE* out) : loop_(loop),
|
||||
delegate_(delegate),
|
||||
host_(host),
|
||||
port_(port),
|
||||
closer_(nullptr),
|
||||
next_session_id_(0),
|
||||
out_(out) {
|
||||
state_ = ServerState::kNew;
|
||||
}
|
||||
|
||||
bool InspectorSocketServer::SessionStarted(SocketSession* session,
|
||||
const std::string& id) {
|
||||
if (TargetExists(id) && delegate_->StartSession(session->id(), id)) {
|
||||
connected_sessions_[session->id()] = session;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorSocketServer::SessionTerminated(SocketSession* session) {
|
||||
int id = session->id();
|
||||
if (connected_sessions_.erase(id) != 0) {
|
||||
delegate_->EndSession(id);
|
||||
if (connected_sessions_.empty()) {
|
||||
if (state_ == ServerState::kRunning && !server_sockets_.empty()) {
|
||||
PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
|
||||
delegate_->GetTargetIds(), out_);
|
||||
}
|
||||
if (state_ == ServerState::kStopped) {
|
||||
delegate_->ServerDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
delete session;
|
||||
}
|
||||
|
||||
bool InspectorSocketServer::HandleGetRequest(InspectorSocket* socket,
|
||||
const std::string& path) {
|
||||
const char* command = MatchPathSegment(path.c_str(), "/json");
|
||||
if (command == nullptr)
|
||||
return false;
|
||||
|
||||
if (MatchPathSegment(command, "list") || command[0] == '\0') {
|
||||
SendListResponse(socket);
|
||||
return true;
|
||||
} else if (MatchPathSegment(command, "protocol")) {
|
||||
SendProtocolJson(socket);
|
||||
return true;
|
||||
} else if (MatchPathSegment(command, "version")) {
|
||||
SendVersionResponse(socket);
|
||||
return true;
|
||||
} else if (const char* target_id = MatchPathSegment(command, "activate")) {
|
||||
if (TargetExists(target_id)) {
|
||||
SendHttpResponse(socket, "Target activated");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InspectorSocketServer::SendListResponse(InspectorSocket* socket) {
|
||||
std::vector<std::map<std::string, std::string>> response;
|
||||
for (const std::string& id : delegate_->GetTargetIds()) {
|
||||
response.push_back(std::map<std::string, std::string>());
|
||||
std::map<std::string, std::string>& target_map = response.back();
|
||||
target_map["description"] = "node.js instance";
|
||||
target_map["faviconUrl"] = "https://nodejs.org/static/favicon.ico";
|
||||
target_map["id"] = id;
|
||||
target_map["title"] = delegate_->GetTargetTitle(id);
|
||||
Escape(&target_map["title"]);
|
||||
target_map["type"] = "node";
|
||||
// This attribute value is a "best effort" URL that is passed as a JSON
|
||||
// string. It is not guaranteed to resolve to a valid resource.
|
||||
target_map["url"] = delegate_->GetTargetUrl(id);
|
||||
Escape(&target_map["url"]);
|
||||
|
||||
bool connected = false;
|
||||
for (const auto& session : connected_sessions_) {
|
||||
if (session.second->IsForTarget(id)) {
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!connected) {
|
||||
std::string host;
|
||||
int port = SocketSession::ServerPortForClient(socket);
|
||||
GetSocketHost(&socket->tcp, &host);
|
||||
std::ostringstream frontend_url;
|
||||
frontend_url << "devtools://devtools/bundled";
|
||||
frontend_url << "/js_app.html?experiments=true&v8only=true&ws=";
|
||||
frontend_url << FormatWsAddress(host, port, id, false);
|
||||
target_map["devtoolsFrontendUrl"] += frontend_url.str();
|
||||
target_map["webSocketDebuggerUrl"] =
|
||||
FormatWsAddress(host, port, id, true);
|
||||
}
|
||||
}
|
||||
SendHttpResponse(socket, MapsToString(response));
|
||||
}
|
||||
|
||||
bool InspectorSocketServer::Start() {
|
||||
CHECK_EQ(state_, ServerState::kNew);
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
uv_getaddrinfo_t req;
|
||||
const std::string port_string = to_string(port_);
|
||||
int err = uv_getaddrinfo(loop_, &req, nullptr, host_.c_str(),
|
||||
port_string.c_str(), &hints);
|
||||
if (err < 0) {
|
||||
SE_LOGE("Unable to resolve \"%s\": %s\n", host_.c_str(),
|
||||
uv_strerror(err));
|
||||
return false;
|
||||
}
|
||||
for (addrinfo* address = req.addrinfo; address != nullptr;
|
||||
address = address->ai_next) {
|
||||
err = ServerSocket::Listen(this, address->ai_addr, loop_);
|
||||
}
|
||||
uv_freeaddrinfo(req.addrinfo);
|
||||
|
||||
if (!connected_sessions_.empty()) {
|
||||
return true;
|
||||
}
|
||||
// We only show error if we failed to start server on all addresses. We only
|
||||
// show one error, for the last address.
|
||||
if (server_sockets_.empty()) {
|
||||
SE_LOGE("Starting inspector on %s:%d failed: %s\n",
|
||||
host_.c_str(), port_, uv_strerror(err));
|
||||
if(err == UV_EADDRINUSE) {
|
||||
SE_LOGE("[FATAL ERROR]: Port [:%s] is occupied by other processes, try to kill the previous debug process or change the port number in `jsb_enable_debugger`.\n", port_string.c_str());
|
||||
} else {
|
||||
SE_LOGE("[FATAL ERROR]: Failed to bind port [%s], error code: %d.\n", port_string.c_str(), err);
|
||||
}
|
||||
assert(false);//failed to start socket server for chrome debugger
|
||||
return false;
|
||||
}
|
||||
state_ = ServerState::kRunning;
|
||||
// getaddrinfo sorts the addresses, so the first port is most relevant.
|
||||
PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
|
||||
delegate_->GetTargetIds(), out_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void InspectorSocketServer::Stop(ServerCallback cb) {
|
||||
CHECK_EQ(state_, ServerState::kRunning);
|
||||
if (closer_ == nullptr) {
|
||||
closer_ = new Closer(this);
|
||||
}
|
||||
closer_->AddCallback(cb);
|
||||
closer_->IncreaseExpectedCount();
|
||||
state_ = ServerState::kStopping;
|
||||
for (ServerSocket* server_socket : server_sockets_)
|
||||
server_socket->Close();
|
||||
closer_->NotifyIfDone();
|
||||
}
|
||||
|
||||
void InspectorSocketServer::TerminateConnections() {
|
||||
for (const auto& session : connected_sessions_) {
|
||||
session.second->Close();
|
||||
}
|
||||
}
|
||||
|
||||
bool InspectorSocketServer::TargetExists(const std::string& id) {
|
||||
const std::vector<std::string>& target_ids = delegate_->GetTargetIds();
|
||||
const auto& found = std::find(target_ids.begin(), target_ids.end(), id);
|
||||
return found != target_ids.end();
|
||||
}
|
||||
|
||||
void InspectorSocketServer::Send(int session_id, const std::string& message) {
|
||||
auto session_iterator = connected_sessions_.find(session_id);
|
||||
if (session_iterator != connected_sessions_.end()) {
|
||||
session_iterator->second->Send(message);
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorSocketServer::ServerSocketListening(ServerSocket* server_socket) {
|
||||
server_sockets_.push_back(server_socket);
|
||||
}
|
||||
|
||||
void InspectorSocketServer::ServerSocketClosed(ServerSocket* server_socket) {
|
||||
CHECK_EQ(state_, ServerState::kStopping);
|
||||
|
||||
server_sockets_.erase(std::remove(server_sockets_.begin(),
|
||||
server_sockets_.end(), server_socket),
|
||||
server_sockets_.end());
|
||||
if (!server_sockets_.empty())
|
||||
return;
|
||||
|
||||
if (closer_ != nullptr) {
|
||||
closer_->DecreaseExpectedCount();
|
||||
}
|
||||
if (connected_sessions_.empty()) {
|
||||
delegate_->ServerDone();
|
||||
}
|
||||
state_ = ServerState::kStopped;
|
||||
}
|
||||
|
||||
int InspectorSocketServer::Port() const {
|
||||
if (!server_sockets_.empty()) {
|
||||
return server_sockets_[0]->port();
|
||||
}
|
||||
return port_;
|
||||
}
|
||||
|
||||
// InspectorSession tracking
|
||||
SocketSession::SocketSession(InspectorSocketServer* server, int server_port)
|
||||
: id_(server->GenerateSessionId()),
|
||||
server_(server),
|
||||
state_(State::kHttp),
|
||||
server_port_(server_port) { }
|
||||
|
||||
void SocketSession::Close() {
|
||||
CHECK_NE(state_, State::kClosing);
|
||||
state_ = State::kClosing;
|
||||
inspector_close(&socket_, CloseCallback);
|
||||
}
|
||||
|
||||
// static
|
||||
int SocketSession::Accept(InspectorSocketServer* server, int server_port,
|
||||
uv_stream_t* server_socket) {
|
||||
// Memory is freed when the socket closes.
|
||||
SocketSession* session = new SocketSession(server, server_port);
|
||||
int err = inspector_accept(server_socket, &session->socket_,
|
||||
HandshakeCallback);
|
||||
if (err != 0) {
|
||||
delete session;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SocketSession::HandshakeCallback(InspectorSocket* socket,
|
||||
inspector_handshake_event event,
|
||||
const std::string& path) {
|
||||
SocketSession* session = SocketSession::From(socket);
|
||||
InspectorSocketServer* server = session->server_;
|
||||
const std::string& id = path.empty() ? path : path.substr(1);
|
||||
switch (event) {
|
||||
case kInspectorHandshakeHttpGet:
|
||||
return server->HandleGetRequest(socket, path);
|
||||
case kInspectorHandshakeUpgrading:
|
||||
if (server->SessionStarted(session, id)) {
|
||||
session->SetTargetId(id);
|
||||
return true;
|
||||
} else {
|
||||
session->SetDeclined();
|
||||
return false;
|
||||
}
|
||||
case kInspectorHandshakeUpgraded:
|
||||
session->FrontendConnected();
|
||||
return true;
|
||||
case kInspectorHandshakeFailed:
|
||||
server->SessionTerminated(session);
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void SocketSession::CloseCallback(InspectorSocket* socket, int code) {
|
||||
SocketSession* session = SocketSession::From(socket);
|
||||
CHECK_EQ(State::kClosing, session->state_);
|
||||
session->server_->SessionTerminated(session);
|
||||
}
|
||||
|
||||
void SocketSession::FrontendConnected() {
|
||||
CHECK_EQ(State::kHttp, state_);
|
||||
state_ = State::kWebSocket;
|
||||
inspector_read_start(&socket_, OnBufferAlloc, ReadCallback);
|
||||
}
|
||||
|
||||
// static
|
||||
void SocketSession::ReadCallback(uv_stream_t* stream, ssize_t read,
|
||||
const uv_buf_t* buf) {
|
||||
InspectorSocket* socket = inspector_from_stream(stream);
|
||||
SocketSession* session = SocketSession::From(socket);
|
||||
if (read > 0) {
|
||||
session->server_->MessageReceived(session->id_,
|
||||
std::string(buf->base, read));
|
||||
} else {
|
||||
session->Close();
|
||||
}
|
||||
if (buf != nullptr && buf->base != nullptr)
|
||||
delete[] buf->base;
|
||||
}
|
||||
|
||||
void SocketSession::Send(const std::string& message) {
|
||||
inspector_write(&socket_, message.data(), message.length());
|
||||
}
|
||||
|
||||
// ServerSocket implementation
|
||||
int ServerSocket::DetectPort() {
|
||||
sockaddr_storage addr;
|
||||
int len = sizeof(addr);
|
||||
int err = uv_tcp_getsockname(&tcp_socket_,
|
||||
reinterpret_cast<struct sockaddr*>(&addr), &len);
|
||||
if (err != 0)
|
||||
return err;
|
||||
int port;
|
||||
if (addr.ss_family == AF_INET6)
|
||||
port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
|
||||
else
|
||||
port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
|
||||
port_ = ntohs(port);
|
||||
return err;
|
||||
}
|
||||
|
||||
// static
|
||||
int ServerSocket::Listen(InspectorSocketServer* inspector_server,
|
||||
sockaddr* addr, uv_loop_t* loop) {
|
||||
ServerSocket* server_socket = new ServerSocket(inspector_server);
|
||||
uv_tcp_t* server = &server_socket->tcp_socket_;
|
||||
CHECK_EQ(0, uv_tcp_init(loop, server));
|
||||
int err = uv_tcp_bind(server, addr, 0);
|
||||
if (err == 0) {
|
||||
err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 1,
|
||||
ServerSocket::SocketConnectedCallback);
|
||||
}
|
||||
if (err == 0) {
|
||||
err = server_socket->DetectPort();
|
||||
}
|
||||
if (err == 0) {
|
||||
inspector_server->ServerSocketListening(server_socket);
|
||||
} else {
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(server), FreeOnCloseCallback);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// static
|
||||
void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket,
|
||||
int status) {
|
||||
if (status == 0) {
|
||||
ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
|
||||
// Memory is freed when the socket closes.
|
||||
SocketSession::Accept(server_socket->server_, server_socket->port_,
|
||||
tcp_socket);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void ServerSocket::SocketClosedCallback(uv_handle_t* tcp_socket) {
|
||||
ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
|
||||
server_socket->server_->ServerSocketClosed(server_socket);
|
||||
delete server_socket;
|
||||
}
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,104 @@
|
||||
#ifndef SRC_INSPECTOR_SOCKET_SERVER_H_
|
||||
#define SRC_INSPECTOR_SOCKET_SERVER_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "inspector_agent.h"
|
||||
#include "inspector_socket.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if !HAVE_INSPECTOR
|
||||
#error("This header can only be used when inspector is enabled")
|
||||
#endif
|
||||
|
||||
namespace node {
|
||||
namespace inspector {
|
||||
|
||||
class Closer;
|
||||
class SocketSession;
|
||||
class ServerSocket;
|
||||
|
||||
class SocketServerDelegate {
|
||||
public:
|
||||
virtual bool StartSession(int session_id, const std::string& target_id) = 0;
|
||||
virtual void EndSession(int session_id) = 0;
|
||||
virtual void MessageReceived(int session_id, const std::string& message) = 0;
|
||||
virtual std::vector<std::string> GetTargetIds() = 0;
|
||||
virtual std::string GetTargetTitle(const std::string& id) = 0;
|
||||
virtual std::string GetTargetUrl(const std::string& id) = 0;
|
||||
virtual void ServerDone() = 0;
|
||||
};
|
||||
|
||||
// HTTP Server, writes messages requested as TransportActions, and responds
|
||||
// to HTTP requests and WS upgrades.
|
||||
|
||||
|
||||
|
||||
class InspectorSocketServer {
|
||||
public:
|
||||
using ServerCallback = void (*)(InspectorSocketServer*);
|
||||
InspectorSocketServer(SocketServerDelegate* delegate,
|
||||
uv_loop_t* loop,
|
||||
const std::string& host,
|
||||
int port,
|
||||
FILE* out = stderr);
|
||||
// Start listening on host/port
|
||||
bool Start();
|
||||
|
||||
// Called by the TransportAction sent with InspectorIo::Write():
|
||||
// kKill and kStop
|
||||
void Stop(ServerCallback callback);
|
||||
// kSendMessage
|
||||
void Send(int session_id, const std::string& message);
|
||||
// kKill
|
||||
void TerminateConnections();
|
||||
|
||||
int Port() const;
|
||||
|
||||
// Server socket lifecycle. There may be multiple sockets
|
||||
void ServerSocketListening(ServerSocket* server_socket);
|
||||
void ServerSocketClosed(ServerSocket* server_socket);
|
||||
|
||||
// Session connection lifecycle
|
||||
bool HandleGetRequest(InspectorSocket* socket, const std::string& path);
|
||||
bool SessionStarted(SocketSession* session, const std::string& id);
|
||||
void SessionTerminated(SocketSession* session);
|
||||
void MessageReceived(int session_id, const std::string& message) {
|
||||
delegate_->MessageReceived(session_id, message);
|
||||
}
|
||||
|
||||
int GenerateSessionId() {
|
||||
return next_session_id_++;
|
||||
}
|
||||
|
||||
private:
|
||||
void SendListResponse(InspectorSocket* socket);
|
||||
bool TargetExists(const std::string& id);
|
||||
|
||||
enum class ServerState {kNew, kRunning, kStopping, kStopped};
|
||||
uv_loop_t* loop_;
|
||||
SocketServerDelegate* const delegate_;
|
||||
const std::string host_;
|
||||
int port_;
|
||||
std::string path_;
|
||||
std::vector<ServerSocket*> server_sockets_;
|
||||
Closer* closer_;
|
||||
std::map<int, SocketSession*> connected_sessions_;
|
||||
int next_session_id_;
|
||||
FILE* out_;
|
||||
ServerState state_;
|
||||
|
||||
friend class Closer;
|
||||
};
|
||||
|
||||
} // namespace inspector
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_INSPECTOR_SOCKET_SERVER_H_
|
||||
1138
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/debugger/node.cc
Normal file
1138
cocos2d-x/cocos/scripting/js-bindings/jswrapper/v8/debugger/node.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifndef BUILDING_NODE_EXTENSION
|
||||
# define NODE_EXTERN __declspec(dllexport)
|
||||
# else
|
||||
# define NODE_EXTERN __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define NODE_EXTERN /* nothing */
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef NODE_STRINGIFY
|
||||
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
|
||||
#define NODE_STRINGIFY_HELPER(n) #n
|
||||
#endif
|
||||
|
||||
// The arraysize(arr) macro returns the # of elements in an array arr.
|
||||
// The expression is a compile-time constant, and therefore can be
|
||||
// used in defining new arrays, for example. If you use arraysize on
|
||||
// a pointer by mistake, you will get a compile-time error.
|
||||
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
|
||||
|
||||
|
||||
// This template function declaration is used in defining arraysize.
|
||||
// Note that the function doesn't need an implementation, as we only
|
||||
// use its type.
|
||||
template <typename T, size_t N>
|
||||
char (&ArraySizeHelper(T (&array)[N]))[N];
|
||||
|
||||
|
||||
#if !V8_CC_MSVC
|
||||
// That gcc wants both of these prototypes seems mysterious. VC, for
|
||||
// its part, can't decide which to use (another mystery). Matching of
|
||||
// template overloads: the final frontier.
|
||||
template <typename T, size_t N>
|
||||
char (&ArraySizeHelper(const T (&array)[N]))[N];
|
||||
#endif
|
||||
|
||||
#ifdef __POSIX__
|
||||
void RegisterSignalHandler(int signal,
|
||||
void (*handler)(int signal),
|
||||
bool reset_handler = false);
|
||||
#endif // __POSIX__
|
||||
|
||||
namespace node {
|
||||
|
||||
|
||||
|
||||
NODE_EXTERN v8::Local<v8::Value> ErrnoException(v8::Isolate* isolate,
|
||||
int errorno,
|
||||
const char* syscall = NULL,
|
||||
const char* message = NULL,
|
||||
const char* path = NULL);
|
||||
NODE_EXTERN v8::Local<v8::Value> UVException(v8::Isolate* isolate,
|
||||
int errorno,
|
||||
const char* syscall = NULL,
|
||||
const char* message = NULL,
|
||||
const char* path = NULL);
|
||||
NODE_EXTERN v8::Local<v8::Value> UVException(v8::Isolate* isolate,
|
||||
int errorno,
|
||||
const char* syscall,
|
||||
const char* message,
|
||||
const char* path,
|
||||
const char* dest);
|
||||
|
||||
|
||||
typedef double async_id;
|
||||
struct async_context {
|
||||
::node::async_id async_id;
|
||||
::node::async_id trigger_async_id;
|
||||
};
|
||||
|
||||
/* An API specific to emit before/after callbacks is unnecessary because
|
||||
* MakeCallback will automatically call them for you.
|
||||
*
|
||||
* These methods may create handles on their own, so run them inside a
|
||||
* HandleScope.
|
||||
*
|
||||
* `asyncId` and `triggerAsyncId` should correspond to the values returned by
|
||||
* `EmitAsyncInit()` and `AsyncHooksGetTriggerAsyncId()`, respectively, when the
|
||||
* invoking resource was created. If these values are unknown, 0 can be passed.
|
||||
* */
|
||||
|
||||
v8::MaybeLocal<v8::Value> MakeCallback(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
v8::Local<v8::Function> callback,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv,
|
||||
async_context asyncContext);
|
||||
|
||||
v8::MaybeLocal<v8::Value> MakeCallback(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
const char* method,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv,
|
||||
async_context asyncContext);
|
||||
|
||||
v8::MaybeLocal<v8::Value> MakeCallback(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
v8::Local<v8::String> symbol,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv,
|
||||
async_context asyncContext);
|
||||
|
||||
/*
|
||||
* These methods need to be called in a HandleScope.
|
||||
*
|
||||
* It is preferred that you use the `MakeCallback` overloads taking
|
||||
* `async_id` arguments.
|
||||
*/
|
||||
|
||||
v8::Local<v8::Value> MakeCallback(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
const char* method,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
v8::Local<v8::Value> MakeCallback(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
v8::Local<v8::String> symbol,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
v8::Local<v8::Value> MakeCallback(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> recv,
|
||||
v8::Local<v8::Function> callback,
|
||||
int argc,
|
||||
v8::Local<v8::Value>* argv);
|
||||
|
||||
class IsolateData;
|
||||
class Environment;
|
||||
|
||||
NODE_EXTERN IsolateData* CreateIsolateData(v8::Isolate* isolate,
|
||||
struct uv_loop_s* loop);
|
||||
NODE_EXTERN void FreeIsolateData(IsolateData* isolate_data);
|
||||
|
||||
NODE_EXTERN Environment* CreateEnvironment(IsolateData* isolate_data,
|
||||
v8::Local<v8::Context> context,
|
||||
int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv);
|
||||
NODE_EXTERN void FreeEnvironment(Environment* env);
|
||||
|
||||
void SetupProcessObject(Environment* env,
|
||||
int argc,
|
||||
const char* const* argv,
|
||||
int exec_argc,
|
||||
const char* const* exec_argv);
|
||||
|
||||
} // namespace node {
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,141 @@
|
||||
#include "node_debug_options.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
namespace {
|
||||
const int default_inspector_port = 9229;
|
||||
|
||||
inline std::string remove_brackets(const std::string& host) {
|
||||
if (!host.empty() && host.front() == '[' && host.back() == ']')
|
||||
return host.substr(1, host.size() - 2);
|
||||
else
|
||||
return host;
|
||||
}
|
||||
|
||||
int parse_and_validate_port(const std::string& port) {
|
||||
char* endptr;
|
||||
errno = 0;
|
||||
const long result = strtol(port.c_str(), &endptr, 10); // NOLINT(runtime/int)
|
||||
if (errno != 0 || *endptr != '\0'||
|
||||
(result != 0 && result < 1024) || result > 65535) {
|
||||
SE_LOGE("Debug port must be 0 or in range 1024 to 65535.\n");
|
||||
exit(12);
|
||||
}
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
std::pair<std::string, int> split_host_port(const std::string& arg) {
|
||||
// remove_brackets only works if no port is specified
|
||||
// so if it has an effect only an IPv6 address was specified
|
||||
std::string host = remove_brackets(arg);
|
||||
if (host.length() < arg.length())
|
||||
return {host, -1};
|
||||
|
||||
size_t colon = arg.rfind(':');
|
||||
if (colon == std::string::npos) {
|
||||
// Either a port number or a host name. Assume that
|
||||
// if it's not all decimal digits, it's a host name.
|
||||
for (char c : arg) {
|
||||
if (c < '0' || c > '9') {
|
||||
return {arg, -1};
|
||||
}
|
||||
}
|
||||
return {"", parse_and_validate_port(arg)};
|
||||
}
|
||||
// host and port found
|
||||
return std::make_pair(remove_brackets(arg.substr(0, colon)),
|
||||
parse_and_validate_port(arg.substr(colon + 1)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DebugOptions::DebugOptions() :
|
||||
inspector_enabled_(false),
|
||||
deprecated_debug_(false),
|
||||
break_first_line_(false),
|
||||
host_name_("127.0.0.1"), port_(-1) { }
|
||||
|
||||
bool DebugOptions::ParseOption(const char* argv0, const std::string& option) {
|
||||
bool has_argument = false;
|
||||
std::string option_name;
|
||||
std::string argument;
|
||||
|
||||
auto pos = option.find("=");
|
||||
if (pos == std::string::npos) {
|
||||
option_name = option;
|
||||
} else {
|
||||
option_name = option.substr(0, pos);
|
||||
argument = option.substr(pos + 1);
|
||||
|
||||
if (argument.length() > 0)
|
||||
has_argument = true;
|
||||
else
|
||||
argument.clear();
|
||||
}
|
||||
|
||||
// Note that --debug-port and --debug-brk in conjunction with --inspect
|
||||
// work but are undocumented.
|
||||
// --debug is no longer valid.
|
||||
// Ref: https://github.com/nodejs/node/issues/12630
|
||||
// Ref: https://github.com/nodejs/node/pull/12949
|
||||
if (option_name == "--inspect") {
|
||||
inspector_enabled_ = true;
|
||||
} else if (option_name == "--debug") {
|
||||
deprecated_debug_ = true;
|
||||
} else if (option_name == "--inspect-brk") {
|
||||
inspector_enabled_ = true;
|
||||
break_first_line_ = true;
|
||||
} else if (option_name == "--debug-brk") {
|
||||
break_first_line_ = true;
|
||||
deprecated_debug_ = true;
|
||||
} else if (option_name == "--debug-port" ||
|
||||
option_name == "--inspect-port") {
|
||||
if (!has_argument) {
|
||||
SE_LOGE("%s: %s requires an argument\n",
|
||||
argv0, option.c_str());
|
||||
exit(9);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !HAVE_INSPECTOR
|
||||
if (inspector_enabled_) {
|
||||
SE_LOGE("Inspector support is not available with this Node.js build\n");
|
||||
}
|
||||
inspector_enabled_ = false;
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// argument can be specified for *any* option to specify host:port
|
||||
if (has_argument) {
|
||||
std::pair<std::string, int> host_port = split_host_port(argument);
|
||||
if (!host_port.first.empty()) {
|
||||
host_name_ = host_port.first;
|
||||
}
|
||||
if (host_port.second >= 0) {
|
||||
port_ = host_port.second;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int DebugOptions::port() const {
|
||||
int port = port_;
|
||||
if (port < 0) {
|
||||
port = default_inspector_port;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef SRC_NODE_DEBUG_OPTIONS_H_
|
||||
#define SRC_NODE_DEBUG_OPTIONS_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include <string>
|
||||
|
||||
// Forward declaration to break recursive dependency chain with src/env.h.
|
||||
namespace node {
|
||||
|
||||
class DebugOptions {
|
||||
public:
|
||||
DebugOptions();
|
||||
bool ParseOption(const char* argv0, const std::string& option);
|
||||
void set_inspector_enabled(bool enabled) { inspector_enabled_ = enabled; }
|
||||
bool inspector_enabled() const { return inspector_enabled_; }
|
||||
bool deprecated_invocation() const {
|
||||
return deprecated_debug_ &&
|
||||
inspector_enabled_ &&
|
||||
break_first_line_;
|
||||
}
|
||||
bool invalid_invocation() const {
|
||||
return deprecated_debug_ && !inspector_enabled_;
|
||||
}
|
||||
void set_wait_for_connect(bool wait) { break_first_line_ = wait; }
|
||||
bool wait_for_connect() const { return break_first_line_; }
|
||||
std::string host_name() const { return host_name_; }
|
||||
void set_host_name(std::string host_name) { host_name_ = host_name; }
|
||||
int port() const;
|
||||
void set_port(int port) { port_ = port; }
|
||||
|
||||
private:
|
||||
bool inspector_enabled_;
|
||||
bool deprecated_debug_;
|
||||
bool break_first_line_;
|
||||
std::string host_name_;
|
||||
int port_;
|
||||
};
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_NODE_DEBUG_OPTIONS_H_
|
||||
@@ -0,0 +1,192 @@
|
||||
#ifndef SRC_NODE_MUTEX_H_
|
||||
#define SRC_NODE_MUTEX_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#include "util.h"
|
||||
#include "uv.h"
|
||||
|
||||
namespace node {
|
||||
|
||||
template <typename Traits> class ConditionVariableBase;
|
||||
template <typename Traits> class MutexBase;
|
||||
struct LibuvMutexTraits;
|
||||
|
||||
using ConditionVariable = ConditionVariableBase<LibuvMutexTraits>;
|
||||
using Mutex = MutexBase<LibuvMutexTraits>;
|
||||
|
||||
template <typename Traits>
|
||||
class MutexBase {
|
||||
public:
|
||||
inline MutexBase();
|
||||
inline ~MutexBase();
|
||||
inline void Lock();
|
||||
inline void Unlock();
|
||||
|
||||
class ScopedLock;
|
||||
class ScopedUnlock;
|
||||
|
||||
class ScopedLock {
|
||||
public:
|
||||
inline explicit ScopedLock(const MutexBase& mutex);
|
||||
inline explicit ScopedLock(const ScopedUnlock& scoped_unlock);
|
||||
inline ~ScopedLock();
|
||||
|
||||
private:
|
||||
template <typename> friend class ConditionVariableBase;
|
||||
friend class ScopedUnlock;
|
||||
const MutexBase& mutex_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(ScopedLock);
|
||||
};
|
||||
|
||||
class ScopedUnlock {
|
||||
public:
|
||||
inline explicit ScopedUnlock(const ScopedLock& scoped_lock);
|
||||
inline ~ScopedUnlock();
|
||||
|
||||
private:
|
||||
friend class ScopedLock;
|
||||
const MutexBase& mutex_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(ScopedUnlock);
|
||||
};
|
||||
|
||||
private:
|
||||
template <typename> friend class ConditionVariableBase;
|
||||
mutable typename Traits::MutexT mutex_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(MutexBase);
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
class ConditionVariableBase {
|
||||
public:
|
||||
using ScopedLock = typename MutexBase<Traits>::ScopedLock;
|
||||
|
||||
inline ConditionVariableBase();
|
||||
inline ~ConditionVariableBase();
|
||||
inline void Broadcast(const ScopedLock&);
|
||||
inline void Signal(const ScopedLock&);
|
||||
inline void Wait(const ScopedLock& scoped_lock);
|
||||
|
||||
private:
|
||||
typename Traits::CondT cond_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(ConditionVariableBase);
|
||||
};
|
||||
|
||||
struct LibuvMutexTraits {
|
||||
using CondT = uv_cond_t;
|
||||
using MutexT = uv_mutex_t;
|
||||
|
||||
static inline int cond_init(CondT* cond) {
|
||||
return uv_cond_init(cond);
|
||||
}
|
||||
|
||||
static inline int mutex_init(MutexT* mutex) {
|
||||
return uv_mutex_init(mutex);
|
||||
}
|
||||
|
||||
static inline void cond_broadcast(CondT* cond) {
|
||||
uv_cond_broadcast(cond);
|
||||
}
|
||||
|
||||
static inline void cond_destroy(CondT* cond) {
|
||||
uv_cond_destroy(cond);
|
||||
}
|
||||
|
||||
static inline void cond_signal(CondT* cond) {
|
||||
uv_cond_signal(cond);
|
||||
}
|
||||
|
||||
static inline void cond_wait(CondT* cond, MutexT* mutex) {
|
||||
uv_cond_wait(cond, mutex);
|
||||
}
|
||||
|
||||
static inline void mutex_destroy(MutexT* mutex) {
|
||||
uv_mutex_destroy(mutex);
|
||||
}
|
||||
|
||||
static inline void mutex_lock(MutexT* mutex) {
|
||||
uv_mutex_lock(mutex);
|
||||
}
|
||||
|
||||
static inline void mutex_unlock(MutexT* mutex) {
|
||||
uv_mutex_unlock(mutex);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
ConditionVariableBase<Traits>::ConditionVariableBase() {
|
||||
CHECK_EQ(0, Traits::cond_init(&cond_));
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
ConditionVariableBase<Traits>::~ConditionVariableBase() {
|
||||
Traits::cond_destroy(&cond_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void ConditionVariableBase<Traits>::Broadcast(const ScopedLock&) {
|
||||
Traits::cond_broadcast(&cond_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void ConditionVariableBase<Traits>::Signal(const ScopedLock&) {
|
||||
Traits::cond_signal(&cond_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void ConditionVariableBase<Traits>::Wait(const ScopedLock& scoped_lock) {
|
||||
Traits::cond_wait(&cond_, &scoped_lock.mutex_.mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::MutexBase() {
|
||||
CHECK_EQ(0, Traits::mutex_init(&mutex_));
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::~MutexBase() {
|
||||
Traits::mutex_destroy(&mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void MutexBase<Traits>::Lock() {
|
||||
Traits::mutex_lock(&mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void MutexBase<Traits>::Unlock() {
|
||||
Traits::mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::ScopedLock::ScopedLock(const MutexBase& mutex)
|
||||
: mutex_(mutex) {
|
||||
Traits::mutex_lock(&mutex_.mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::ScopedLock::ScopedLock(const ScopedUnlock& scoped_unlock)
|
||||
: MutexBase(scoped_unlock.mutex_) {}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::ScopedLock::~ScopedLock() {
|
||||
Traits::mutex_unlock(&mutex_.mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::ScopedUnlock::ScopedUnlock(const ScopedLock& scoped_lock)
|
||||
: mutex_(scoped_lock.mutex_) {
|
||||
Traits::mutex_unlock(&mutex_.mutex_);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
MutexBase<Traits>::ScopedUnlock::~ScopedUnlock() {
|
||||
Traits::mutex_lock(&mutex_.mutex_);
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_NODE_MUTEX_H_
|
||||
@@ -0,0 +1,428 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef SRC_UTIL_INL_H_
|
||||
#define SRC_UTIL_INL_H_
|
||||
|
||||
#ifndef NODE_UTIL_H_INCLUDE
|
||||
#error "util-inl.h could only be included in util.h"
|
||||
#endif
|
||||
|
||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#include "util.h"
|
||||
#include <cstring>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#define BSWAP_2(x) _byteswap_ushort(x)
|
||||
#define BSWAP_4(x) _byteswap_ulong(x)
|
||||
#define BSWAP_8(x) _byteswap_uint64(x)
|
||||
#else
|
||||
#define BSWAP_2(x) ((x) << 8) | ((x) >> 8)
|
||||
#define BSWAP_4(x) \
|
||||
(((x) & 0xFF) << 24) | \
|
||||
(((x) & 0xFF00) << 8) | \
|
||||
(((x) >> 8) & 0xFF00) | \
|
||||
(((x) >> 24) & 0xFF)
|
||||
#define BSWAP_8(x) \
|
||||
(((x) & 0xFF00000000000000ull) >> 56) | \
|
||||
(((x) & 0x00FF000000000000ull) >> 40) | \
|
||||
(((x) & 0x0000FF0000000000ull) >> 24) | \
|
||||
(((x) & 0x000000FF00000000ull) >> 8) | \
|
||||
(((x) & 0x00000000FF000000ull) << 8) | \
|
||||
(((x) & 0x0000000000FF0000ull) << 24) | \
|
||||
(((x) & 0x000000000000FF00ull) << 40) | \
|
||||
(((x) & 0x00000000000000FFull) << 56)
|
||||
#endif
|
||||
|
||||
namespace node {
|
||||
|
||||
template <typename T>
|
||||
ListNode<T>::ListNode() : prev_(this), next_(this) {}
|
||||
|
||||
template <typename T>
|
||||
ListNode<T>::~ListNode() {
|
||||
Remove();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ListNode<T>::Remove() {
|
||||
prev_->next_ = next_;
|
||||
next_->prev_ = prev_;
|
||||
prev_ = this;
|
||||
next_ = this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool ListNode<T>::IsEmpty() const {
|
||||
return prev_ == this;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
ListHead<T, M>::Iterator::Iterator(ListNode<T>* node) : node_(node) {}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
T* ListHead<T, M>::Iterator::operator*() const {
|
||||
return ContainerOf(M, node_);
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
const typename ListHead<T, M>::Iterator&
|
||||
ListHead<T, M>::Iterator::operator++() {
|
||||
node_ = node_->next_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
bool ListHead<T, M>::Iterator::operator!=(const Iterator& that) const {
|
||||
return node_ != that.node_;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
ListHead<T, M>::~ListHead() {
|
||||
while (IsEmpty() == false)
|
||||
head_.next_->Remove();
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
void ListHead<T, M>::MoveBack(ListHead* that) {
|
||||
if (IsEmpty())
|
||||
return;
|
||||
ListNode<T>* to = &that->head_;
|
||||
head_.next_->prev_ = to->prev_;
|
||||
to->prev_->next_ = head_.next_;
|
||||
head_.prev_->next_ = to;
|
||||
to->prev_ = head_.prev_;
|
||||
head_.prev_ = &head_;
|
||||
head_.next_ = &head_;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
void ListHead<T, M>::PushBack(T* element) {
|
||||
ListNode<T>* that = &(element->*M);
|
||||
head_.prev_->next_ = that;
|
||||
that->prev_ = head_.prev_;
|
||||
that->next_ = &head_;
|
||||
head_.prev_ = that;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
void ListHead<T, M>::PushFront(T* element) {
|
||||
ListNode<T>* that = &(element->*M);
|
||||
head_.next_->prev_ = that;
|
||||
that->prev_ = &head_;
|
||||
that->next_ = head_.next_;
|
||||
head_.next_ = that;
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
bool ListHead<T, M>::IsEmpty() const {
|
||||
return head_.IsEmpty();
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
T* ListHead<T, M>::PopFront() {
|
||||
if (IsEmpty())
|
||||
return nullptr;
|
||||
ListNode<T>* node = head_.next_;
|
||||
node->Remove();
|
||||
return ContainerOf(M, node);
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
typename ListHead<T, M>::Iterator ListHead<T, M>::begin() const {
|
||||
return Iterator(head_.next_);
|
||||
}
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
typename ListHead<T, M>::Iterator ListHead<T, M>::end() const {
|
||||
return Iterator(const_cast<ListNode<T>*>(&head_));
|
||||
}
|
||||
|
||||
template <typename Inner, typename Outer>
|
||||
ContainerOfHelper<Inner, Outer>::ContainerOfHelper(Inner Outer::*field,
|
||||
Inner* pointer)
|
||||
: pointer_(reinterpret_cast<Outer*>(
|
||||
reinterpret_cast<uintptr_t>(pointer) -
|
||||
reinterpret_cast<uintptr_t>(&(static_cast<Outer*>(0)->*field)))) {
|
||||
}
|
||||
|
||||
template <typename Inner, typename Outer>
|
||||
template <typename TypeName>
|
||||
ContainerOfHelper<Inner, Outer>::operator TypeName*() const {
|
||||
return static_cast<TypeName*>(pointer_);
|
||||
}
|
||||
|
||||
template <typename Inner, typename Outer>
|
||||
inline ContainerOfHelper<Inner, Outer> ContainerOf(Inner Outer::*field,
|
||||
Inner* pointer) {
|
||||
return ContainerOfHelper<Inner, Outer>(field, pointer);
|
||||
}
|
||||
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> PersistentToLocal(
|
||||
v8::Isolate* isolate,
|
||||
const v8::Persistent<TypeName>& persistent) {
|
||||
if (persistent.IsWeak()) {
|
||||
return WeakPersistentToLocal(isolate, persistent);
|
||||
} else {
|
||||
return StrongPersistentToLocal(persistent);
|
||||
}
|
||||
}
|
||||
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> StrongPersistentToLocal(
|
||||
const v8::Persistent<TypeName>& persistent) {
|
||||
return *reinterpret_cast<v8::Local<TypeName>*>(
|
||||
const_cast<v8::Persistent<TypeName>*>(&persistent));
|
||||
}
|
||||
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> WeakPersistentToLocal(
|
||||
v8::Isolate* isolate,
|
||||
const v8::Persistent<TypeName>& persistent) {
|
||||
return v8::Local<TypeName>::New(isolate, persistent);
|
||||
}
|
||||
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const char* data,
|
||||
int length) {
|
||||
return v8::String::NewFromOneByte(isolate,
|
||||
reinterpret_cast<const uint8_t*>(data),
|
||||
v8::NewStringType::kNormal,
|
||||
length).ToLocalChecked();
|
||||
}
|
||||
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const signed char* data,
|
||||
int length) {
|
||||
return v8::String::NewFromOneByte(isolate,
|
||||
reinterpret_cast<const uint8_t*>(data),
|
||||
v8::NewStringType::kNormal,
|
||||
length).ToLocalChecked();
|
||||
}
|
||||
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const unsigned char* data,
|
||||
int length) {
|
||||
return v8::String::NewFromOneByte(isolate,
|
||||
reinterpret_cast<const uint8_t*>(data),
|
||||
v8::NewStringType::kNormal,
|
||||
length).ToLocalChecked();
|
||||
}
|
||||
|
||||
template <typename TypeName>
|
||||
void Wrap(v8::Local<v8::Object> object, TypeName* pointer) {
|
||||
CHECK_EQ(false, object.IsEmpty());
|
||||
CHECK_GT(object->InternalFieldCount(), 0);
|
||||
object->SetAlignedPointerInInternalField(0, pointer);
|
||||
}
|
||||
|
||||
void ClearWrap(v8::Local<v8::Object> object) {
|
||||
Wrap<void>(object, nullptr);
|
||||
}
|
||||
|
||||
template <typename TypeName>
|
||||
TypeName* Unwrap(v8::Local<v8::Object> object) {
|
||||
CHECK_EQ(false, object.IsEmpty());
|
||||
CHECK_GT(object->InternalFieldCount(), 0);
|
||||
void* pointer = object->GetAlignedPointerFromInternalField(0);
|
||||
return static_cast<TypeName*>(pointer);
|
||||
}
|
||||
|
||||
void SwapBytes16(char* data, size_t nbytes) {
|
||||
CHECK_EQ(nbytes % 2, 0);
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
int align = reinterpret_cast<uintptr_t>(data) % sizeof(uint16_t);
|
||||
if (align == 0) {
|
||||
// MSVC has no strict aliasing, and is able to highly optimize this case.
|
||||
uint16_t* data16 = reinterpret_cast<uint16_t*>(data);
|
||||
size_t len16 = nbytes / sizeof(*data16);
|
||||
for (size_t i = 0; i < len16; i++) {
|
||||
data16[i] = BSWAP_2(data16[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint16_t temp;
|
||||
for (size_t i = 0; i < nbytes; i += sizeof(temp)) {
|
||||
memcpy(&temp, &data[i], sizeof(temp));
|
||||
temp = BSWAP_2(temp);
|
||||
memcpy(&data[i], &temp, sizeof(temp));
|
||||
}
|
||||
}
|
||||
|
||||
void SwapBytes32(char* data, size_t nbytes) {
|
||||
CHECK_EQ(nbytes % 4, 0);
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
int align = reinterpret_cast<uintptr_t>(data) % sizeof(uint32_t);
|
||||
// MSVC has no strict aliasing, and is able to highly optimize this case.
|
||||
if (align == 0) {
|
||||
uint32_t* data32 = reinterpret_cast<uint32_t*>(data);
|
||||
size_t len32 = nbytes / sizeof(*data32);
|
||||
for (size_t i = 0; i < len32; i++) {
|
||||
data32[i] = BSWAP_4(data32[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t temp;
|
||||
for (size_t i = 0; i < nbytes; i += sizeof(temp)) {
|
||||
memcpy(&temp, &data[i], sizeof(temp));
|
||||
temp = BSWAP_4(temp);
|
||||
memcpy(&data[i], &temp, sizeof(temp));
|
||||
}
|
||||
}
|
||||
|
||||
void SwapBytes64(char* data, size_t nbytes) {
|
||||
CHECK_EQ(nbytes % 8, 0);
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
int align = reinterpret_cast<uintptr_t>(data) % sizeof(uint64_t);
|
||||
if (align == 0) {
|
||||
// MSVC has no strict aliasing, and is able to highly optimize this case.
|
||||
uint64_t* data64 = reinterpret_cast<uint64_t*>(data);
|
||||
size_t len64 = nbytes / sizeof(*data64);
|
||||
for (size_t i = 0; i < len64; i++) {
|
||||
data64[i] = BSWAP_8(data64[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t temp;
|
||||
for (size_t i = 0; i < nbytes; i += sizeof(temp)) {
|
||||
memcpy(&temp, &data[i], sizeof(temp));
|
||||
temp = BSWAP_8(temp);
|
||||
memcpy(&data[i], &temp, sizeof(temp));
|
||||
}
|
||||
}
|
||||
|
||||
char ToLower(char c) {
|
||||
return c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c;
|
||||
}
|
||||
|
||||
bool StringEqualNoCase(const char* a, const char* b) {
|
||||
do {
|
||||
if (*a == '\0')
|
||||
return *b == '\0';
|
||||
if (*b == '\0')
|
||||
return *a == '\0';
|
||||
} while (ToLower(*a++) == ToLower(*b++));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StringEqualNoCaseN(const char* a, const char* b, size_t length) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (ToLower(a[i]) != ToLower(b[i]))
|
||||
return false;
|
||||
if (a[i] == '\0')
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline size_t MultiplyWithOverflowCheck(size_t a, size_t b) {
|
||||
size_t ret = a * b;
|
||||
if (a != 0)
|
||||
CHECK_EQ(b, ret / a);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// These should be used in our code as opposed to the native
|
||||
// versions as they abstract out some platform and or
|
||||
// compiler version specific functionality.
|
||||
// malloc(0) and realloc(ptr, 0) have implementation-defined behavior in
|
||||
// that the standard allows them to either return a unique pointer or a
|
||||
// nullptr for zero-sized allocation requests. Normalize by always using
|
||||
// a nullptr.
|
||||
template <typename T>
|
||||
T* UncheckedRealloc(T* pointer, size_t n) {
|
||||
size_t full_size = MultiplyWithOverflowCheck(sizeof(T), n);
|
||||
|
||||
if (full_size == 0) {
|
||||
free(pointer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* allocated = realloc(pointer, full_size);
|
||||
|
||||
if (UNLIKELY(allocated == nullptr)) {
|
||||
// Tell V8 that memory is low and retry.
|
||||
LowMemoryNotification();
|
||||
allocated = realloc(pointer, full_size);
|
||||
}
|
||||
|
||||
return static_cast<T*>(allocated);
|
||||
}
|
||||
|
||||
// As per spec realloc behaves like malloc if passed nullptr.
|
||||
template <typename T>
|
||||
inline T* UncheckedMalloc(size_t n) {
|
||||
if (n == 0) n = 1;
|
||||
return UncheckedRealloc<T>(nullptr, n);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* UncheckedCalloc(size_t n) {
|
||||
if (n == 0) n = 1;
|
||||
MultiplyWithOverflowCheck(sizeof(T), n);
|
||||
return static_cast<T*>(calloc(n, sizeof(T)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* Realloc(T* pointer, size_t n) {
|
||||
T* ret = UncheckedRealloc(pointer, n);
|
||||
if (n > 0) CHECK_NE(ret, nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* Malloc(size_t n) {
|
||||
T* ret = UncheckedMalloc<T>(n);
|
||||
if (n > 0) CHECK_NE(ret, nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* Calloc(size_t n) {
|
||||
T* ret = UncheckedCalloc<T>(n);
|
||||
if (n > 0) CHECK_NE(ret, nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Shortcuts for char*.
|
||||
inline char* Malloc(size_t n) { return Malloc<char>(n); }
|
||||
inline char* Calloc(size_t n) { return Calloc<char>(n); }
|
||||
inline char* UncheckedMalloc(size_t n) { return UncheckedMalloc<char>(n); }
|
||||
inline char* UncheckedCalloc(size_t n) { return UncheckedCalloc<char>(n); }
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#endif // SRC_UTIL_INL_H_
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 "util.h"
|
||||
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
//cjh #include "string_bytes.h"
|
||||
//#include "node_buffer.h"
|
||||
//#include "node_internals.h"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace node {
|
||||
|
||||
using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
template <typename T>
|
||||
static void MakeUtf8String(Isolate* isolate,
|
||||
Local<Value> value,
|
||||
T* target) {
|
||||
Local<String> string;
|
||||
if (! value->ToString(isolate->GetCurrentContext()).ToLocal(&string) )
|
||||
return;
|
||||
|
||||
const size_t storage = 3 * string->Length() + 1;
|
||||
target->AllocateSufficientStorage(storage);
|
||||
const int flags =
|
||||
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8;
|
||||
const int length = string->WriteUtf8(isolate, target->out(), (int)storage, 0, flags);
|
||||
target->SetLengthAndZeroTerminate(length);
|
||||
}
|
||||
|
||||
Utf8Value::Utf8Value(Isolate* isolate, Local<Value> value) {
|
||||
if (value.IsEmpty())
|
||||
return;
|
||||
|
||||
MakeUtf8String(isolate, value, this);
|
||||
}
|
||||
|
||||
|
||||
TwoByteValue::TwoByteValue(Isolate* isolate, Local<Value> value) {
|
||||
if (value.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Local<String> string;
|
||||
if (! value->ToString(isolate->GetCurrentContext()).ToLocal(&string) )
|
||||
if (string.IsEmpty())
|
||||
return;
|
||||
|
||||
// Allocate enough space to include the null terminator
|
||||
const size_t storage = string->Length() + 1;
|
||||
AllocateSufficientStorage(storage);
|
||||
|
||||
const int flags = String::NO_NULL_TERMINATION;
|
||||
const int length = string->Write(isolate, out(), 0, (int)storage, flags);
|
||||
SetLengthAndZeroTerminate(length);
|
||||
}
|
||||
|
||||
BufferValue::BufferValue(Isolate* isolate, Local<Value> value) {
|
||||
// Slightly different take on Utf8Value. If value is a String,
|
||||
// it will return a Utf8 encoded string. If value is a Buffer,
|
||||
// it will copy the data out of the Buffer as is.
|
||||
if (value.IsEmpty()) {
|
||||
// Dereferencing this object will return nullptr.
|
||||
Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (value->IsString()) {
|
||||
MakeUtf8String(isolate, value, this);
|
||||
//cjh } else if (Buffer::HasInstance(value)) {
|
||||
// const size_t len = Buffer::Length(value);
|
||||
// // Leave place for the terminating '\0' byte.
|
||||
// AllocateSufficientStorage(len + 1);
|
||||
// memcpy(out(), Buffer::Data(value), len);
|
||||
// SetLengthAndZeroTerminate(len);
|
||||
} else {
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void LowMemoryNotification() {
|
||||
// if (v8_initialized) {
|
||||
auto isolate = v8::Isolate::GetCurrent();
|
||||
if (isolate != nullptr) {
|
||||
isolate->LowMemoryNotification();
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
void DumpBacktrace(FILE* fp) {
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
@@ -0,0 +1,446 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef SRC_UTIL_H_
|
||||
#define SRC_UTIL_H_
|
||||
|
||||
#include "../../config.hpp"
|
||||
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#define NODE_WANT_INTERNALS 1 //cjh added
|
||||
|
||||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <type_traits> // std::remove_reference
|
||||
|
||||
namespace node {
|
||||
|
||||
// These should be used in our code as opposed to the native
|
||||
// versions as they abstract out some platform and or
|
||||
// compiler version specific functionality
|
||||
// malloc(0) and realloc(ptr, 0) have implementation-defined behavior in
|
||||
// that the standard allows them to either return a unique pointer or a
|
||||
// nullptr for zero-sized allocation requests. Normalize by always using
|
||||
// a nullptr.
|
||||
template <typename T>
|
||||
inline T* UncheckedRealloc(T* pointer, size_t n);
|
||||
template <typename T>
|
||||
inline T* UncheckedMalloc(size_t n);
|
||||
template <typename T>
|
||||
inline T* UncheckedCalloc(size_t n);
|
||||
|
||||
// Same things, but aborts immediately instead of returning nullptr when
|
||||
// no memory is available.
|
||||
template <typename T>
|
||||
inline T* Realloc(T* pointer, size_t n);
|
||||
template <typename T>
|
||||
inline T* Malloc(size_t n);
|
||||
template <typename T>
|
||||
inline T* Calloc(size_t n);
|
||||
|
||||
inline char* Malloc(size_t n);
|
||||
inline char* Calloc(size_t n);
|
||||
inline char* UncheckedMalloc(size_t n);
|
||||
inline char* UncheckedCalloc(size_t n);
|
||||
|
||||
// Used by the allocation functions when allocation fails.
|
||||
// Thin wrapper around v8::Isolate::LowMemoryNotification() that checks
|
||||
// whether V8 is initialized.
|
||||
void LowMemoryNotification();
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define NO_RETURN __attribute__((noreturn))
|
||||
#else
|
||||
#define NO_RETURN
|
||||
#endif
|
||||
|
||||
// The slightly odd function signature for Assert() is to ease
|
||||
// instruction cache pressure in calls from CHECK.
|
||||
NO_RETURN void Abort();
|
||||
NO_RETURN void Assert(const char* const (*args)[4]);
|
||||
void DumpBacktrace(FILE* fp);
|
||||
|
||||
template <typename T> using remove_reference = std::remove_reference<T>;
|
||||
|
||||
#define FIXED_ONE_BYTE_STRING(isolate, string) \
|
||||
(node::OneByteString((isolate), (string), sizeof(string) - 1))
|
||||
|
||||
#define NODE_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
void operator=(const TypeName&) = delete; \
|
||||
void operator=(TypeName&&) = delete; \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
TypeName(TypeName&&) = delete
|
||||
|
||||
// Windows 8+ does not like abort() in Release mode
|
||||
#ifdef _WIN32
|
||||
#define ABORT_NO_BACKTRACE() raise(SIGABRT)
|
||||
#else
|
||||
#define ABORT_NO_BACKTRACE() abort()
|
||||
#endif
|
||||
|
||||
#define ABORT() node::Abort()
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define LIKELY(expr) __builtin_expect(!!(expr), 1)
|
||||
#define UNLIKELY(expr) __builtin_expect(!!(expr), 0)
|
||||
#define PRETTY_FUNCTION_NAME __PRETTY_FUNCTION__
|
||||
#else
|
||||
#define LIKELY(expr) expr
|
||||
#define UNLIKELY(expr) expr
|
||||
#define PRETTY_FUNCTION_NAME ""
|
||||
#endif
|
||||
|
||||
#define STRINGIFY_(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_(x)
|
||||
|
||||
#define CHECK(expr) \
|
||||
do { \
|
||||
if (UNLIKELY(!(expr))) { \
|
||||
static const char* const args[] = { __FILE__, STRINGIFY(__LINE__), \
|
||||
#expr, PRETTY_FUNCTION_NAME }; \
|
||||
node::Assert(&args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_EQ(a, b) CHECK((a) == (b))
|
||||
#define CHECK_GE(a, b) CHECK((a) >= (b))
|
||||
#define CHECK_GT(a, b) CHECK((a) > (b))
|
||||
#define CHECK_LE(a, b) CHECK((a) <= (b))
|
||||
#define CHECK_LT(a, b) CHECK((a) < (b))
|
||||
#define CHECK_NE(a, b) CHECK((a) != (b))
|
||||
|
||||
#define UNREACHABLE() ABORT()
|
||||
|
||||
#define ASSIGN_OR_RETURN_UNWRAP(ptr, obj, ...) \
|
||||
do { \
|
||||
*ptr = \
|
||||
Unwrap<typename node::remove_reference<decltype(**ptr)>::type>(obj); \
|
||||
if (*ptr == nullptr) \
|
||||
return __VA_ARGS__; \
|
||||
} while (0)
|
||||
|
||||
// TAILQ-style intrusive list node.
|
||||
template <typename T>
|
||||
class ListNode;
|
||||
|
||||
// TAILQ-style intrusive list head.
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
class ListHead;
|
||||
|
||||
template <typename T>
|
||||
class ListNode {
|
||||
public:
|
||||
inline ListNode();
|
||||
inline ~ListNode();
|
||||
inline void Remove();
|
||||
inline bool IsEmpty() const;
|
||||
|
||||
private:
|
||||
template <typename U, ListNode<U> (U::*M)> friend class ListHead;
|
||||
ListNode* prev_;
|
||||
ListNode* next_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(ListNode);
|
||||
};
|
||||
|
||||
template <typename T, ListNode<T> (T::*M)>
|
||||
class ListHead {
|
||||
public:
|
||||
class Iterator {
|
||||
public:
|
||||
inline T* operator*() const;
|
||||
inline const Iterator& operator++();
|
||||
inline bool operator!=(const Iterator& that) const;
|
||||
|
||||
private:
|
||||
friend class ListHead;
|
||||
inline explicit Iterator(ListNode<T>* node);
|
||||
ListNode<T>* node_;
|
||||
};
|
||||
|
||||
inline ListHead() = default;
|
||||
inline ~ListHead();
|
||||
inline void MoveBack(ListHead* that);
|
||||
inline void PushBack(T* element);
|
||||
inline void PushFront(T* element);
|
||||
inline bool IsEmpty() const;
|
||||
inline T* PopFront();
|
||||
inline Iterator begin() const;
|
||||
inline Iterator end() const;
|
||||
|
||||
private:
|
||||
ListNode<T> head_;
|
||||
NODE_DISALLOW_COPY_AND_ASSIGN(ListHead);
|
||||
};
|
||||
|
||||
// The helper is for doing safe downcasts from base types to derived types.
|
||||
template <typename Inner, typename Outer>
|
||||
class ContainerOfHelper {
|
||||
public:
|
||||
inline ContainerOfHelper(Inner Outer::*field, Inner* pointer);
|
||||
template <typename TypeName>
|
||||
inline operator TypeName*() const;
|
||||
private:
|
||||
Outer* const pointer_;
|
||||
};
|
||||
|
||||
// Calculate the address of the outer (i.e. embedding) struct from
|
||||
// the interior pointer to a data member.
|
||||
template <typename Inner, typename Outer>
|
||||
inline ContainerOfHelper<Inner, Outer> ContainerOf(Inner Outer::*field,
|
||||
Inner* pointer);
|
||||
|
||||
// If persistent.IsWeak() == false, then do not call persistent.Reset()
|
||||
// while the returned Local<T> is still in scope, it will destroy the
|
||||
// reference to the object.
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> PersistentToLocal(
|
||||
v8::Isolate* isolate,
|
||||
const v8::Persistent<TypeName>& persistent);
|
||||
|
||||
// Unchecked conversion from a non-weak Persistent<T> to Local<TLocal<T>,
|
||||
// use with care!
|
||||
//
|
||||
// Do not call persistent.Reset() while the returned Local<T> is still in
|
||||
// scope, it will destroy the reference to the object.
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> StrongPersistentToLocal(
|
||||
const v8::Persistent<TypeName>& persistent);
|
||||
|
||||
template <class TypeName>
|
||||
inline v8::Local<TypeName> WeakPersistentToLocal(
|
||||
v8::Isolate* isolate,
|
||||
const v8::Persistent<TypeName>& persistent);
|
||||
|
||||
// Convenience wrapper around v8::String::NewFromOneByte().
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const char* data,
|
||||
int length = -1);
|
||||
|
||||
// For the people that compile with -funsigned-char.
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const signed char* data,
|
||||
int length = -1);
|
||||
|
||||
inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate,
|
||||
const unsigned char* data,
|
||||
int length = -1);
|
||||
|
||||
inline void Wrap(v8::Local<v8::Object> object, void* pointer);
|
||||
|
||||
inline void ClearWrap(v8::Local<v8::Object> object);
|
||||
|
||||
template <typename TypeName>
|
||||
inline TypeName* Unwrap(v8::Local<v8::Object> object);
|
||||
|
||||
// Swaps bytes in place. nbytes is the number of bytes to swap and must be a
|
||||
// multiple of the word size (checked by function).
|
||||
inline void SwapBytes16(char* data, size_t nbytes);
|
||||
inline void SwapBytes32(char* data, size_t nbytes);
|
||||
inline void SwapBytes64(char* data, size_t nbytes);
|
||||
|
||||
// tolower() is locale-sensitive. Use ToLower() instead.
|
||||
inline char ToLower(char c);
|
||||
|
||||
// strcasecmp() is locale-sensitive. Use StringEqualNoCase() instead.
|
||||
inline bool StringEqualNoCase(const char* a, const char* b);
|
||||
|
||||
// strncasecmp() is locale-sensitive. Use StringEqualNoCaseN() instead.
|
||||
inline bool StringEqualNoCaseN(const char* a, const char* b, size_t length);
|
||||
|
||||
// Allocates an array of member type T. For up to kStackStorageSize items,
|
||||
// the stack is used, otherwise malloc().
|
||||
template <typename T, size_t kStackStorageSize = 1024>
|
||||
class MaybeStackBuffer {
|
||||
public:
|
||||
const T* out() const {
|
||||
return buf_;
|
||||
}
|
||||
|
||||
T* out() {
|
||||
return buf_;
|
||||
}
|
||||
|
||||
// operator* for compatibility with `v8::String::(Utf8)Value`
|
||||
T* operator*() {
|
||||
return buf_;
|
||||
}
|
||||
|
||||
const T* operator*() const {
|
||||
return buf_;
|
||||
}
|
||||
|
||||
T& operator[](size_t index) {
|
||||
CHECK_LT(index, length());
|
||||
return buf_[index];
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const {
|
||||
CHECK_LT(index, length());
|
||||
return buf_[index];
|
||||
}
|
||||
|
||||
size_t length() const {
|
||||
return length_;
|
||||
}
|
||||
|
||||
// Current maximum capacity of the buffer with which SetLength() can be used
|
||||
// without first calling AllocateSufficientStorage().
|
||||
size_t capacity() const {
|
||||
return IsAllocated() ? capacity_ :
|
||||
IsInvalidated() ? 0 : kStackStorageSize;
|
||||
}
|
||||
|
||||
// Make sure enough space for `storage` entries is available.
|
||||
// This method can be called multiple times throughout the lifetime of the
|
||||
// buffer, but once this has been called Invalidate() cannot be used.
|
||||
// Content of the buffer in the range [0, length()) is preserved.
|
||||
void AllocateSufficientStorage(size_t storage) {
|
||||
CHECK(!IsInvalidated());
|
||||
if (storage > capacity()) {
|
||||
bool was_allocated = IsAllocated();
|
||||
T* allocated_ptr = was_allocated ? buf_ : nullptr;
|
||||
buf_ = Realloc(allocated_ptr, storage);
|
||||
capacity_ = storage;
|
||||
if (!was_allocated && length_ > 0)
|
||||
memcpy(buf_, buf_st_, length_ * sizeof(buf_[0]));
|
||||
}
|
||||
|
||||
length_ = storage;
|
||||
}
|
||||
|
||||
void SetLength(size_t length) {
|
||||
// capacity() returns how much memory is actually available.
|
||||
CHECK_LE(length, capacity());
|
||||
length_ = length;
|
||||
}
|
||||
|
||||
void SetLengthAndZeroTerminate(size_t length) {
|
||||
// capacity() returns how much memory is actually available.
|
||||
CHECK_LE(length + 1, capacity());
|
||||
SetLength(length);
|
||||
|
||||
// T() is 0 for integer types, nullptr for pointers, etc.
|
||||
buf_[length] = T();
|
||||
}
|
||||
|
||||
// Make derefencing this object return nullptr.
|
||||
// This method can be called multiple times throughout the lifetime of the
|
||||
// buffer, but once this has been called AllocateSufficientStorage() cannot
|
||||
// be used.
|
||||
void Invalidate() {
|
||||
CHECK(!IsAllocated());
|
||||
length_ = 0;
|
||||
buf_ = nullptr;
|
||||
}
|
||||
|
||||
// If the buffer is stored in the heap rather than on the stack.
|
||||
bool IsAllocated() const {
|
||||
return !IsInvalidated() && buf_ != buf_st_;
|
||||
}
|
||||
|
||||
// If Invalidate() has been called.
|
||||
bool IsInvalidated() const {
|
||||
return buf_ == nullptr;
|
||||
}
|
||||
|
||||
// Release ownership of the malloc'd buffer.
|
||||
// Note: This does not free the buffer.
|
||||
void Release() {
|
||||
CHECK(IsAllocated());
|
||||
buf_ = buf_st_;
|
||||
length_ = 0;
|
||||
capacity_ = 0;
|
||||
}
|
||||
|
||||
MaybeStackBuffer() : length_(0), capacity_(0), buf_(buf_st_) {
|
||||
// Default to a zero-length, null-terminated buffer.
|
||||
buf_[0] = T();
|
||||
}
|
||||
|
||||
explicit MaybeStackBuffer(size_t storage) : MaybeStackBuffer() {
|
||||
AllocateSufficientStorage(storage);
|
||||
}
|
||||
|
||||
~MaybeStackBuffer() {
|
||||
if (IsAllocated())
|
||||
free(buf_);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t length_;
|
||||
// capacity of the malloc'ed buf_
|
||||
size_t capacity_;
|
||||
T* buf_;
|
||||
T buf_st_[kStackStorageSize];
|
||||
};
|
||||
|
||||
class Utf8Value : public MaybeStackBuffer<char> {
|
||||
public:
|
||||
explicit Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
||||
};
|
||||
|
||||
class TwoByteValue : public MaybeStackBuffer<uint16_t> {
|
||||
public:
|
||||
explicit TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
||||
};
|
||||
|
||||
class BufferValue : public MaybeStackBuffer<char> {
|
||||
public:
|
||||
explicit BufferValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
|
||||
};
|
||||
|
||||
#define THROW_AND_RETURN_UNLESS_BUFFER(env, obj) \
|
||||
do { \
|
||||
if (!Buffer::HasInstance(obj)) \
|
||||
return env->ThrowTypeError("argument should be a Buffer"); \
|
||||
} while (0)
|
||||
|
||||
#define SPREAD_BUFFER_ARG(val, name) \
|
||||
CHECK((val)->IsArrayBufferView()); \
|
||||
v8::Local<v8::ArrayBufferView> name = (val).As<v8::ArrayBufferView>(); \
|
||||
v8::ArrayBuffer::Contents name##_c = name->Buffer()->GetContents(); \
|
||||
const size_t name##_offset = name->ByteOffset(); \
|
||||
const size_t name##_length = name->ByteLength(); \
|
||||
char* const name##_data = \
|
||||
static_cast<char*>(name##_c.Data()) + name##_offset; \
|
||||
if (name##_length > 0) \
|
||||
CHECK_NE(name##_data, nullptr);
|
||||
|
||||
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
#define NODE_UTIL_H_INCLUDE
|
||||
#include "util-inl.h"
|
||||
|
||||
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
|
||||
|
||||
#endif // SRC_UTIL_H_
|
||||
@@ -0,0 +1,501 @@
|
||||
0,205,180,120,218,237,125,107,111,220,56,182,224,95,33,140,11,196,1,42,
|
||||
158,153,187,192,221,197,220,139,5,18,39,153,246,69,30,134,157,204,44,176,
|
||||
200,7,149,196,114,177,163,146,106,69,201,142,103,208,255,125,207,139,15,73,
|
||||
148,74,101,187,95,51,1,26,105,151,68,145,135,228,225,121,159,195,127,156,
|
||||
20,245,46,51,149,61,249,243,255,253,199,73,94,239,118,89,85,240,143,70,
|
||||
183,93,35,47,76,171,119,240,215,63,78,254,173,209,155,147,63,159,188,166,
|
||||
143,78,126,90,157,180,247,123,13,15,178,166,201,238,79,86,39,85,182,195,
|
||||
159,174,211,213,73,161,109,222,152,125,107,234,10,158,191,51,182,85,245,70,
|
||||
217,110,191,175,155,86,23,74,90,158,157,252,244,101,117,178,133,193,75,221,
|
||||
224,152,39,235,166,190,179,186,129,46,26,93,21,186,129,63,191,248,254,111,
|
||||
116,251,122,98,136,43,6,123,106,8,254,1,237,174,243,173,222,101,163,207,
|
||||
47,155,250,214,192,35,101,170,77,221,236,50,124,172,178,117,221,181,170,221,
|
||||
106,181,111,234,182,206,235,82,89,250,252,236,132,87,128,87,9,94,238,117,
|
||||
211,26,249,41,75,99,219,198,84,55,97,109,232,127,195,97,121,54,10,223,
|
||||
1,156,171,201,143,111,97,117,240,139,137,239,229,53,79,85,186,168,215,63,
|
||||
234,188,133,47,76,17,118,110,244,125,248,133,251,211,155,42,47,25,246,137,
|
||||
128,77,226,72,191,195,55,183,89,217,241,234,53,218,118,101,123,22,38,193,
|
||||
15,224,183,96,211,149,222,213,173,254,200,112,226,24,131,174,190,229,154,33,
|
||||
43,116,155,153,210,98,87,53,61,201,202,147,63,183,77,167,125,215,218,181,
|
||||
125,205,77,195,32,111,134,111,126,10,232,164,25,88,220,150,125,214,192,179,
|
||||
150,145,112,114,27,244,183,61,76,34,185,19,111,252,43,213,214,202,245,204,
|
||||
155,58,4,122,162,119,222,177,191,52,117,183,31,117,127,125,191,91,215,165,
|
||||
201,213,13,190,38,132,129,221,202,90,149,103,149,90,107,213,89,192,120,24,
|
||||
184,209,165,206,172,86,59,88,105,179,47,181,226,78,237,44,32,235,186,134,
|
||||
175,170,0,137,169,242,178,43,244,57,239,249,59,83,233,151,151,23,9,228,
|
||||
129,245,218,193,75,171,238,182,26,112,167,81,242,133,194,79,20,124,163,236,
|
||||
182,238,202,2,33,204,110,97,249,179,53,128,84,116,56,111,66,54,237,209,
|
||||
229,56,0,173,41,117,213,142,32,186,168,20,191,81,187,186,128,222,221,222,
|
||||
91,24,12,232,74,229,134,14,195,170,172,209,170,170,91,88,56,33,27,8,
|
||||
126,81,211,179,125,6,203,10,189,232,188,35,16,213,71,56,104,13,145,137,
|
||||
255,202,97,128,255,109,117,123,137,109,62,86,30,205,254,235,15,244,70,217,
|
||||
214,111,255,96,39,247,58,55,27,67,164,6,214,205,228,219,48,130,202,235,
|
||||
170,213,223,90,220,73,32,41,72,137,226,37,82,23,114,68,29,178,42,99,
|
||||
85,189,51,45,194,221,95,78,117,103,202,18,151,93,186,129,6,48,28,182,
|
||||
113,67,200,113,7,10,9,0,97,7,251,236,70,207,28,49,249,238,162,136,
|
||||
207,150,192,125,238,223,29,179,137,76,69,94,221,255,21,128,30,147,198,191,
|
||||
9,74,33,144,76,57,112,182,112,2,25,90,88,33,68,42,245,223,215,31,
|
||||
63,8,146,243,129,8,24,103,17,17,214,247,10,23,69,31,135,95,55,186,
|
||||
210,13,108,224,101,163,111,141,190,131,23,56,112,99,118,208,101,248,58,13,
|
||||
240,158,191,137,0,113,189,21,10,182,34,154,208,113,48,1,158,53,127,209,
|
||||
22,214,76,31,5,79,192,174,0,81,219,104,130,39,67,44,52,173,161,31,
|
||||
176,84,56,134,67,148,207,23,199,193,151,221,101,166,5,62,186,51,118,122,
|
||||
55,71,192,224,71,180,44,123,254,82,54,22,22,168,46,111,117,225,113,94,
|
||||
80,0,176,54,66,114,64,8,60,167,153,146,97,87,202,180,207,44,162,184,
|
||||
53,40,59,120,52,169,20,28,220,186,17,89,32,197,177,52,225,150,35,225,
|
||||
240,223,77,89,175,179,82,80,139,87,98,154,237,201,248,110,95,213,223,240,
|
||||
244,225,137,65,238,220,232,31,25,103,9,17,149,217,248,185,222,193,250,187,
|
||||
183,79,200,40,113,8,160,63,249,87,248,183,201,114,141,235,228,201,239,147,
|
||||
115,209,193,190,15,56,233,128,64,23,128,176,72,253,154,72,220,192,15,163,
|
||||
217,203,19,158,110,76,110,226,101,248,103,38,53,79,64,86,70,88,254,178,
|
||||
40,148,136,217,196,92,28,2,154,118,171,110,204,173,174,252,35,153,160,41,
|
||||
14,225,252,121,6,56,254,219,23,242,114,0,243,109,87,229,248,250,99,117,
|
||||
60,130,186,13,175,21,246,164,54,210,149,66,145,101,32,186,29,64,214,9,
|
||||
177,207,245,248,90,231,37,128,214,38,229,252,240,206,193,229,1,17,200,120,
|
||||
187,188,198,150,216,171,172,185,233,144,97,156,5,40,241,249,75,121,60,173,
|
||||
214,185,15,109,98,111,102,198,177,103,234,37,146,193,222,51,144,76,65,23,
|
||||
92,235,178,70,17,176,166,169,88,148,102,255,27,40,212,53,117,165,238,234,
|
||||
166,36,222,132,47,91,248,84,183,61,58,252,79,41,37,254,106,164,140,101,
|
||||
208,239,98,211,119,177,233,24,177,9,79,186,13,68,40,98,36,69,76,171,
|
||||
120,77,248,133,28,97,197,68,81,52,217,122,51,192,85,83,193,132,13,237,
|
||||
58,64,55,69,2,230,172,84,151,108,143,185,119,198,141,186,153,38,109,158,
|
||||
93,245,103,39,32,6,203,78,207,58,227,250,24,146,21,224,41,149,23,25,
|
||||
163,143,213,105,93,149,247,110,170,160,161,35,254,193,154,91,93,110,158,207,
|
||||
48,60,35,61,94,6,3,211,106,52,219,139,126,163,222,172,127,13,126,11,
|
||||
59,213,131,247,193,236,150,183,56,94,198,13,162,226,131,120,110,56,117,19,
|
||||
115,5,178,223,131,122,0,38,192,134,173,5,38,27,3,197,172,12,249,5,
|
||||
109,178,176,180,254,38,175,232,188,193,43,248,201,6,54,132,75,229,91,54,
|
||||
176,29,1,103,150,231,160,162,212,77,0,246,35,140,186,136,176,141,38,225,
|
||||
250,234,97,42,31,100,221,194,134,253,193,210,255,158,211,196,254,83,57,108,
|
||||
140,155,7,190,136,93,194,153,213,6,233,213,111,145,107,216,20,21,187,26,
|
||||
239,39,224,97,246,51,16,44,79,111,200,62,247,209,25,103,31,113,58,168,
|
||||
163,163,206,67,98,250,212,9,42,161,216,210,75,4,129,152,59,53,32,5,
|
||||
190,179,84,46,179,157,46,178,110,214,241,74,139,85,124,26,106,86,64,34,
|
||||
200,45,235,101,65,186,116,59,73,29,246,103,210,85,23,155,191,1,23,5,
|
||||
32,223,214,205,107,189,238,110,110,200,239,208,31,236,147,70,62,23,172,100,
|
||||
240,87,155,85,57,49,92,232,3,85,109,224,196,168,200,223,113,103,132,118,
|
||||
133,116,71,48,180,160,138,111,123,131,235,10,53,241,177,13,153,30,91,145,
|
||||
50,137,164,108,198,198,65,96,212,40,134,224,19,16,61,118,112,148,8,105,
|
||||
89,214,212,3,147,220,57,75,44,78,222,212,183,40,255,43,56,61,149,96,
|
||||
177,27,9,240,21,228,68,2,192,217,18,145,126,57,51,34,9,133,102,183,
|
||||
211,5,10,61,64,231,112,150,58,35,227,165,177,212,195,8,208,222,148,11,
|
||||
99,147,115,126,205,207,151,79,122,216,107,158,53,5,76,22,228,30,253,166,
|
||||
2,156,75,144,239,215,220,10,37,156,178,228,109,140,132,126,20,229,115,238,
|
||||
128,76,214,168,175,8,191,79,209,33,167,91,232,246,28,52,153,122,199,103,
|
||||
225,45,57,141,224,12,240,30,22,147,199,98,68,253,100,205,197,205,50,173,
|
||||
110,95,20,142,8,240,211,25,198,205,13,98,74,112,237,158,252,42,26,120,
|
||||
189,219,131,190,197,48,60,189,175,69,186,159,245,159,217,186,107,114,253,249,
|
||||
234,221,152,244,208,27,213,53,165,8,189,176,177,158,115,184,149,78,114,104,
|
||||
103,174,66,223,155,109,253,236,166,172,253,119,145,102,38,32,23,50,64,196,
|
||||
189,164,55,93,60,133,235,64,122,7,50,245,235,186,14,244,216,79,48,235,
|
||||
67,24,43,27,188,94,177,137,246,144,117,234,170,251,61,120,32,97,107,166,
|
||||
142,197,252,233,23,246,19,205,237,168,67,255,175,130,73,191,71,231,235,63,
|
||||
165,213,234,55,239,81,254,125,153,213,126,89,19,218,239,212,68,5,44,192,
|
||||
58,202,213,83,105,144,10,57,205,32,200,170,113,148,16,124,218,154,68,188,
|
||||
142,60,151,216,24,68,128,26,21,145,200,118,221,72,139,88,52,23,29,37,
|
||||
62,153,128,209,59,131,128,123,26,161,70,161,51,172,94,123,213,26,84,140,
|
||||
222,39,76,130,24,4,255,8,118,112,165,152,182,225,177,135,142,80,120,245,
|
||||
99,118,149,249,127,232,130,12,138,229,136,142,225,142,109,186,134,246,84,122,
|
||||
5,250,10,75,14,74,15,80,137,198,220,152,96,228,98,16,113,45,208,211,
|
||||
201,244,125,7,179,109,238,97,40,96,216,100,194,191,167,70,108,25,64,120,
|
||||
129,146,154,22,84,8,33,151,192,217,26,153,169,252,206,72,125,163,45,35,
|
||||
165,55,130,197,10,7,49,77,79,87,236,7,100,13,73,60,69,64,93,7,
|
||||
254,216,223,211,207,188,38,130,40,97,105,210,2,37,245,53,80,172,39,122,
|
||||
244,158,180,126,143,186,234,118,24,233,118,81,109,208,136,140,198,155,15,217,
|
||||
7,248,247,69,244,228,197,31,79,190,172,210,131,127,174,44,40,37,89,105,
|
||||
254,142,180,47,77,175,46,65,107,1,109,244,86,139,207,153,137,17,108,51,
|
||||
30,42,216,105,36,84,47,184,91,132,76,164,205,65,32,155,3,212,71,146,
|
||||
57,107,47,252,217,85,133,222,224,142,195,223,129,149,118,187,53,169,208,129,
|
||||
130,88,98,157,169,185,8,109,161,199,19,230,87,124,151,166,84,14,52,111,
|
||||
205,237,202,18,255,7,236,137,34,7,111,0,205,176,87,14,238,218,101,248,
|
||||
3,24,26,252,123,167,179,175,252,27,255,226,103,166,69,34,89,35,228,66,
|
||||
48,233,111,34,44,40,152,53,245,183,123,254,191,80,64,4,172,224,177,167,
|
||||
103,102,187,245,220,228,228,181,218,26,212,200,157,64,198,231,143,57,48,175,
|
||||
187,99,186,212,152,118,211,146,65,238,168,208,178,188,204,172,253,144,138,64,
|
||||
20,104,168,129,58,69,250,10,29,229,176,0,207,217,254,242,212,144,101,85,
|
||||
100,125,191,77,34,239,85,207,24,37,65,19,64,169,81,180,170,49,120,162,
|
||||
135,219,22,201,7,49,94,249,121,26,76,50,141,134,115,136,218,212,243,148,
|
||||
58,53,121,72,212,196,41,1,178,175,153,47,109,51,248,138,215,130,190,149,
|
||||
165,88,169,117,215,178,41,165,221,26,111,86,188,159,145,104,187,228,105,22,
|
||||
145,54,117,212,143,216,245,120,186,35,153,54,201,35,122,102,198,212,154,77,
|
||||
145,54,117,138,216,81,213,213,139,225,246,204,57,56,22,153,240,135,155,198,
|
||||
50,142,4,215,224,28,178,245,26,31,146,152,227,86,92,6,127,24,250,78,
|
||||
194,187,159,181,80,203,20,24,120,39,171,165,182,203,29,74,178,31,93,46,
|
||||
233,243,188,215,116,50,202,183,167,207,14,119,252,125,79,120,112,60,157,12,
|
||||
110,142,169,71,114,76,140,2,201,177,206,23,64,191,44,50,122,171,51,16,
|
||||
233,102,141,44,219,204,190,170,139,251,216,244,183,113,22,183,69,129,66,242,
|
||||
209,218,84,69,252,214,5,134,28,25,102,20,66,34,55,230,230,240,248,95,
|
||||
190,51,215,9,230,250,251,224,158,79,66,71,167,80,187,6,189,123,83,214,
|
||||
99,173,238,83,67,193,130,27,101,235,157,142,194,229,188,163,10,132,105,54,
|
||||
120,251,225,250,194,185,42,64,213,65,110,181,49,237,32,58,104,224,41,15,
|
||||
196,106,194,77,190,159,246,141,186,220,142,62,116,233,241,208,64,127,120,48,
|
||||
237,205,248,243,193,70,241,200,242,77,26,21,0,37,29,30,160,18,228,237,
|
||||
27,222,158,33,200,212,199,143,41,34,219,167,239,75,156,149,78,198,74,179,
|
||||
172,102,44,238,204,144,221,225,150,61,134,240,38,83,82,220,0,81,82,202,
|
||||
19,210,170,149,247,96,63,134,108,169,151,206,117,205,58,54,41,177,17,2,
|
||||
222,139,223,157,2,95,171,145,163,251,184,211,159,22,82,63,131,100,246,98,
|
||||
3,56,87,21,160,202,246,197,14,81,192,83,242,211,7,18,71,165,153,136,
|
||||
19,51,2,7,181,11,91,189,88,194,248,23,33,225,95,38,143,73,143,210,
|
||||
44,58,35,105,49,83,200,203,87,125,63,4,17,22,233,69,105,190,106,231,
|
||||
84,68,178,239,168,208,244,134,66,71,115,251,56,11,132,144,134,17,98,166,
|
||||
123,75,9,29,15,166,3,200,108,248,4,171,168,85,10,193,63,57,64,21,
|
||||
40,148,117,206,36,206,155,114,22,40,68,131,89,141,92,52,75,141,150,119,
|
||||
141,105,147,30,103,225,171,97,73,103,33,133,125,190,71,93,48,223,102,213,
|
||||
13,52,56,133,51,147,249,69,168,27,198,197,164,126,249,50,10,143,99,99,
|
||||
176,110,110,49,108,193,162,245,145,98,107,188,89,214,13,183,82,30,251,61,
|
||||
101,117,7,128,97,110,52,27,71,93,15,167,158,186,37,128,154,92,229,27,
|
||||
189,220,13,54,59,15,251,232,121,216,199,204,195,206,206,99,10,55,88,112,
|
||||
239,154,131,248,65,52,135,142,95,164,205,71,0,14,177,3,197,11,179,73,
|
||||
34,80,161,75,221,11,18,202,235,6,68,198,125,93,21,164,131,45,16,21,
|
||||
145,170,235,67,64,199,128,218,109,125,103,85,183,247,190,33,238,96,144,238,
|
||||
233,5,202,234,0,92,139,143,94,102,63,145,79,106,118,109,197,238,127,151,
|
||||
13,61,88,41,55,206,210,248,56,99,63,30,24,54,136,8,48,227,187,42,
|
||||
114,141,76,91,61,60,45,20,26,200,45,87,201,46,249,145,200,152,212,60,
|
||||
102,96,115,168,236,36,164,36,54,79,73,163,137,176,207,249,96,214,24,125,
|
||||
147,102,223,99,152,196,121,93,97,152,16,77,39,12,16,36,199,95,146,55,
|
||||
76,173,208,76,128,236,196,74,13,35,29,123,75,166,62,245,142,152,177,213,
|
||||
179,22,200,88,179,203,74,16,4,111,141,53,232,145,52,85,108,76,193,253,
|
||||
79,174,245,195,141,164,3,227,229,147,216,55,159,218,76,57,103,214,229,100,
|
||||
164,71,217,6,167,54,188,151,212,50,54,46,139,162,30,5,175,247,50,84,
|
||||
206,212,27,246,84,245,245,50,83,244,132,210,139,194,155,124,131,193,51,101,
|
||||
16,238,47,220,208,120,141,92,178,18,207,24,80,14,50,162,122,126,249,28,
|
||||
143,197,46,118,33,59,1,180,71,19,17,91,57,114,145,37,223,84,136,68,
|
||||
42,152,4,93,167,233,144,189,89,201,248,179,115,33,122,213,123,212,137,186,
|
||||
24,133,70,48,232,247,115,225,38,226,128,139,188,164,189,176,40,14,32,137,
|
||||
196,95,115,56,246,99,42,214,131,236,36,137,176,178,33,72,220,112,54,178,
|
||||
44,73,22,127,232,118,25,122,113,179,130,54,157,36,104,110,177,166,88,203,
|
||||
129,231,121,154,181,122,196,118,158,248,238,219,107,144,63,199,160,131,190,93,
|
||||
20,160,145,10,138,228,10,90,154,210,100,13,208,46,248,96,198,156,49,92,
|
||||
186,215,51,198,174,65,185,136,12,253,244,117,201,100,28,115,182,230,248,73,
|
||||
64,211,97,196,84,2,67,67,228,149,41,102,151,31,33,158,249,26,95,175,
|
||||
70,161,24,130,148,55,28,184,64,12,200,3,19,194,55,96,139,162,164,217,
|
||||
228,129,19,32,74,56,173,31,156,201,99,104,163,170,96,255,233,93,56,47,
|
||||
110,168,178,206,25,209,79,255,248,98,141,158,239,231,243,227,128,166,217,237,
|
||||
170,137,145,206,233,229,209,99,13,172,153,124,6,47,94,79,119,240,240,184,
|
||||
211,133,246,150,174,41,199,214,150,171,119,211,16,173,36,22,132,182,245,206,
|
||||
133,54,187,192,143,204,246,98,160,82,179,142,56,53,167,77,75,214,244,102,
|
||||
81,214,52,125,242,9,191,136,230,29,158,205,133,21,58,222,178,57,46,61,
|
||||
123,177,230,54,78,32,112,148,237,142,20,176,176,150,219,108,191,215,213,172,
|
||||
8,240,128,40,206,9,130,51,138,148,28,69,129,101,20,29,59,174,197,19,
|
||||
0,62,69,243,55,26,160,158,179,245,111,172,70,88,39,123,97,236,168,232,
|
||||
60,81,152,82,239,168,121,131,37,65,248,201,236,52,236,234,110,28,9,248,
|
||||
193,159,174,157,41,75,99,53,172,103,97,149,53,152,17,0,24,230,226,252,
|
||||
151,137,212,78,0,73,58,230,35,164,244,130,202,148,96,29,35,176,11,99,
|
||||
89,30,26,122,196,57,28,15,228,172,67,208,250,193,68,114,220,107,25,145,
|
||||
205,167,32,142,227,17,242,30,185,140,199,152,19,43,223,54,169,173,162,211,
|
||||
78,198,63,206,139,112,49,96,132,159,156,96,0,10,15,226,67,93,217,99,
|
||||
244,128,7,56,162,128,136,232,210,155,44,34,114,118,166,222,98,156,149,189,
|
||||
175,114,126,32,49,10,98,158,200,100,39,135,201,229,120,182,66,174,43,190,
|
||||
227,46,18,41,231,81,78,57,175,211,164,135,39,119,77,236,163,208,254,37,
|
||||
66,2,103,190,170,187,94,12,94,76,196,105,2,32,242,231,186,32,248,221,
|
||||
162,172,150,82,221,125,214,176,30,177,132,180,159,187,204,156,77,19,173,166,
|
||||
171,14,194,114,8,108,125,209,229,178,152,21,73,190,209,52,24,106,226,98,
|
||||
46,82,114,57,168,60,142,3,194,33,235,92,92,65,216,171,41,180,191,142,
|
||||
217,91,34,219,159,102,74,185,153,17,146,43,71,157,21,188,179,217,141,150,
|
||||
236,59,74,42,98,148,159,224,42,146,176,116,40,30,254,37,172,220,29,232,
|
||||
219,185,100,100,39,52,153,97,229,162,105,110,21,139,188,227,48,210,11,107,
|
||||
59,39,88,192,160,9,205,197,88,7,71,63,203,107,60,76,219,212,247,135,
|
||||
39,23,130,253,11,247,137,10,147,120,170,164,138,120,94,201,57,249,193,103,
|
||||
103,101,207,75,13,71,36,161,101,70,253,35,162,140,62,84,119,40,131,228,
|
||||
252,57,106,134,174,246,95,111,52,97,248,222,160,56,187,114,158,115,143,164,
|
||||
197,8,31,218,136,189,203,130,5,142,159,26,123,89,74,199,220,234,58,169,
|
||||
37,146,82,56,28,151,205,32,195,21,150,230,87,250,182,254,58,147,75,54,
|
||||
100,15,128,131,54,193,25,174,232,113,172,128,222,109,239,7,64,53,60,212,
|
||||
60,139,157,211,216,208,182,199,54,1,233,42,12,176,82,153,13,57,8,176,
|
||||
207,46,85,81,222,127,118,171,32,70,147,179,3,203,233,87,109,110,14,225,
|
||||
236,99,98,223,203,203,11,36,86,169,181,116,174,210,178,190,161,73,173,59,
|
||||
138,180,5,209,51,10,1,189,203,154,138,151,186,48,13,255,251,109,135,226,
|
||||
145,115,49,181,66,32,9,155,201,21,158,53,62,155,36,252,0,37,173,204,
|
||||
246,150,224,208,85,225,26,48,225,228,16,211,141,161,14,229,175,55,21,54,
|
||||
205,235,142,88,16,98,46,62,57,210,117,254,201,123,49,244,12,223,30,232,
|
||||
18,211,181,96,236,201,129,178,47,15,208,68,28,104,180,145,187,172,136,253,
|
||||
156,15,72,250,73,128,231,79,253,49,148,32,37,231,49,135,206,179,61,150,
|
||||
20,137,20,206,17,248,15,212,23,103,113,63,78,84,197,177,114,194,234,30,
|
||||
202,75,50,213,149,139,125,29,163,124,207,194,122,216,125,54,52,66,161,195,
|
||||
254,16,201,19,205,54,216,92,66,138,23,25,57,245,55,88,226,82,175,216,
|
||||
137,24,82,64,164,217,233,115,37,5,71,89,21,112,121,185,207,147,229,72,
|
||||
151,101,85,251,156,108,231,239,145,186,87,152,113,166,206,75,67,54,97,1,
|
||||
152,114,79,96,58,46,209,139,121,49,126,143,228,115,155,97,217,7,36,236,
|
||||
146,47,141,242,126,25,59,181,120,8,148,11,100,22,6,201,83,174,205,237,
|
||||
96,179,14,166,69,247,160,142,32,142,59,177,186,125,5,180,255,235,190,198,
|
||||
157,121,153,163,125,121,121,2,114,230,218,15,35,85,238,196,70,141,35,175,
|
||||
67,255,138,63,240,89,96,227,90,99,248,158,42,234,253,1,192,207,252,47,
|
||||
60,31,113,63,226,111,76,77,231,250,171,217,191,44,75,74,64,179,203,167,
|
||||
98,225,179,3,19,193,38,156,252,102,167,103,240,62,251,10,175,17,48,194,
|
||||
4,242,7,53,29,70,197,34,223,190,119,223,159,134,217,172,98,118,87,212,
|
||||
187,136,53,233,54,127,126,40,91,53,72,124,78,160,13,93,247,50,114,66,
|
||||
42,78,152,117,104,26,83,198,87,241,211,20,189,127,39,230,179,105,90,239,
|
||||
12,108,243,65,127,190,21,99,124,4,184,83,96,112,1,107,213,237,113,245,
|
||||
138,194,180,190,84,113,10,127,95,221,127,38,195,67,122,203,31,106,123,109,
|
||||
41,206,32,6,46,107,207,158,208,42,137,83,197,68,114,155,24,233,64,42,
|
||||
98,98,164,43,12,208,74,200,113,240,20,112,15,195,37,42,79,197,0,8,
|
||||
187,0,10,60,110,222,181,37,129,25,141,119,77,215,209,51,26,198,189,144,
|
||||
106,109,67,215,211,196,84,142,51,202,124,220,108,16,70,73,9,38,58,255,
|
||||
232,109,66,123,156,105,15,23,13,192,228,87,98,61,209,80,254,91,41,144,
|
||||
225,231,188,10,84,152,50,154,169,208,143,109,235,189,35,97,81,39,46,6,
|
||||
35,42,38,170,125,133,81,44,14,212,116,73,146,115,141,73,35,145,13,163,
|
||||
183,4,66,248,189,13,223,67,230,178,234,214,247,138,144,177,161,255,81,124,
|
||||
223,153,250,72,101,74,6,92,200,16,147,94,137,70,38,165,59,224,172,89,
|
||||
95,141,192,242,36,41,197,37,198,31,127,154,177,39,159,149,232,197,121,79,
|
||||
6,28,234,120,127,190,122,43,196,107,151,181,249,54,50,205,226,176,164,142,
|
||||
24,95,73,18,251,179,221,218,162,240,2,83,231,174,3,20,87,2,67,175,
|
||||
158,137,155,148,184,230,65,144,55,32,43,196,43,72,3,216,174,185,69,190,
|
||||
69,100,189,209,101,157,21,246,183,64,151,7,68,203,237,241,44,49,61,235,
|
||||
177,239,46,43,61,49,95,141,201,251,4,161,61,164,75,7,72,70,135,210,
|
||||
196,234,116,57,51,244,191,252,193,205,6,71,119,80,77,105,87,223,234,185,
|
||||
29,57,2,147,190,164,210,232,110,245,4,108,211,245,109,230,42,235,209,136,
|
||||
79,37,55,236,107,203,177,50,209,138,249,47,207,134,37,229,164,113,36,231,
|
||||
30,194,223,107,212,186,201,32,129,177,121,140,197,89,131,6,216,185,129,251,
|
||||
184,77,154,251,4,98,15,181,140,226,33,99,169,83,16,20,203,14,67,237,
|
||||
158,11,246,162,196,25,97,176,230,142,29,109,6,12,37,63,103,102,221,27,
|
||||
26,114,206,111,87,21,199,29,205,68,109,4,60,173,121,251,169,126,27,2,
|
||||
255,7,188,28,79,150,203,206,143,102,200,38,111,204,238,22,54,79,181,94,
|
||||
79,49,81,176,226,204,204,96,230,207,72,34,111,218,217,66,109,110,61,195,
|
||||
16,125,245,228,76,57,111,23,177,18,194,1,100,88,184,88,188,57,225,203,
|
||||
168,176,167,64,54,180,29,1,119,236,244,167,58,162,174,139,73,166,251,90,
|
||||
245,168,245,145,196,210,199,241,83,64,88,222,53,228,139,152,49,255,80,225,
|
||||
185,243,216,189,50,62,227,169,250,58,12,171,141,12,193,172,211,250,120,18,
|
||||
47,123,144,38,155,229,219,129,34,11,123,185,199,18,34,9,191,148,222,131,
|
||||
240,121,43,213,51,72,231,226,42,196,131,175,47,128,175,77,124,77,250,67,
|
||||
207,47,21,204,88,241,248,93,59,53,124,215,142,92,91,163,46,72,171,75,
|
||||
116,80,239,189,174,74,238,154,190,167,41,53,27,92,157,174,212,215,50,43,
|
||||
242,83,45,202,20,138,166,203,158,33,233,169,112,142,187,204,126,37,111,80,
|
||||
69,193,166,225,245,90,195,49,16,248,104,30,103,42,220,136,67,129,213,98,
|
||||
136,143,186,33,77,190,163,176,69,223,81,168,14,201,238,27,24,171,170,185,
|
||||
61,89,235,195,136,232,239,169,184,52,67,114,190,83,230,41,148,243,82,149,
|
||||
53,232,113,143,91,13,92,245,199,50,171,107,162,192,239,81,230,60,186,254,
|
||||
171,191,176,136,169,56,9,174,218,14,148,86,124,117,193,134,199,234,65,5,
|
||||
156,164,119,51,95,198,73,106,142,156,45,241,217,131,212,220,220,79,185,134,
|
||||
195,136,155,250,200,18,157,152,123,127,173,129,186,39,77,69,190,178,168,155,
|
||||
144,229,100,125,235,190,56,178,86,144,77,235,191,126,24,42,195,108,93,153,
|
||||
147,80,136,138,124,0,168,250,36,197,52,4,77,28,149,44,147,201,247,200,
|
||||
38,92,76,0,109,229,88,41,152,207,67,68,3,83,47,110,72,106,21,232,
|
||||
130,11,66,162,185,208,197,215,32,67,4,238,245,215,247,116,60,80,20,221,
|
||||
115,208,205,2,71,249,140,163,253,168,250,82,8,234,57,39,44,76,86,243,
|
||||
17,86,195,70,109,158,28,219,182,235,130,21,207,108,67,43,190,223,151,247,
|
||||
46,102,159,147,32,236,164,167,62,94,164,149,80,177,25,185,133,72,213,117,
|
||||
194,88,238,15,196,178,32,171,232,82,140,249,1,167,253,125,110,196,185,82,
|
||||
110,104,181,164,241,185,162,224,67,232,1,34,204,147,145,2,219,7,102,140,
|
||||
178,130,237,195,170,146,199,160,82,209,220,3,72,163,222,149,156,212,8,43,
|
||||
88,255,34,179,186,227,57,24,116,2,232,131,54,37,245,186,185,167,106,170,
|
||||
18,140,226,162,134,177,136,174,216,5,162,17,40,98,19,57,186,239,138,176,
|
||||
50,224,33,71,219,143,111,122,41,76,95,85,99,99,153,42,61,133,154,99,
|
||||
39,143,11,107,25,208,136,95,229,140,124,233,9,244,32,24,187,0,145,89,
|
||||
68,13,65,30,189,178,81,225,142,179,254,77,24,126,13,98,188,61,143,30,
|
||||
166,100,123,130,6,13,238,77,107,242,174,204,26,185,107,131,70,245,9,83,
|
||||
107,125,99,170,202,39,214,198,155,245,144,51,208,67,130,161,190,249,216,163,
|
||||
140,152,43,232,213,175,90,190,228,80,79,106,63,161,199,94,136,235,100,185,
|
||||
230,68,13,63,59,237,1,7,165,76,83,70,119,158,117,55,91,148,104,96,
|
||||
19,230,242,123,91,78,31,30,100,157,80,97,193,186,138,43,20,238,38,78,
|
||||
228,107,74,116,176,82,140,176,255,13,59,103,212,57,231,17,160,1,10,229,
|
||||
23,177,221,176,245,210,181,197,44,11,134,57,238,128,234,209,68,15,206,212,
|
||||
5,197,177,149,211,195,161,228,194,38,71,92,139,16,142,48,107,35,148,164,
|
||||
161,187,6,89,124,240,46,234,99,110,66,20,20,248,69,234,145,46,97,102,
|
||||
238,108,127,172,226,168,199,95,150,78,172,126,139,215,48,178,200,56,42,83,
|
||||
78,181,82,187,54,24,177,225,247,41,76,6,211,47,1,47,76,33,197,237,
|
||||
184,84,8,182,137,186,225,104,22,180,235,224,67,198,191,113,161,117,193,198,
|
||||
231,63,71,221,205,113,149,226,145,247,61,85,118,211,221,117,32,171,92,68,
|
||||
182,83,180,188,110,50,42,98,8,173,54,89,105,245,247,42,168,223,111,60,
|
||||
60,14,38,218,210,143,213,53,236,199,155,205,134,99,80,150,67,69,216,9,
|
||||
29,112,162,155,207,39,2,165,222,32,230,80,135,81,73,196,134,173,26,112,
|
||||
136,71,40,116,204,181,129,190,174,168,39,135,67,142,252,215,172,49,113,18,
|
||||
229,66,71,183,205,235,253,148,167,91,162,215,163,228,31,106,173,232,86,2,
|
||||
11,135,216,74,228,29,63,230,123,71,20,217,108,159,161,97,175,124,182,82,
|
||||
207,242,178,182,93,163,159,209,249,120,150,163,201,227,153,124,64,165,53,233,
|
||||
32,17,77,67,137,253,163,88,126,106,124,147,187,125,7,154,97,246,29,103,
|
||||
132,193,223,36,157,207,95,108,44,139,145,76,129,112,43,53,25,0,206,161,
|
||||
29,210,104,88,30,163,210,119,131,60,85,199,249,6,183,158,165,229,57,220,
|
||||
64,102,103,116,200,182,117,89,88,63,216,227,4,94,86,190,173,75,1,221,
|
||||
132,57,80,97,90,63,178,187,121,68,118,87,22,219,209,179,112,211,53,85,
|
||||
147,21,71,253,174,107,19,171,31,176,143,180,12,4,143,52,131,215,112,42,
|
||||
182,203,113,112,151,125,115,95,12,3,102,190,153,93,183,3,162,15,111,41,
|
||||
53,208,39,9,176,70,131,149,153,116,219,138,25,138,105,235,31,29,37,37,
|
||||
221,144,15,36,156,29,64,74,137,138,242,165,85,176,98,210,176,67,117,42,
|
||||
28,230,121,242,120,74,208,23,150,147,112,161,84,227,46,80,205,250,234,21,
|
||||
138,249,219,30,94,149,208,118,93,127,187,228,248,139,132,28,237,53,198,62,
|
||||
174,79,23,213,10,61,13,52,65,108,199,161,180,88,168,71,234,27,185,98,
|
||||
234,78,57,206,183,26,111,248,100,29,0,239,47,32,71,136,64,57,29,219,
|
||||
116,165,247,37,90,169,136,104,99,198,129,255,196,193,195,10,197,30,131,82,
|
||||
225,36,84,88,85,235,109,77,81,37,107,104,137,142,20,148,196,49,142,10,
|
||||
141,238,123,152,227,31,144,143,246,108,105,210,9,130,229,189,254,208,149,247,
|
||||
253,201,80,103,100,9,195,137,97,238,12,116,11,12,0,195,14,4,164,112,
|
||||
67,2,48,53,201,1,198,158,158,225,192,48,24,208,39,13,12,59,147,232,
|
||||
206,149,218,96,249,51,42,45,108,229,50,17,232,147,91,3,101,127,134,212,
|
||||
191,171,196,32,190,233,202,99,246,93,23,87,116,106,143,213,6,143,55,232,
|
||||
140,12,217,244,242,178,182,102,222,237,186,151,22,118,50,162,205,237,159,223,
|
||||
60,235,221,114,146,54,86,197,107,223,176,61,175,183,71,209,6,9,91,113,
|
||||
27,254,51,109,145,114,19,71,202,135,231,66,202,168,145,47,208,56,47,35,
|
||||
250,38,250,184,79,86,104,182,114,2,254,154,198,74,28,31,16,93,41,218,
|
||||
16,102,122,166,248,200,69,146,148,75,22,77,232,172,18,81,48,93,3,60,
|
||||
68,138,230,217,62,91,155,210,80,73,60,76,139,23,157,192,10,45,228,80,
|
||||
154,93,125,139,63,162,96,155,149,63,90,36,195,128,106,27,132,212,21,213,
|
||||
208,174,57,199,49,88,139,224,27,221,230,136,109,190,138,121,239,82,163,61,
|
||||
108,60,214,251,164,140,51,95,224,60,153,147,195,211,189,164,160,160,227,239,
|
||||
168,138,130,123,116,241,100,230,204,201,248,187,166,151,184,214,27,156,74,1,
|
||||
103,213,253,129,204,65,178,62,161,106,148,142,32,172,57,76,173,63,0,82,
|
||||
55,83,245,130,238,98,59,12,66,70,113,206,206,34,148,221,216,37,80,112,
|
||||
18,247,84,110,247,3,33,57,144,212,193,106,225,120,238,153,149,60,204,132,
|
||||
129,120,166,179,137,41,188,211,213,13,139,6,20,236,55,215,249,148,110,234,
|
||||
188,52,46,199,110,156,233,53,151,41,16,204,30,199,148,138,216,102,118,155,
|
||||
170,122,67,150,115,124,121,132,249,124,24,71,63,132,246,229,195,139,59,28,
|
||||
229,93,123,7,136,129,182,240,69,218,212,39,242,184,185,224,40,199,38,108,
|
||||
164,225,13,34,248,57,150,243,150,221,95,10,67,0,15,223,198,145,190,174,
|
||||
233,125,182,79,221,216,36,81,183,130,234,187,108,63,170,38,36,80,246,79,
|
||||
255,210,245,129,77,189,142,110,139,122,224,10,161,207,207,95,58,117,236,6,
|
||||
189,175,209,155,254,168,237,121,115,253,31,104,33,233,74,125,92,192,110,73,
|
||||
199,116,217,208,209,136,252,217,129,212,116,180,221,50,195,242,217,177,33,249,
|
||||
167,79,184,229,242,57,204,150,107,12,178,48,93,60,168,28,195,76,234,235,
|
||||
132,135,164,15,61,200,13,46,163,229,214,52,232,109,2,132,67,105,86,51,
|
||||
164,110,1,36,244,148,193,198,192,138,210,214,32,233,52,98,0,65,149,227,
|
||||
107,21,178,254,194,61,116,78,120,162,224,120,74,39,65,182,238,162,22,7,
|
||||
81,37,216,244,45,149,70,248,84,19,119,254,206,156,191,51,231,239,204,249,
|
||||
247,203,156,191,243,189,239,124,239,247,198,247,54,20,76,131,254,46,4,121,
|
||||
116,188,70,17,235,46,101,227,16,175,10,145,221,227,91,170,30,155,84,241,
|
||||
146,2,69,82,49,216,11,67,115,231,22,40,206,33,136,178,100,240,230,223,
|
||||
74,66,84,220,46,147,71,45,149,9,64,126,168,98,198,164,248,36,65,40,
|
||||
231,33,152,139,138,31,15,118,86,162,209,124,190,154,139,11,248,63,63,92,
|
||||
65,87,175,63,190,135,127,223,32,130,98,116,36,138,255,132,104,161,38,149,
|
||||
207,98,39,241,69,92,142,225,222,164,43,253,163,118,193,227,31,169,51,138,
|
||||
30,197,15,119,107,115,211,213,157,157,9,54,152,168,109,192,209,6,252,242,
|
||||
40,250,94,164,136,249,184,80,63,109,110,138,182,15,175,58,152,15,17,252,
|
||||
193,244,115,241,46,94,219,201,152,191,173,105,251,89,14,83,118,229,95,39,
|
||||
132,104,242,28,204,163,84,47,33,178,137,235,237,53,20,205,44,145,196,72,
|
||||
43,115,188,77,189,49,89,34,108,120,28,176,120,0,0,249,172,23,75,252,
|
||||
229,224,93,113,175,250,68,102,146,80,45,186,44,238,188,231,32,90,16,52,
|
||||
113,54,151,22,230,236,181,44,5,112,168,219,68,229,158,56,107,97,174,162,
|
||||
231,245,240,230,187,97,189,142,80,208,216,89,20,207,98,19,161,143,147,121,
|
||||
160,54,241,208,156,222,190,233,122,80,38,236,73,50,84,251,53,21,167,199,
|
||||
251,242,52,27,54,48,244,31,115,181,199,220,26,46,174,154,150,154,71,92,
|
||||
197,56,184,180,149,119,81,229,253,26,80,187,80,10,131,200,208,228,92,123,
|
||||
113,69,115,216,153,62,34,172,111,71,40,107,184,86,60,250,84,141,139,178,
|
||||
78,17,4,35,209,102,197,19,197,34,205,214,240,251,16,41,191,169,194,102,
|
||||
156,30,161,92,182,231,48,102,224,40,140,154,32,239,110,172,136,24,204,8,
|
||||
135,51,9,110,7,134,95,150,223,52,118,109,193,206,79,203,50,228,241,62,
|
||||
199,104,133,68,244,166,143,100,136,170,109,204,46,32,147,48,108,232,211,222,
|
||||
153,223,39,191,143,162,80,140,61,58,84,47,148,58,95,107,14,250,226,124,
|
||||
105,95,51,222,227,0,90,139,90,121,175,36,55,115,114,55,185,217,68,88,
|
||||
195,160,28,122,170,186,43,174,89,239,2,143,73,74,84,239,19,39,211,73,
|
||||
132,55,101,189,206,208,228,67,193,35,88,31,201,144,242,36,33,36,248,23,
|
||||
165,221,172,78,214,208,226,43,150,64,114,183,156,99,84,13,94,57,195,170,
|
||||
222,145,133,140,174,125,52,74,106,127,63,186,155,237,164,194,183,139,7,39,
|
||||
44,226,242,135,140,2,12,254,248,74,40,156,133,143,238,226,96,11,19,245,
|
||||
103,185,252,33,75,244,188,94,255,25,199,55,69,198,31,252,22,175,44,166,
|
||||
221,109,144,68,229,24,120,10,50,89,101,13,5,221,51,172,254,66,6,74,
|
||||
138,175,233,130,27,14,5,161,235,53,48,94,61,22,52,87,147,165,123,166,
|
||||
176,114,161,225,161,98,149,226,136,51,47,42,44,3,205,241,219,179,186,105,
|
||||
211,190,155,39,13,15,27,89,99,13,160,185,212,215,217,196,248,233,82,173,
|
||||
190,32,236,209,34,137,183,243,245,178,137,38,214,29,187,13,9,100,137,126,
|
||||
201,174,67,17,20,81,127,147,165,26,163,140,183,69,217,134,81,118,27,23,
|
||||
46,213,62,26,126,234,98,165,126,10,248,3,110,86,250,46,109,62,86,218,
|
||||
156,201,19,118,142,131,107,151,151,74,164,152,238,254,98,214,113,136,226,194,
|
||||
153,120,104,125,45,87,154,140,195,15,48,25,16,75,35,227,28,92,13,80,
|
||||
21,249,61,48,40,184,113,17,196,57,85,223,234,221,153,30,176,64,190,126,
|
||||
89,20,161,238,71,85,19,77,29,27,82,14,214,211,26,66,137,97,80,114,
|
||||
27,68,35,145,207,220,192,3,77,249,39,142,139,11,154,246,224,238,39,141,
|
||||
99,1,192,247,242,237,24,12,185,64,120,203,65,103,95,146,182,69,9,44,
|
||||
161,10,156,88,185,150,12,188,47,168,242,133,187,127,30,206,234,187,26,195,
|
||||
173,108,171,51,18,101,67,83,127,214,93,24,200,57,207,232,216,40,144,120,
|
||||
221,15,6,255,247,23,77,66,38,93,153,180,12,187,136,14,172,180,138,100,
|
||||
109,254,92,150,109,65,13,214,193,38,17,155,229,65,250,218,253,132,252,194,
|
||||
181,27,127,4,137,220,203,37,149,110,239,234,230,43,21,91,164,190,95,100,
|
||||
123,67,181,27,235,134,97,205,246,251,28,243,222,233,44,193,42,202,233,177,
|
||||
58,239,26,190,180,221,217,179,220,94,48,133,196,110,245,236,61,121,233,252,
|
||||
36,89,13,21,147,228,126,177,202,80,149,210,213,169,236,149,175,156,30,177,
|
||||
132,221,46,167,7,196,128,45,211,222,31,127,241,130,235,225,224,189,22,71,
|
||||
86,215,114,219,28,223,199,177,216,178,63,233,59,236,19,90,207,183,9,119,
|
||||
67,120,3,23,201,22,0,78,255,244,8,178,191,140,224,31,13,199,100,189,
|
||||
242,254,161,90,205,31,89,111,197,112,84,227,146,11,144,206,5,143,173,66,
|
||||
132,217,151,213,28,203,72,16,232,65,189,63,172,6,9,24,113,33,17,122,
|
||||
203,67,146,77,248,34,145,113,41,221,70,145,127,149,218,153,188,169,165,114,
|
||||
255,217,76,116,246,249,229,103,37,85,88,155,113,79,103,234,189,68,92,139,
|
||||
18,47,21,24,162,175,168,228,99,221,20,18,168,215,180,163,146,25,88,92,
|
||||
102,62,185,236,138,122,224,139,205,177,207,179,222,157,188,82,43,54,220,230,
|
||||
75,15,226,164,228,182,222,31,140,113,69,48,46,1,84,44,29,142,117,58,
|
||||
24,91,6,235,191,188,82,64,89,158,75,185,218,17,182,35,243,199,219,88,
|
||||
59,68,105,214,182,169,180,45,242,137,251,26,3,83,13,86,5,85,207,114,
|
||||
132,67,23,207,144,209,61,195,96,119,247,96,58,214,155,138,188,99,149,117,
|
||||
210,17,114,153,200,153,114,83,34,111,44,73,185,163,18,19,97,247,124,4,
|
||||
70,178,51,151,158,108,42,188,84,2,175,243,59,83,111,162,47,88,144,104,
|
||||
58,74,82,85,184,98,59,243,119,170,221,13,157,112,168,167,197,106,81,113,
|
||||
181,237,14,241,201,14,240,162,222,143,247,227,176,56,47,50,206,212,66,240,
|
||||
107,22,99,40,185,204,42,128,84,99,156,45,58,52,6,64,9,238,202,237,
|
||||
5,28,181,42,77,70,115,59,156,60,205,203,237,103,115,108,57,142,241,30,
|
||||
146,244,37,53,10,228,14,162,94,10,111,155,125,213,15,89,67,135,164,249,
|
||||
146,17,87,7,54,85,93,38,177,168,210,186,32,15,42,149,216,235,81,134,
|
||||
223,212,26,222,232,246,149,182,237,155,13,52,110,127,190,85,68,91,174,30,
|
||||
52,29,29,52,85,80,37,37,117,147,53,107,108,21,46,221,61,32,242,77,
|
||||
242,141,177,51,102,116,179,89,229,113,123,224,115,114,182,187,132,23,57,143,
|
||||
236,218,137,180,35,231,188,93,46,59,108,77,235,232,233,225,133,15,247,211,
|
||||
16,195,210,214,71,26,208,93,171,104,60,201,184,152,81,189,143,43,130,59,
|
||||
99,125,207,70,59,128,104,218,227,188,53,101,1,59,122,240,194,246,115,108,
|
||||
200,96,152,194,30,37,32,22,26,26,94,165,125,192,159,182,206,3,140,83,
|
||||
98,77,13,121,134,39,82,140,97,222,226,234,47,100,13,84,140,110,114,109,
|
||||
190,114,52,107,81,99,118,128,123,217,247,211,139,111,230,147,201,191,166,74,
|
||||
178,92,70,239,47,80,6,255,105,53,121,199,163,44,226,225,77,125,89,73,
|
||||
230,67,136,254,113,112,168,22,1,57,155,189,30,20,101,130,15,124,241,247,
|
||||
232,86,83,124,199,104,174,126,160,4,55,68,6,232,90,199,119,47,173,34,
|
||||
218,134,169,21,88,52,52,103,198,144,251,29,77,223,107,51,90,159,8,156,
|
||||
73,124,162,238,146,187,92,74,89,166,125,4,185,245,217,29,48,22,42,130,
|
||||
36,68,215,117,27,29,223,9,163,30,145,222,79,102,55,181,52,94,112,11,
|
||||
197,227,19,2,228,116,255,32,52,31,232,157,146,169,22,245,237,214,104,24,
|
||||
224,105,227,195,142,167,90,54,99,218,22,203,109,19,113,5,254,160,31,28,
|
||||
22,103,229,37,97,20,222,218,59,214,243,127,204,114,164,236,14,158,225,132,
|
||||
248,28,210,110,21,186,4,58,79,1,51,120,57,215,173,207,38,119,123,235,
|
||||
55,103,102,46,184,116,175,177,163,249,233,28,56,28,83,7,227,168,219,12,
|
||||
147,106,230,181,43,48,19,221,102,245,167,69,183,89,181,66,96,14,145,247,
|
||||
172,5,66,185,238,34,155,148,13,99,206,209,133,33,161,90,100,47,246,209,
|
||||
158,89,156,106,156,4,37,83,57,172,26,218,177,6,36,235,168,85,37,36,
|
||||
224,58,209,11,238,244,146,145,36,18,215,123,102,168,254,144,171,44,57,31,
|
||||
31,251,36,67,65,63,135,174,42,155,209,143,6,151,25,117,81,133,38,30,
|
||||
83,234,125,206,232,253,44,73,81,186,226,66,113,109,40,166,101,195,193,126,
|
||||
137,59,237,70,193,110,189,137,76,203,187,46,45,51,121,246,248,45,90,74,
|
||||
49,235,191,231,121,37,223,74,79,238,156,89,85,39,243,29,41,7,143,23,
|
||||
54,177,6,11,110,221,61,250,134,191,199,134,239,47,184,233,111,184,95,163,
|
||||
37,154,220,50,55,241,241,174,185,62,172,11,194,11,62,33,23,173,239,76,
|
||||
203,75,119,110,160,55,61,193,190,249,120,215,164,221,92,236,199,194,68,174,
|
||||
89,189,91,124,135,147,153,45,10,142,1,55,220,253,153,240,201,211,231,179,
|
||||
209,23,222,133,246,144,154,220,173,105,167,185,163,162,183,46,77,28,147,192,
|
||||
42,127,5,144,212,191,29,194,153,170,202,88,181,193,170,239,88,127,48,155,
|
||||
25,235,244,99,169,142,51,234,117,92,198,181,191,254,111,77,101,236,246,103,
|
||||
220,128,55,85,241,160,61,56,104,197,251,53,247,233,160,19,240,128,69,151,
|
||||
100,55,41,174,240,131,206,246,28,145,96,31,110,86,164,74,13,47,203,80,
|
||||
89,156,33,140,44,102,79,58,28,59,250,96,5,111,176,180,203,84,37,82,
|
||||
245,140,219,225,144,215,85,182,183,219,218,127,243,204,221,77,224,202,54,4,
|
||||
75,62,7,167,89,249,0,113,156,117,86,180,89,85,33,148,213,21,167,224,
|
||||
67,32,133,66,123,211,198,15,226,177,127,143,243,29,76,73,236,58,127,97,
|
||||
51,207,1,227,248,155,71,151,88,235,25,187,248,225,171,251,128,65,23,135,
|
||||
46,207,138,57,110,188,42,254,243,167,170,59,118,191,91,215,165,201,123,37,
|
||||
199,144,17,230,92,30,207,213,4,17,203,174,218,97,133,49,180,164,115,167,
|
||||
118,176,202,89,81,92,184,251,184,194,108,15,57,121,177,101,216,70,119,119,
|
||||
181,47,76,69,21,27,184,214,125,228,199,255,183,111,163,90,98,209,46,109,
|
||||
251,75,61,187,146,95,14,5,30,208,252,55,218,71,21,176,189,43,46,142,
|
||||
168,110,77,134,16,157,90,173,149,36,40,168,119,174,196,25,242,249,29,122,
|
||||
1,92,197,88,104,233,37,149,35,174,112,218,166,214,9,56,26,160,228,30,
|
||||
111,170,224,146,23,92,6,133,136,114,67,56,25,86,116,176,64,163,165,56,
|
||||
188,80,1,169,231,145,249,80,186,168,43,51,198,5,45,105,98,97,223,251,
|
||||
133,45,199,7,34,117,228,8,190,33,167,112,222,191,197,244,107,100,200,25,
|
||||
187,15,7,38,52,145,231,88,87,237,185,3,215,247,45,218,144,46,107,99,
|
||||
233,150,76,99,89,141,149,216,76,58,89,78,191,243,38,15,182,100,72,1,
|
||||
34,9,244,132,198,255,227,223,255,231,127,252,47,233,81,230,185,192,181,231,
|
||||
125,139,180,190,11,28,125,110,189,112,111,39,156,126,126,73,233,216,56,159,
|
||||
110,244,193,161,162,32,9,25,121,44,234,2,29,137,81,240,124,219,85,95,
|
||||
23,11,90,57,181,238,97,3,185,82,4,64,219,207,254,152,226,59,203,61,
|
||||
197,5,150,55,157,181,185,212,56,217,99,82,22,55,78,184,236,205,2,243,
|
||||
103,175,181,174,166,143,221,164,17,105,248,229,140,113,49,220,93,153,8,151,
|
||||
217,196,103,53,212,185,10,33,57,78,170,110,41,131,77,202,2,53,250,6,
|
||||
139,239,226,125,0,20,187,149,121,87,77,184,63,143,146,131,173,14,87,60,
|
||||
26,174,63,54,160,111,254,178,77,170,5,13,103,133,239,250,221,58,109,60,
|
||||
34,148,6,175,169,162,110,57,251,145,64,162,90,81,66,142,137,6,182,89,
|
||||
107,63,239,11,244,27,15,4,13,46,122,200,254,219,140,244,136,225,50,242,
|
||||
23,61,37,97,208,231,225,98,94,7,189,32,182,215,219,180,5,31,78,1,
|
||||
122,180,128,132,188,201,242,173,251,233,110,231,37,219,218,6,104,21,93,42,
|
||||
17,217,75,101,124,103,224,118,77,224,121,161,191,173,88,65,38,43,107,220,
|
||||
50,83,132,211,193,140,228,16,194,17,52,215,13,119,208,110,77,147,252,222,
|
||||
154,191,235,62,75,24,119,113,246,116,120,136,14,26,196,64,213,241,134,227,
|
||||
80,49,66,184,33,237,146,92,174,31,210,28,116,145,104,179,192,48,243,36,
|
||||
190,192,41,182,166,203,205,53,44,253,24,159,202,232,18,27,220,27,199,199,
|
||||
252,174,84,82,29,149,175,22,82,206,69,151,54,220,36,248,201,188,135,38,
|
||||
242,248,77,121,248,230,156,82,83,227,141,164,94,199,24,105,123,150,187,171,
|
||||
178,50,220,148,119,156,191,42,80,135,98,150,221,242,242,28,49,191,25,31,
|
||||
195,23,236,7,228,19,170,103,12,155,178,203,126,172,27,104,241,39,76,147,
|
||||
48,21,253,253,239,39,63,253,244,255,1,17,146,119,172
|
||||
Reference in New Issue
Block a user