初始化

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