初始化

This commit is contained in:
SmallMain
2022-06-25 00:23:03 +08:00
commit ef0589e8e5
2264 changed files with 617829 additions and 0 deletions

View File

@@ -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 {

View 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 {

View 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 {

View File

@@ -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 {

View 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

View File

@@ -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 {

View File

@@ -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 {

View 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"

View 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 {

View 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
};
}

View 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 {

View 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;

View 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 "ChakraCore.h"
#include "ChakraCoreVersion.h"
#include <string>
#include <vector>
#include <unordered_map>
#include <chrono>
#include <functional>
#include <assert.h>
#include "HelperMacros.h"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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 "HelperMacros.h"
#include "Utils.hpp"

View File

@@ -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

View File

@@ -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

View 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)

View 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

View 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"

View 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

View 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

View File

@@ -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

View 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, &timestamp, 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;
}

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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"

View 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

View File

@@ -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

View 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"

View 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

View 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

View File

@@ -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

View 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

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View 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"

View 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

View 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

View 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);
}

View 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

View 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

View File

@@ -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

View 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

View 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

View File

@@ -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

View File

@@ -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_

View File

@@ -0,0 +1,2 @@
V8 inspector from node commit (4e8bc7181c1f2491e187477798d433a4488f43d4)

View File

@@ -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

View File

@@ -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

View 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"

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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