no message

This commit is contained in:
gem
2025-02-18 15:21:31 +08:00
commit 2d133e56d7
1980 changed files with 465595 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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.h> // Resolves that memset, memcpy aren't found while APP_PLATFORM >= 22 on Android
#include <algorithm> // for std::find
#include <chrono>
#include <functional>
#include "../PrivateObject.h"
#include "base/std/container/string.h"
#include "base/std/container/unordered_map.h"
#include "base/std/container/unordered_set.h"
#include "HelperMacros.h"
namespace se {
using V8FinalizeFunc = void (*)(Object *seObj);
} // namespace se

View File

@@ -0,0 +1,328 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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.h"
#include <initializer_list>
#include "Value.h"
#include "base/Macros.h"
#include "v8/HelperMacros.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
#include "Object.h"
#include "ScriptEngine.h"
#include "Utils.h"
namespace {
inline v8::Local<v8::Value> createExternal(v8::Isolate *isolate, void *data) {
if (data) {
return v8::External::New(isolate, data);
}
return {};
}
} // namespace
namespace se {
// ------------------------------------------------------- Object
namespace {
// ccstd::unordered_map<ccstd::string, Class *> __clsMap;
v8::Isolate *__isolate = nullptr; // NOLINT
ccstd::vector<Class *> __allClasses; // NOLINT
void invalidConstructor(const v8::FunctionCallbackInfo<v8::Value> &args) {
v8::Local<v8::Object> thisObj = args.This();
v8::Local<v8::String> constructorName = thisObj->GetConstructorName();
v8::String::Utf8Value strConstructorName{args.GetIsolate(), constructorName};
SE_ASSERT(false, "%s 's constructor is not public!", *strConstructorName); // NOLINT(misc-static-assert)
}
} // namespace
Class::Class() {
__allClasses.push_back(this);
}
Class::~Class() = default;
/* static */
Class *Class::create(const ccstd::string &clsName, Object *parent, Object *parentProto, v8::FunctionCallback ctor, void *data) {
auto *cls = ccnew Class();
if (cls != nullptr && !cls->init(clsName, parent, parentProto, ctor, data)) {
delete cls;
cls = nullptr;
}
return cls;
}
Class *Class::create(const std::initializer_list<const char *> &classPath, Object *parent, Object *parentProto, v8::FunctionCallback ctor, void *data) {
se::AutoHandleScope scope;
se::Value currentParent{parent};
for (auto i = 0; i < classPath.size() - 1; i++) {
se::Value tmp;
bool ok = currentParent.toObject()->getProperty(*(classPath.begin() + i), &tmp);
CC_ASSERT(ok); // class or namespace in path is not defined
currentParent = tmp;
}
return create(*(classPath.end() - 1), currentParent.toObject(), parentProto, ctor, data);
}
bool Class::init(const ccstd::string &clsName, Object *parent, Object *parentProto, v8::FunctionCallback ctor, void *data) {
_name = clsName;
_parent = parent;
if (_parent != nullptr) {
_parent->incRef();
}
_parentProto = parentProto;
if (_parentProto != nullptr) {
_parentProto->incRef();
}
_constructor = ctor;
v8::FunctionCallback ctorToSet = _constructor != nullptr ? _constructor : invalidConstructor;
_constructorTemplate.Reset(__isolate, v8::FunctionTemplate::New(__isolate, ctorToSet, createExternal(__isolate, data)));
v8::MaybeLocal<v8::String> jsNameVal = v8::String::NewFromUtf8(__isolate, _name.c_str(), v8::NewStringType::kNormal);
if (jsNameVal.IsEmpty()) {
return false;
}
_constructorTemplate.Get(__isolate)->SetClassName(jsNameVal.ToLocalChecked());
_constructorTemplate.Get(__isolate)->InstanceTemplate()->SetInternalFieldCount(1);
return true;
}
void Class::_setCtor(Object *obj) {
assert(!_ctor.has_value());
_ctor = obj;
if (obj != nullptr) {
obj->root();
obj->incRef();
}
}
void Class::destroy() {
SAFE_DEC_REF(_parent);
SAFE_DEC_REF(_proto);
SAFE_DEC_REF(_parentProto);
if (_ctor.has_value()) {
if (_ctor.value() != nullptr) {
_ctor.value()->unroot();
_ctor.value()->decRef();
}
_ctor.reset();
}
_constructorTemplate.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) {
_constructorTemplate.Get(__isolate)->Inherit(_parentProto->_getClass()->_constructorTemplate.Get(__isolate));
}
v8::Local<v8::Context> context = __isolate->GetCurrentContext();
v8::MaybeLocal<v8::Function> ctor = _constructorTemplate.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, void *data) {
v8::MaybeLocal<v8::String> jsName = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
if (jsName.IsEmpty()) {
return false;
}
_constructorTemplate.Get(__isolate)->PrototypeTemplate()->Set(jsName.ToLocalChecked(), v8::FunctionTemplate::New(__isolate, func, createExternal(__isolate, data)));
return true;
}
bool Class::defineProperty(const char *name, v8::FunctionCallback getter, v8::FunctionCallback setter, void *data) {
v8::MaybeLocal<v8::String> jsName = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
if (jsName.IsEmpty()) {
return false;
}
auto prototypeTemplate = _constructorTemplate.Get(__isolate)->PrototypeTemplate();
auto externalData = createExternal(__isolate, data);
v8::Local<v8::FunctionTemplate> getterTemplate = v8::Local<v8::FunctionTemplate>();
v8::Local<v8::FunctionTemplate> setterTemplate = v8::Local<v8::FunctionTemplate>();
if (getter != nullptr) {
getterTemplate = v8::FunctionTemplate::New(__isolate, getter, externalData);
}
if (setter != nullptr) {
setterTemplate = v8::FunctionTemplate::New(__isolate, setter, externalData);
}
prototypeTemplate->SetAccessorProperty(jsName.ToLocalChecked(), getterTemplate, setterTemplate);
return true;
}
bool Class::defineProperty(const std::initializer_list<const char *> &names, v8::FunctionCallback getter, v8::FunctionCallback setter, void *data) {
bool ret = true;
for (const auto *name : names) {
ret &= defineProperty(name, getter, setter, data);
}
return ret;
}
bool Class::defineStaticFunction(const char *name, v8::FunctionCallback func, void *data) {
v8::MaybeLocal<v8::String> jsName = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
if (jsName.IsEmpty()) {
return false;
}
_constructorTemplate.Get(__isolate)->Set(jsName.ToLocalChecked(), v8::FunctionTemplate::New(__isolate, func, createExternal(__isolate, data)));
return true;
}
bool Class::defineStaticProperty(const char *name, v8::FunctionCallback getter, v8::FunctionCallback setter, void *data) {
v8::MaybeLocal<v8::String> jsName = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
if (jsName.IsEmpty()) {
return false;
}
auto externalData = createExternal(__isolate, data);
v8::Local<v8::FunctionTemplate> getterTemplate = v8::Local<v8::FunctionTemplate>();
v8::Local<v8::FunctionTemplate> setterTemplate = v8::Local<v8::FunctionTemplate>();
if (getter != nullptr) {
getterTemplate = v8::FunctionTemplate::New(__isolate, getter, externalData);
}
if (setter != nullptr) {
setterTemplate = v8::FunctionTemplate::New(__isolate, setter, externalData);
}
_constructorTemplate.Get(__isolate)->SetAccessorProperty(jsName.ToLocalChecked(), getterTemplate, setterTemplate);
return true;
}
bool Class::defineStaticProperty(const char *name, const Value &value, PropertyAttribute attribute /* = PropertyAttribute::NONE */) {
v8::MaybeLocal<v8::String> jsName = v8::String::NewFromUtf8(__isolate, name, v8::NewStringType::kNormal);
if (jsName.IsEmpty()) {
return false;
}
v8::Local<v8::Value> v8Val;
internal::seToJsValue(__isolate, value, &v8Val);
_constructorTemplate.Get(__isolate)->Set(jsName.ToLocalChecked(), v8Val, static_cast<v8::PropertyAttribute>(attribute));
return true;
}
bool Class::defineFinalizeFunction(V8FinalizeFunc finalizeFunc) {
CC_ASSERT_NOT_NULL(finalizeFunc);
_finalizeFunc = finalizeFunc;
return true;
}
// v8::Local<v8::Object> Class::_createJSObject(const ccstd::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) { // NOLINT
v8::MaybeLocal<v8::Object> ret = cls->_constructorTemplate.Get(__isolate)->InstanceTemplate()->NewInstance(__isolate->GetCurrentContext());
CC_ASSERT(!ret.IsEmpty());
return ret.ToLocalChecked();
}
Object *Class::getProto() const {
return _proto;
}
V8FinalizeFunc Class::_getFinalizeFunction() const { // NOLINT
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,173 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
#include "../Define.h"
#include "../Value.h"
#include "Base.h"
#include "base/std/optional.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.
* @param[in] data A data pointer attach to the function callback.
* @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 ccstd::string &clsName, Object *parent, Object *parentProto, v8::FunctionCallback ctor, void *data = nullptr);
static Class *create(const std::initializer_list<const char *> &classPath, Object *parent, Object *parentProto, v8::FunctionCallback ctor, void *data = nullptr);
/**
* @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.
* @param[in] data A data pointer attach to the function callback.
* @return true if succeed, otherwise false.
*/
bool defineFunction(const char *name, v8::FunctionCallback func, void *data = nullptr);
/**
* @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.
* @param[in] data A data pointer attach to the property's callback
* @return true if succeed, otherwise false.
*/
bool defineProperty(const char *name, v8::FunctionCallback getter, v8::FunctionCallback setter, void *data = nullptr);
bool defineProperty(const std::initializer_list<const char *> &names, v8::FunctionCallback getter, v8::FunctionCallback setter, void *data = nullptr);
/**
* @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.
* @param[in] data A data pointer attach to static function callback
* @return true if succeed, otherwise false.
*/
bool defineStaticFunction(const char *name, v8::FunctionCallback func, void *data = nullptr);
/**
* @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.
* @param[in] data A data pointer attach to static property callback
* @return true if succeed, otherwise false.
*/
bool defineStaticProperty(const char *name, v8::FunctionCallback getter, v8::FunctionCallback setter, void *data = nullptr);
/**
* @brief Defines a static property with a value. Only JavaScript constructor object will have this property.
* @param[in] name A null-terminated UTF8 string containing the property name.
* @param[in] value A value to be set on the constructor.
* @param[in] attribute An attribute to describe the property.
* @return true if succeed, otherwise false.
*/
bool defineStaticProperty(const char *name, const Value &value, PropertyAttribute attribute = PropertyAttribute::NONE);
/**
* @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; // NOLINT(readability-identifier-naming)
void _setCtor(Object *obj); // NOLINT(readability-identifier-naming)
inline const ccstd::optional<Object *> &_getCtor() const { return _ctor; } // NOLINT(readability-identifier-naming)
private:
Class();
~Class();
void setCreateProto(bool createProto);
bool init(const ccstd::string &clsName, Object *parent, Object *parentProto, v8::FunctionCallback ctor, void *data = nullptr);
void destroy();
static void cleanup();
// static v8::Local<v8::Object> _createJSObject(const ccstd::string &clsName, Class** outCls);
static v8::Local<v8::Object> _createJSObjectWithClass(Class *cls); // NOLINT(readability-identifier-naming)
static void setIsolate(v8::Isolate *isolate);
ccstd::string _name;
Object *_parent{nullptr};
Object *_parentProto{nullptr};
Object *_proto{nullptr};
ccstd::optional<Object *> _ctor;
v8::FunctionCallback _constructor{nullptr};
v8::UniquePersistent<v8::FunctionTemplate> _constructorTemplate;
V8FinalizeFunc _finalizeFunc{nullptr};
bool _createProto{true};
friend class ScriptEngine;
friend class Object;
friend class JSBPersistentHandleVisitor;
};
} // namespace se
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8

View File

@@ -0,0 +1,181 @@
/****************************************************************************
Copyright (c) 2021-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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 "HelperMacros.h"
#include "../State.h"
#include "../ValueArrayPool.h"
#include "Class.h"
#include "Object.h"
#include "ScriptEngine.h"
#include "Utils.h"
#if defined(RECORD_JSB_INVOKING)
namespace {
bool cmp(const std::pair<const char *, std::tuple<int, uint64_t>> &a, const std::pair<const char *, std::tuple<int, uint64_t>> &b) {
return std::get<1>(a.second) > std::get<1>(b.second);
}
unsigned int __jsbInvocationCount; // NOLINT(readability-identifier-naming)
ccstd::unordered_map<char const *, std::tuple<int, uint64_t>> __jsbFunctionInvokedRecords; // NOLINT(readability-identifier-naming)
} // namespace
JsbInvokeScopeT::JsbInvokeScopeT(const char *functionName) : _functionName(functionName) {
_start = std::chrono::high_resolution_clock::now();
__jsbInvocationCount++;
}
JsbInvokeScopeT::~JsbInvokeScopeT() {
auto &ref = __jsbFunctionInvokedRecords[_functionName];
std::get<0>(ref) += 1;
std::get<1>(ref) += std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - _start).count();
}
#endif
void printJSBInvokeAtFrame(int n) {
#if defined(RECORD_JSB_INVOKING)
static int cnt = 0;
cnt += 1;
if (cnt % n == 0) {
printJSBInvoke();
}
#endif
}
void clearRecordJSBInvoke() {
#if defined(RECORD_JSB_INVOKING)
__jsbInvocationCount = 0;
__jsbFunctionInvokedRecords.clear();
#endif
}
void printJSBInvoke() {
#if defined(RECORD_JSB_INVOKING)
static ccstd::vector<std::pair<const char *, std::tuple<int, uint64_t>>> pairs;
for (const auto &it : __jsbFunctionInvokedRecords) {
pairs.emplace_back(it); // NOLINT
}
std::sort(pairs.begin(), pairs.end(), cmp);
cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::LEVEL_DEBUG, "Start print JSB function record info....... %d times", __jsbInvocationCount);
for (const auto &pair : pairs) {
cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::LEVEL_DEBUG, "\t%s takes %.3lf ms, invoked %u times,", pair.first, std::get<1>(pair.second) / 1000000.0, std::get<0>(pair.second));
}
pairs.clear();
cc::Log::logMessage(cc::LogType::KERNEL, cc::LogLevel::LEVEL_DEBUG, "End print JSB function record info.......\n");
#endif
}
SE_HOT void jsbFunctionWrapper(const v8::FunctionCallbackInfo<v8::Value> &v8args, se_function_ptr func, const char *funcName) {
bool ret = false;
v8::Isolate *isolate = v8args.GetIsolate();
v8::HandleScope scope(isolate);
bool needDeleteValueArray{false};
se::ValueArray &args = se::gValueArrayPool.get(v8args.Length(), needDeleteValueArray);
se::CallbackDepthGuard depthGuard{args, se::gValueArrayPool._depth, needDeleteValueArray};
se::internal::jsToSeArgs(v8args, args);
se::Object *thisObject = se::internal::getPrivate(isolate, v8args.This());
se::State state(thisObject, args);
ret = func(state);
if (!ret) {
SE_LOGE("[ERROR] Failed to invoke %s\n", funcName);
}
se::internal::setReturnValue(state.rval(), v8args);
}
SE_HOT void jsbFinalizeWrapper(se::Object *thisObject, se_function_ptr func, const char *funcName) {
auto *engine = se::ScriptEngine::getInstance();
engine->_setGarbageCollecting(true);
se::State state(thisObject);
bool ret = func(state);
if (!ret) {
SE_LOGE("[ERROR] Failed to invoke %s\n", funcName);
}
engine->_setGarbageCollecting(false);
}
SE_HOT void jsbConstructorWrapper(const v8::FunctionCallbackInfo<v8::Value> &v8args, se_function_ptr func, se_finalize_ptr finalizeCb, se::Class *cls, const char *funcName) {
v8::Isolate *isolate = v8args.GetIsolate();
v8::HandleScope scope(isolate);
bool ret = true;
bool needDeleteValueArray{false};
se::ValueArray &args = se::gValueArrayPool.get(v8args.Length(), needDeleteValueArray);
se::CallbackDepthGuard depthGuard{args, se::gValueArrayPool._depth, needDeleteValueArray};
se::internal::jsToSeArgs(v8args, args);
se::Object *thisObject = se::Object::_createJSObject(cls, v8args.This());
thisObject->_setFinalizeCallback(finalizeCb);
se::State state(thisObject, args);
ret = func(state);
if (!ret) {
SE_LOGE("[ERROR] Failed to invoke %s\n", funcName);
}
se::Value property;
bool foundCtor = false;
if (!cls->_getCtor().has_value()) {
foundCtor = thisObject->getProperty("_ctor", &property, true);
if (foundCtor) {
cls->_setCtor(property.toObject());
} else {
cls->_setCtor(nullptr);
}
} else {
auto *ctorObj = cls->_getCtor().value();
if (ctorObj != nullptr) {
property.setObject(ctorObj);
foundCtor = true;
}
}
if (foundCtor) {
property.toObject()->call(args, thisObject);
}
}
SE_HOT void jsbGetterWrapper(const v8::FunctionCallbackInfo<v8::Value> &v8args, se_function_ptr func, const char *funcName) {
v8::Isolate *isolate = v8args.GetIsolate();
v8::HandleScope scope(isolate);
bool ret = true;
se::Object *thisObject = se::internal::getPrivate(isolate, v8args.This());
se::State state(thisObject);
ret = func(state);
if (!ret) {
SE_LOGE("[ERROR] Failed to invoke %s\n", funcName);
}
se::internal::setReturnValue(state.rval(), v8args);
}
SE_HOT void jsbSetterWrapper(const v8::FunctionCallbackInfo<v8::Value> &v8args, se_function_ptr func, const char *funcName) {
v8::Isolate *isolate = v8args.GetIsolate();
v8::HandleScope scope(isolate);
bool ret = true;
se::Object *thisObject = se::internal::getPrivate(isolate, v8args.This());
bool needDeleteValueArray{false};
se::ValueArray &args = se::gValueArrayPool.get(1, needDeleteValueArray);
se::CallbackDepthGuard depthGuard{args, se::gValueArrayPool._depth, needDeleteValueArray};
se::Value &data{args[0]};
se::internal::jsToSeValue(isolate, v8args[0], &data);
se::State state(thisObject, args);
ret = func(state);
if (!ret) {
SE_LOGE("[ERROR] Failed to invoke %s\n", funcName);
}
}

View File

@@ -0,0 +1,211 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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 "v8.h"
#include <algorithm>
#include <chrono>
#include <map>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include "../config.h"
#include "base/Log.h"
#include "base/Macros.h"
#include "base/std/container/string.h"
// #define RECORD_JSB_INVOKING
#ifndef CC_DEBUG
#undef RECORD_JSB_INVOKING
#endif
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
#if defined(RECORD_JSB_INVOKING)
class JsbInvokeScopeT {
public:
JsbInvokeScopeT(const char *functionName);
~JsbInvokeScopeT();
private:
const char *_functionName;
std::chrono::time_point<std::chrono::high_resolution_clock> _start;
};
#define JsbInvokeScope(arg) JsbInvokeScopeT invokeScope(arg); // NOLINT(readability-identifier-naming)
#else
// NOLINTNEXTLINE(readability-identifier-naming)
#define JsbInvokeScope(arg) \
do { \
} while (0)
#endif
template <typename T, typename STATE>
constexpr inline T *SE_THIS_OBJECT(STATE &s) { // NOLINT(readability-identifier-naming)
return reinterpret_cast<T *>(s.nativeThisObject());
}
template <typename T>
constexpr typename std::enable_if<std::is_enum<T>::value, char *>::type SE_UNDERLYING_TYPE_NAME() { // NOLINT(readability-identifier-naming)
return typeid(std::underlying_type_t<T>).name();
}
template <typename T>
constexpr typename std::enable_if<!std::is_enum<T>::value, char *>::type SE_UNDERLYING_TYPE_NAME() { // NOLINT(readability-identifier-naming)
return typeid(T).name();
}
void clearRecordJSBInvoke();
void printJSBInvoke();
void printJSBInvokeAtFrame(int n);
namespace se {
class Class;
class Object;
class State;
} // namespace se
using se_function_ptr = bool (*)(se::State &state);
using se_finalize_ptr = void (*)(se::Object *seObj);
void jsbFunctionWrapper(const v8::FunctionCallbackInfo<v8::Value> &,
se_function_ptr,
const char *);
void jsbFinalizeWrapper(se::Object *thisObject,
se_function_ptr,
const char *);
void jsbConstructorWrapper(const v8::FunctionCallbackInfo<v8::Value> &,
se_function_ptr,
se_finalize_ptr finalizeCb,
se::Class *,
const char *);
void jsbGetterWrapper(const v8::FunctionCallbackInfo<v8::Value> &,
se_function_ptr,
const char *);
void jsbSetterWrapper(const v8::FunctionCallbackInfo<v8::Value> &,
se_function_ptr,
const char *);
#ifdef __GNUC__
#define SE_UNUSED __attribute__((unused))
#define SE_HOT __attribute__((hot))
#else
#define SE_UNUSED
#define SE_HOT
#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 // NOLINT(readability-identifier-naming, bugprone-reserved-identifier)
#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) { \
JsbInvokeScope(#funcName); \
jsbFunctionWrapper(_v8args, funcName, #funcName); \
}
#define SE_BIND_FUNC_FAST(funcName) \
void funcName##Registry(const v8::FunctionCallbackInfo<v8::Value> &_v8args) { \
JsbInvokeScope(#funcName); \
auto *thisObject = static_cast<se::Object *>(_v8args.This()->GetAlignedPointerFromInternalField(0)); \
auto *nativeObject = thisObject != nullptr ? thisObject->getPrivateData() : nullptr; \
funcName(nativeObject); \
}
#define SE_BIND_FINALIZE_FUNC(funcName) \
void funcName##Registry(se::Object *thisObject) { \
JsbInvokeScope(#funcName); \
if (thisObject == nullptr) \
return; \
jsbFinalizeWrapper(thisObject, funcName, #funcName); \
}
#define SE_DECLARE_FINALIZE_FUNC(funcName) \
void funcName##Registry(se::Object *thisObject);
// 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) { \
JsbInvokeScope(#funcName); \
jsbConstructorWrapper(_v8args, funcName, _SE(finalizeCb), cls, #funcName); \
}
#define SE_BIND_PROP_GET_IMPL(funcName, postFix) \
void funcName##postFix##Registry(const v8::FunctionCallbackInfo<v8::Value> &_v8args) { \
JsbInvokeScope(#funcName); \
jsbGetterWrapper(_v8args, funcName, #funcName); \
}
#define SE_BIND_PROP_GET(funcName) SE_BIND_PROP_GET_IMPL(funcName, )
#define SE_BIND_FUNC_AS_PROP_GET(funcName) SE_BIND_PROP_GET_IMPL(funcName, _asGetter)
#define SE_BIND_PROP_SET_IMPL(funcName, postFix) \
void funcName##postFix##Registry(const v8::FunctionCallbackInfo<v8::Value> &_v8args) { \
JsbInvokeScope(#funcName); \
jsbSetterWrapper(_v8args, funcName, #funcName); \
}
#define SE_BIND_PROP_SET(funcName) SE_BIND_PROP_SET_IMPL(funcName, )
#define SE_BIND_FUNC_AS_PROP_SET(funcName) SE_BIND_PROP_SET_IMPL(funcName, _asSetter)
#define SE_TYPE_NAME(t) typeid(t).name()
#define SE_QUOTEME_(x) #x // NOLINT(readability-identifier-naming)
#define SE_QUOTEME(x) SE_QUOTEME_(x)
// IDEA: implement this macro
// #define SE_REPORT_ERROR(fmt, ...) SE_LOGE(SE_STR_CONCAT3("[ERROR] ( %s, %d): ", fmt, "\n"), __FILE__, __LINE__, ##__VA_ARGS__)
#define SE_REPORT_ERROR(fmt, ...) selogMessage(cc::LogLevel::ERR, "[SE_ERROR]", (" (%s, %d): " fmt), __FILE__, __LINE__, ##__VA_ARGS__)
#if CC_DEBUG > 0
#define SE_ASSERT(cond, fmt, ...) \
do { \
if (!(cond)) { \
selogMessage(cc::LogLevel::ERR, "[SE_ASSERT]", (" (%s, %d): " fmt), __FILE__, __LINE__, ##__VA_ARGS__); \
CC_ABORT(); \
} \
} while (false)
#else
#define SE_ASSERT(cond, fmt, ...)
#endif // #if CC_DEBUG > 0
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8

View File

@@ -0,0 +1,40 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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 "MissingSymbols.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
//TODO(PatriceJiang): modify this when OHOS llvm upgrade
#if CC_PLATFORM == CC_PLATFORM_OHOS
#include <string.h>
extern "C" {
int local_bcmp(const void *cs, const void *ct, size_t count) {
return memcmp(cs, ct, count);
}
int bcmp(const void *cs, const void *ct, size_t count) __attribute__((weak, alias("local_bcmp")));
} // extern "C"
#endif
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8

View File

@@ -0,0 +1,31 @@
/****************************************************************************
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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 <cerrno>
#include <cstddef>
#include <cstring>
#include "../config.h"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,706 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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.h"
#include "bindings/jswrapper/PrivateObject.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
#include "../RefCounter.h"
#include "../Value.h"
#include "Base.h"
#include "ObjectWrap.h"
#include "base/HasMemberFunction.h"
#include <memory>
// DEBUG ONLY:
// Set `__object_id__` && `__native_class_name__` for js object
#ifndef CC_DEBUG_JS_OBJECT_ID
#define CC_DEBUG_JS_OBJECT_ID 0
#endif
#define JSB_TRACK_OBJECT_CREATION 0
namespace se {
class Class;
class ScriptEngine;
/**
* 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 ES6 Map Object like `new Map()` in JS.
* @return A JavaScript Object, or nullptr if there is an error.
* @note The return value (non-null) has to be released manually.
*/
static Object *createMapObject();
/**
* @brief Creates a ES6 Set Object like `new Set()` in JS.
* @return A JavaScript Object, or nullptr if there is an error.
* @note The return value (non-null) has to be released manually.
*/
static Object *createSetObject();
/**
* @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, const void *data, size_t byteLength);
/**
* @brief Creates a JavaScript Typed Array Object with a se::Object, which is a ArrayBuffer,
if provide a null pointer,then will create a empty JavaScript Typed Array Object.
* @param[in] type The format of typed array.
* @param[in] obj A ArrayBuffer to TypedArray.
* @param[in] offset Offset of ArrayBuffer to create with.
* @param[in] byteLength The number of bytes pointed to by the parameter bytes.
* @return A JavaScript Typed Array Object which refers to the ArrayBuffer Object, or nullptr if there is an error.
* @note The return value (non-null) has to be released manually.
*/
static Object *createTypedArrayWithBuffer(TypedArrayType type, const Object *obj);
static Object *createTypedArrayWithBuffer(TypedArrayType type, const Object *obj, size_t offset);
static Object *createTypedArrayWithBuffer(TypedArrayType type, const Object *obj, size_t offset, 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(const void *data, size_t byteLength);
using BufferContentsFreeFunc = void (*)(void *contents, size_t byteLength, void *userData);
static Object *createExternalArrayBufferObject(void *contents, size_t byteLength, BufferContentsFreeFunc freeFunc, void *freeUserData = nullptr);
/**
* @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 ccstd::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 Creates a JavaScript Native Binding Object from an JS constructor with no arguments, which behaves as `new MyClass();` in JS.
* @param[in] constructor The JS constructor
* @return A JavaScript object, or nullptr if there is an error.
* @note The return value (non-null) has to be released manually.
*/
static Object *createObjectWithConstructor(se::Object *constructor);
/**
* @brief Creates a JavaScript Native Binding Object from an JS constructor with arguments, which behaves as `new MyClass(arg0, arg1, arg2, ...);` in JS.
* @param[in] constructor The JS constructor
* @param[in] args The arguments passed to the JS constructor
* @return A JavaScript object, or nullptr if there is an error.
* @note The return value (non-null) has to be released manually.
*/
static Object *createObjectWithConstructor(se::Object *constructor, const ValueArray &args);
/**
* Gets the Proxy Target object
* @param proxy The JavaScript Proxy object.
* @return The target JavaScript object of the parameter.
*/
static Object *createProxyTarget(se::Object *proxy);
/**
* @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.
* @deprecated Use NativePtrToObjectMap to query the native object.
*/
CC_DEPRECATED(3.7)
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.
*/
inline bool getProperty(const char *name, Value *data) {
return getProperty(name, data, false);
}
bool getProperty(const char *name, Value *data, bool cachePropertyName);
inline bool getProperty(const ccstd::string &name, Value *value) {
return getProperty(name.c_str(), 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 &data);
inline bool setProperty(const ccstd::string &name, const Value &value) {
return setProperty(name.c_str(), 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::FunctionCallback getter, v8::FunctionCallback setter);
bool defineOwnProperty(const char *name, const se::Value &value, bool writable = true, bool enumerable = true, bool configurable = true);
/**
* @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 a ES6 Map.
* @return true if object is a Map, otherwise false.
*/
bool isMap() const;
/**
* @brief Tests whether an object is a ES6 WeakMap.
* @return true if object is a WeakMap, otherwise false.
*/
bool isWeakMap() const;
/**
* @brief Tests whether an object is a ES6 Set.
* @return true if object is a Set, otherwise false.
*/
bool isSet() const;
/**
* @brief Tests whether an object is a ES6 WeakSet.
* @return true if object is a WeakSet, otherwise false.
*/
bool isWeakSet() const;
/**
* @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 Tests whether an object is a proxy object.
* @return true if object is a proxy object, otherwise false.
*/
bool isProxy() 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(ccstd::vector<ccstd::string> *allKeys) const;
// ES6 Map operations
/**
* @brief Clear all elements in a ES6 map object.
*/
void clearMap();
/**
* @brief Remove an element in a ES6 map by a key.
* @param[in] key The key of the element to remove, it could be any type that se::Value supports.
* @return true if succeed, otherwise false.
*/
bool removeMapElement(const Value &key);
/**
* @brief Get an element in a ES6 map by a key.
* @param[in] key Key of the element to get, it could be any type that se::Value supports.
* @param[out] outValue Out parameter, On success, *outValue receives the current value of the map element, or nullptr if no such element is found
* @return true if succeed, otherwise false.
*/
bool getMapElement(const Value &key, Value *outValue) const;
/**
* @brief Set an element in a ES6 map by a key.
* @param[in] key Key of the element to get, it could be any type that se::Value supports.
* @param[in] value The value to set the map.
* @return true if succeed, otherwise false.
*/
bool setMapElement(const Value &key, const Value &value);
/**
* @brief Get the size of a ES6 map
* @return The size of a ES6 map
*/
uint32_t getMapSize() const;
/**
* @brief Get all elements in a ES6 map
* @return All elements in a ES6 map, they're stored in a std::vector instead of `std::map/unordered_map` since we don't know how to compare or make a hash for `se::Value`s.
*/
ccstd::vector<std::pair<Value, Value>> getAllElementsInMap() const;
// ES6 Set operations
/**
* @brief Clear all elements in a ES6 set object.
*/
void clearSet();
/**
* @brief Remove an element in a ES6 set.
* @param[in] value The value to remove.
* @return true if succeed, otherwise false.
*/
bool removeSetElement(const Value &value);
/**
* @brief Add an element to a ES6 set.
* @param[in] value The value to set the set.
* @return true if succeed, otherwise false.
*/
bool addSetElement(const Value &value);
/**
* @brief Check whether the value is in a ES6 set.
* @return true if the value is in the ES6 set, otherwise false.
*/
bool isElementInSet(const Value &value) const;
/**
* @brief Get the size of a ES6 set.
* @return The size of a ES6 set.
*/
uint32_t getSetSize() const;
/**
* @brief Get all elements in a ES6 set.
* @return All elements in a ES6 set.
*/
ValueArray getAllElementsInSet() const;
void setPrivateObject(PrivateObjectBase *data);
template <typename T>
inline void setPrivateObject(TypedPrivateObject<T> *data) {
setPrivateObject(static_cast<PrivateObjectBase *>(data));
if constexpr (cc::has_setScriptObject<T, void(Object *)>::value) {
data->template get<T>()->setScriptObject(this);
}
}
PrivateObjectBase *getPrivateObject() const;
/*
* @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.
*/
inline void *getPrivateData() const {
return _privateData;
}
/**
* @brief Sets a pointer to private data on an object and use smart pointer to hold it.
*
* If the pointer is an instance of `cc::RefCounted`, an `cc::IntrusivePtr` will be created to hold
* the reference to the object, otherwise a `std::shared_ptr` object will be used.
* When the JS object is freed by GC, the corresponding smart pointer `IntrusivePtr/shared_ptr` will also be destroyed.
*
* If you do not want the pointer to be released by GC, you can call `setRawPrivateData`.
*
* @param[in] data A void* to set as the object's private data.
* @note This method will associate private data with se::Object by ccstd::unordered_map::emplace.
* It's used for search a se::Object via a void* private data.
*/
template <typename T>
inline void setPrivateData(T *data) {
static_assert(!std::is_void<T>::value, "void * is not allowed for private data");
setPrivateObject(se::make_shared_private_object(data));
}
/**
* @brief Use a InstrusivePtr to hold private data on the se::Object.
*
* @tparam T
* @param data A intrusive pointer object
*/
template <typename T>
inline void setPrivateData(const cc::IntrusivePtr<T> &data) {
setPrivateObject(se::ccintrusive_ptr_private_object(data));
}
/**
* @brief Use a std::shared_ptr to hold private data on the se::Object.
*
* @tparam T
* @param data A shared_ptr object
*/
template <typename T>
inline void setPrivateData(const std::shared_ptr<T> &data) {
setPrivateObject(se::shared_ptr_private_object(data));
}
/**
* @brief Set pointer to the private data on an object and will not use smart pointer to hold it.
*
* @tparam T
* @param data
* @param tryDestroyInGC When GCing the JS object, whether to `delete` the `data` pointer.
*/
template <typename T>
inline void setRawPrivateData(T *data, bool tryDestroyInGC = false) {
static_assert(!std::is_void<T>::value, "void * is not allowed for private data");
auto *privateObject = se::rawref_private_object(data);
if (tryDestroyInGC) {
privateObject->tryAllowDestroyInGC();
}
setPrivateObject(privateObject);
}
/**
* @brief Get the underlying private data as std::shared_ptr
*
* @tparam T
* @return std::shared_ptr<T>
*/
template <typename T>
inline std::shared_ptr<T> getPrivateSharedPtr() const {
assert(_privateObject->isSharedPtr());
return static_cast<se::SharedPtrPrivateObject<T> *>(_privateObject)->getData();
}
/**
* @brief Get the underlying private data as InstrusivePtr
*
* @tparam T
* @return cc::IntrusivePtr<T>
*/
template <typename T>
inline cc::IntrusivePtr<T> getPrivateInstrusivePtr() const {
assert(_privateObject->isCCIntrusivePtr());
return static_cast<se::CCIntrusivePtrPrivateObject<T> *>(_privateObject)->getData();
}
template <typename T>
inline T *getTypedPrivateData() const {
return reinterpret_cast<T *>(getPrivateData());
}
/**
* @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 Sets whether to clear the mapping of native object & se::Object in finalizer
*/
void setClearMappingInFinalizer(bool v) { _clearMappingInFinalizer = v; }
/**
* @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.
*/
ccstd::string toString() const;
ccstd::string toStringExt() const;
// Private API used in wrapper
static Object *_createJSObject(Class *cls, v8::Local<v8::Object> obj); // NOLINT(readability-identifier-naming)
v8::Local<v8::Object> _getJSObject() const; // NOLINT(readability-identifier-naming)
ObjectWrap &_getWrap(); // NOLINT(readability-identifier-naming)
Class *_getClass() const; // NOLINT(readability-identifier-naming)
void _setFinalizeCallback(V8FinalizeFunc finalizeCb); // NOLINT(readability-identifier-naming)
bool _isNativeFunction() const; // NOLINT(readability-identifier-naming)
//
#if CC_DEBUG && CC_DEBUG_JS_OBJECT_ID
uint32_t getObjectId() const { return _objectId; }
#endif
private:
static void nativeObjectFinalizeHook(Object *seObj);
static void setIsolate(v8::Isolate *isolate);
static void cleanup();
Object();
~Object() override;
bool init(Class *cls, v8::Local<v8::Object> obj);
v8::Local<v8::Value> getProxyTarget() const;
Class *_cls{nullptr};
ObjectWrap _obj;
uint32_t _rootCount{0};
PrivateObjectBase *_privateObject{nullptr};
void *_privateData{nullptr};
V8FinalizeFunc _finalizeCb{nullptr};
bool _clearMappingInFinalizer{true};
#if CC_DEBUG && CC_DEBUG_JS_OBJECT_ID
uint32_t _objectId = 0;
#endif
#if JSB_TRACK_OBJECT_CREATION
ccstd::string _objectCreationStackFrame;
#endif
friend class ScriptEngine;
friend class JSBPersistentHandleVisitor;
};
} // namespace se
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8

View File

@@ -0,0 +1,151 @@
/****************************************************************************
Copyright Joyent, Inc. and other Node contributors.
Copyright (c) 2021 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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"
#include "Object.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
namespace {
bool gIsIsolateValid = false;
}
namespace se {
/* static */
void ObjectWrap::setIsolateValid(bool valid) {
gIsIsolateValid = valid;
}
ObjectWrap::ObjectWrap() = default;
ObjectWrap::~ObjectWrap() {
if (!gIsIsolateValid || persistent().IsEmpty()) {
return;
}
//cjh CC_ASSERT(persistent().IsNearDeath());
persistent().ClearWeak();
persistent().Reset();
}
bool ObjectWrap::init(v8::Local<v8::Object> handle, Object *parent, bool registerWeak) {
CC_ASSERT(persistent().IsEmpty());
_parent = parent;
_registerWeakCallback = registerWeak;
persistent().Reset(v8::Isolate::GetCurrent(), handle);
makeWeak();
return true;
}
void ObjectWrap::setFinalizeCallback(FinalizeFunc finalizeCb) {
_finalizeCb = finalizeCb;
}
/*static*/
void *ObjectWrap::unwrap(v8::Local<v8::Object> handle, uint32_t fieldIndex) {
CC_ASSERT(!handle.IsEmpty());
CC_ASSERT(handle->InternalFieldCount() > 0);
CC_ASSERT(fieldIndex >= 0 && fieldIndex < 1);
return handle->GetAlignedPointerFromInternalField(static_cast<int>(fieldIndex));
}
void ObjectWrap::wrap(void *nativeObj, uint32_t fieldIndex) {
CC_ASSERT(handle()->InternalFieldCount() > 0);
CC_ASSERT(fieldIndex >= 0 && fieldIndex < 1);
handle()->SetAlignedPointerInInternalField(static_cast<int>(fieldIndex), nativeObj);
if (nativeObj) {
persistent().SetWrapperClassId(MAGIC_CLASS_ID_JSB);
} else {
persistent().SetWrapperClassId(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::makeWeak() {
// V8 offical documentation said that:
// kParameter will pass a void* parameter back to the callback, kInternalFields
// will pass the first two internal fields back to the callback,
// kFinalizer will pass a void* parameter back, but is invoked before the object is
// actually collected, so it can be resurrected. In the last case, it is not
// possible to request a second pass callback.
// enum class WeakCallbackType { kParameter, kInternalFields, kFinalizer };
//
// NOTE: We get random crashes while previewing material in editor's inspector window,
// the reason is that kFinalizer will trigger weak callback when some assets are
// still being used, jsbinding code will get a dead se::Object pointer that was
// freed by weak callback. According V8 documentation, kParameter is a better option.
if (_registerWeakCallback) {
persistent().SetWeak(_parent, weakCallback, v8::WeakCallbackType::kParameter);
} else {
persistent().SetWeak();
}
// persistent().MarkIndependent();
}
void ObjectWrap::ref() {
CC_ASSERT(!persistent().IsEmpty());
persistent().ClearWeak();
_refs++;
}
void ObjectWrap::unref() {
if (!gIsIsolateValid) {
return;
}
CC_ASSERT(!persistent().IsEmpty());
CC_ASSERT(!persistent().IsWeak());
CC_ASSERT_GT(_refs, 0);
if (--_refs == 0) {
makeWeak();
}
}
/*static*/
void ObjectWrap::weakCallback(const v8::WeakCallbackInfo<Object> &data) {
Object *seObj = data.GetParameter();
ObjectWrap *wrap = &seObj->_getWrap();
CC_ASSERT(wrap->_refs == 0);
wrap->_handle.Reset();
if (wrap->_finalizeCb != nullptr) {
wrap->_finalizeCb(seObj);
} else {
CC_ABORT();
}
}
} // namespace se
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8

View File

@@ -0,0 +1,88 @@
/****************************************************************************
Copyright Joyent, Inc. and other Node contributors.
Copyright (c) 2021 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
#include "../PrivateObject.h"
#include "Base.h"
namespace se {
class ObjectWrap {
public:
static constexpr uint16_t MAGIC_CLASS_ID_JSB = 0x1234;
ObjectWrap();
~ObjectWrap();
bool init(v8::Local<v8::Object> handle, Object *parent, bool registerWeak);
using FinalizeFunc = void (*)(Object *seObj);
void setFinalizeCallback(FinalizeFunc finalizeCb);
v8::Local<v8::Object> handle();
v8::Local<v8::Object> handle(v8::Isolate *isolate);
v8::Persistent<v8::Object> &persistent();
void wrap(void *nativeObj, uint32_t fieldIndex);
static void *unwrap(v8::Local<v8::Object> handle, uint32_t fieldIndex);
/* 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();
static void setIsolateValid(bool valid);
private:
static void weakCallback(const v8::WeakCallbackInfo<Object> &data);
void makeWeak();
int _refs{0}; // ro
v8::Persistent<v8::Object> _handle;
FinalizeFunc _finalizeCb{nullptr};
Object *_parent{nullptr};
bool _registerWeakCallback{false};
};
} // namespace se
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,441 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
#include "../Value.h"
#include "Base.h"
#include <thread>
#if SE_ENABLE_INSPECTOR
namespace node {
namespace inspector {
class Agent;
}
class Environment;
class IsolateData;
} // namespace node
#endif
namespace se {
class Object;
class Class;
class Value;
/**
* A stack-allocated class that governs a number of local handles.
* It's only implemented for v8 wrapper now.
* Other script engine wrappers have empty implementation for this class.
* It's used at the beginning of executing any wrapper API.
*/
class AutoHandleScope {
public:
AutoHandleScope();
~AutoHandleScope();
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.
*/
CC_DEPRECATED(3.6.0)
static void destroyInstance();
ScriptEngine();
~ScriptEngine();
/**
* @brief Gets the global object of JavaScript VM.
* @return The se::Object stores the global JavaScript object.
*/
Object *getGlobalObject() const;
using RegisterCallback = bool (*)(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 Adds a callback for registering a native binding module, which will not be removed by ScriptEngine::cleanup.
* @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 addPermanentRegisterCallback(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 Starts the script engine with isolate.
* @return true if succeed, otherwise false.
* @note This method will invoke all callbacks of native binding modules by the order of registration.
*/
bool start(v8::Isolate *isolate);
/**
* @brief Initializes script engine.
* @return true if succeed, otherwise false.
* @note This method will create JavaScript context and global object.
*/
bool init();
/**
* @brief Initializes script engine with isolate.
* @return true if succeed, otherwise false.
* @note This method will create JavaScript context and global object.
*/
bool init(v8::Isolate *isolate);
/**
* @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] script 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] ret 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 *script, uint32_t length = 0, Value *ret = nullptr, const char *fileName = nullptr);
/**
* @brief Compile script file into v8::ScriptCompiler::CachedData and save to file.
* @param[in] path The path of script file.
* @param[in] pathBc The location where bytecode file should be written to. The path should be ends with ".bc", which indicates a bytecode file.
* @return true if succeed, otherwise false.
*/
bool saveByteCodeToFile(const ccstd::string &path, const ccstd::string &pathBc);
/**
* @brief Grab a snapshot of the current JavaScript execution stack.
* @return current stack trace string
*/
ccstd::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 ccstd::string &, const std::function<void(const uint8_t *, size_t)> &)> onGetDataFromFile;
// path, return file string content.
std::function<ccstd::string(const ccstd::string &)> onGetStringFromFile;
// path
std::function<bool(const ccstd::string &)> onCheckFileExist;
// path, return full path
std::function<ccstd::string(const ccstd::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] ret 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 ccstd::string &path, Value *ret = nullptr);
/**
* @brief Tests whether script engine is doing garbage collection.
* @return true if it's in garbage collection, otherwise false.
*/
bool isGarbageCollecting() const;
/**
* @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() const { return _isInCleanup; }
/**
* @brief Tests whether script engine is valid.
* @return true if it's valid, otherwise false.
*/
bool isValid() const;
/**
* @brief Throw JS exception
*/
void throwException(const ccstd::string &errorMessage);
/**
* @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 ccstd::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; }
/**
* @brief Fast version of call script function, faster than Object::call
*/
bool callFunction(Object *targetObj, const char *funcName, uint32_t argc, Value *args, Value *rval = nullptr);
/**
* @brief Handle all exceptions throwed by promise
*/
void handlePromiseExceptions();
// Private API used in wrapper
class VMStringPool final {
public:
VMStringPool();
~VMStringPool();
v8::MaybeLocal<v8::String> get(v8::Isolate *isolate, const char *name);
void clear();
private:
ccstd::unordered_map<ccstd::string, v8::Persistent<v8::String> *> _vmStringPoolMap;
};
inline VMStringPool &_getStringPool() { return _stringPool; } // NOLINT(readability-identifier-naming)
void _retainScriptObject(void *owner, void *target); // NOLINT(readability-identifier-naming)
void _releaseScriptObject(void *owner, void *target); // NOLINT(readability-identifier-naming)
v8::Local<v8::Context> _getContext() const; // NOLINT(readability-identifier-naming)
void _setGarbageCollecting(bool isGarbageCollecting); // NOLINT(readability-identifier-naming)
struct DebuggerInfo {
ccstd::string serverAddr;
uint32_t port{0};
bool isWait{false};
inline bool isValid() const { return !serverAddr.empty() && port != 0; }
inline void reset() {
serverAddr.clear();
port = 0;
isWait = false;
}
};
static void _setDebuggerInfo(const DebuggerInfo &info); // NOLINT(readability-identifier-naming)
//
private:
static void privateDataFinalize(PrivateObjectBase *privateObj);
static void onFatalErrorCallback(const char *location, const char *message);
static void onOOMErrorCallback(const char *location,
#if V8_MAJOR_VERSION > 10 || (V8_MAJOR_VERSION == 10 && V8_MINOR_VERSION > 4)
const v8::OOMDetails& details
#else
bool isHeapOom
#endif
);
static void onMessageCallback(v8::Local<v8::Message> message, v8::Local<v8::Value> data);
static void onPromiseRejectCallback(v8::PromiseRejectMessage msg);
/**
* @brief Load the bytecode file and set the return value
* @param[in] path_bc The path of bytecode file.
* @param[in] ret 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 runByteCodeFile(const ccstd::string &pathBc, Value *ret /* = nullptr */);
void callExceptionCallback(const char *, const char *, const char *);
bool callRegisteredCallback();
bool postInit();
// Struct to save exception info
struct PromiseExceptionMsg {
ccstd::string event;
ccstd::string stackTrace;
};
// Push promise and exception msg to _promiseArray
void pushPromiseExeception(const v8::Local<v8::Promise> &promise, const char *event, const char *stackTrace);
static ScriptEngine *instance;
static DebuggerInfo debuggerInfo;
ccstd::vector<std::tuple<std::unique_ptr<v8::Persistent<v8::Promise>>, ccstd::vector<PromiseExceptionMsg>>> _promiseArray;
std::chrono::steady_clock::time_point _startTime;
ccstd::vector<RegisterCallback> _registerCallbackArray;
ccstd::vector<RegisterCallback> _permRegisterCallbackArray;
ccstd::vector<std::function<void()>> _beforeInitHookArray;
ccstd::vector<std::function<void()>> _afterInitHookArray;
ccstd::vector<std::function<void()>> _beforeCleanupHookArray;
ccstd::vector<std::function<void()>> _afterCleanupHookArray;
v8::Persistent<v8::Context> _context;
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
VMStringPool _stringPool;
std::thread::id _engineThreadId;
ccstd::string _lastStackTrace;
ccstd::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,32 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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 "Class.h"
#include "HelperMacros.h"
#include "Object.h"
#include "ScriptEngine.h"
#include "Utils.h"

View File

@@ -0,0 +1,249 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
#include <cfloat>
#include "Class.h"
#include "Object.h"
#include "ScriptEngine.h"
#include "base/Log.h"
#include "base/Macros.h"
namespace se {
namespace internal {
void jsToSeArgs(const v8::FunctionCallbackInfo<v8::Value> &v8args, ValueArray &outArr) {
v8::Isolate *isolate = v8args.GetIsolate();
for (int i = 0; i < v8args.Length(); i++) {
jsToSeValue(isolate, v8args[i], &outArr[i]);
}
}
void seToJsArgs(v8::Isolate *isolate, const ValueArray &args, v8::Local<v8::Value> *outArr) {
CC_ASSERT_NOT_NULL(outArr);
uint32_t i = 0;
for (const auto &data : args) {
v8::Local<v8::Value> &jsval = outArr[i];
seToJsValue(isolate, data, &jsval);
++i;
}
}
void seToJsValue(v8::Isolate *isolate, const Value &v, v8::Local<v8::Value> *outJsVal) {
CC_ASSERT_NOT_NULL(outJsVal);
switch (v.getType()) {
case Value::Type::Number:
*outJsVal = v8::Number::New(isolate, v.toDouble());
break;
case Value::Type::String: {
v8::MaybeLocal<v8::String> str = v8::String::NewFromUtf8(isolate, v.toString().data(), v8::NewStringType::kNormal, static_cast<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;
case Value::Type::BigInt:
*outJsVal = v8::BigInt::New(isolate, v.toInt64());
break;
default:
CC_ABORT();
break;
}
}
void jsToSeValue(v8::Isolate *isolate, v8::Local<v8::Value> jsval, Value *v) {
CC_ASSERT_NOT_NULL(v);
v8::HandleScope handleScope(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->setDouble(jsNumber.ToLocalChecked()->Value());
} else {
v->setUndefined();
}
} else if (jsval->IsBigInt()) {
v8::MaybeLocal<v8::BigInt> jsBigInt = jsval->ToBigInt(isolate->GetCurrentContext());
if (!jsBigInt.IsEmpty()) {
auto bigInt = jsBigInt.ToLocalChecked();
v->setInt64(bigInt->Int64Value());
} else {
v->setUndefined();
}
} else if (jsval->IsString()) {
v8::String::Utf8Value utf8(isolate, jsval);
const char *utf8Str = *utf8;
v->setString(utf8Str);
} 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()) {
auto *obj = internal::getPrivate(isolate, jsObj.ToLocalChecked());
if (obj == nullptr) {
obj = Object::_createJSObject(nullptr, jsObj.ToLocalChecked());
} else {
obj->incRef();
}
v->setObject(obj, true);
obj->decRef();
} else {
v->setUndefined();
}
}
}
template <int N>
static void warnWithinTimesInReleaseMode(const char *msg) {
static int timesLimit = N;
#if CC_DEBUG
CC_LOG_DEBUG(msg);
#else
if (timesLimit > 0) {
CC_LOG_WARNING(msg);
timesLimit--;
}
#endif
}
template <typename T>
void setReturnValueTemplate(const Value &data, const T &argv) {
switch (data.getType()) {
case Value::Type::Undefined: {
argv.GetReturnValue().Set(v8::Undefined(argv.GetIsolate()));
break;
}
case Value::Type::Null: {
argv.GetReturnValue().Set(v8::Null(argv.GetIsolate()));
break;
}
case Value::Type::Number: {
argv.GetReturnValue().Set(v8::Number::New(argv.GetIsolate(), data.toDouble()));
break;
}
case Value::Type::BigInt: {
constexpr int64_t maxSafeInt = 9007199254740991LL; // value refer to JS Number.MAX_SAFE_INTEGER
constexpr int64_t minSafeInt = -9007199254740991LL; // value refer to JS Number.MIN_SAFE_INTEGER
if (data.toInt64() > maxSafeInt || data.toInt64() < minSafeInt) {
// NOTICE: Precision loss will happend here.
warnWithinTimesInReleaseMode<100>("int64 value is out of range for double");
CC_ABORT(); // should be fixed in debug mode.
}
argv.GetReturnValue().Set(v8::Number::New(argv.GetIsolate(), static_cast<double>(data.toInt64())));
break;
}
case Value::Type::String: {
v8::MaybeLocal<v8::String> value = v8::String::NewFromUtf8(argv.GetIsolate(), data.toString().c_str(), v8::NewStringType::kNormal);
CC_ASSERT(!value.IsEmpty());
argv.GetReturnValue().Set(value.ToLocalChecked());
break;
}
case Value::Type::Boolean: {
argv.GetReturnValue().Set(v8::Boolean::New(argv.GetIsolate(), data.toBoolean()));
break;
}
case Value::Type::Object: {
argv.GetReturnValue().Set(data.toObject()->_getJSObject());
break;
}
}
}
void setReturnValue(const Value &data, const v8::FunctionCallbackInfo<v8::Value> &argv) {
setReturnValueTemplate(data, argv);
}
void setReturnValue(const Value &data, const v8::PropertyCallbackInfo<v8::Value> &argv) {
setReturnValueTemplate(data, argv);
}
bool hasPrivate(v8::Isolate * /*isolate*/, v8::Local<v8::Value> value) {
v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(value);
return obj->InternalFieldCount() > 0;
}
void setPrivate(v8::Isolate *isolate, ObjectWrap &wrap, Object *thizObj) {
v8::Local<v8::Object> obj = wrap.handle(isolate);
int c = obj->InternalFieldCount();
CC_ASSERT_GT(c, 0);
if (c == 1) {
wrap.wrap(thizObj, 0);
}
}
Object *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 == 1) {
return static_cast<Object *>(ObjectWrap::unwrap(objChecked, 0));
}
return nullptr;
}
void clearPrivate(v8::Isolate *isolate, ObjectWrap &wrap) {
v8::Local<v8::Object> obj = wrap.handle(isolate);
int c = obj->InternalFieldCount();
if (c == 1) {
wrap.wrap(nullptr, 0);
}
}
} // namespace internal
} // namespace se
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8

View File

@@ -0,0 +1,56 @@
/****************************************************************************
Copyright (c) 2016 Chukong Technologies Inc.
Copyright (c) 2017-2023 Xiamen Yaji Software Co., Ltd.
http://www.cocos.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated 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.h"
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
#include "../Value.h"
#include "Base.h"
#include "ObjectWrap.h"
namespace se {
namespace internal {
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, 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, Object *obj);
Object *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,597 @@
/* -*- 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,69 @@
/* -*- 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.h"
#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,188 @@
#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(do {} while (false) /* empty*/);
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 = static_cast<unsigned>(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,117 @@
#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,618 @@
#pragma once
// clang-format off
#include "../../config.h"
#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),
inspector_agent_(this),
makecallback_cntr_(0),
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(isolate()->GetCurrentContext(), name_string, function).Check();
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
// clang-format on

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,353 @@
/* 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.h"
#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 <cstdint>
#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
using http_parser = struct http_parser;
using http_parser_settings = struct 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.
*/
using http_data_cb = int (*)(http_parser *, const char *, size_t);
using http_cb = int (*)(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 { //NOLINT(readability-identifier-naming)
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST, //NOLINT(readability-identifier-naming)
HTTP_RESPONSE,
HTTP_BOTH };
/* Flag values for http_parser.flags field */
enum flags { F_CHUNKED = 1 << 0, //NOLINT(readability-identifier-naming)
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 { //NOLINT(readability-identifier-naming)
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; //NOLINT(google-runtime-int)
unsigned short http_minor; //NOLINT(google-runtime-int)
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, //NOLINT(readability-identifier-naming)
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 { //NOLINT(readability-identifier-naming)
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); //NOLINT(readability-identifier-naming,google-runtime-int)
void http_parser_init(http_parser *parser, enum http_parser_type type); //NOLINT(readability-identifier-naming)
/* Initialize http_parser_settings members to 0
*/
void http_parser_settings_init(http_parser_settings *settings); //NOLINT(readability-identifier-naming)
/* Executes the parser. Returns number of parsed bytes. Sets
* `parser->http_errno` on error. */
size_t http_parser_execute(http_parser *parser, //NOLINT(readability-identifier-naming)
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); //NOLINT(readability-identifier-naming)
/* Returns a string version of the HTTP method. */
const char *http_method_str(enum http_method m); //NOLINT(readability-identifier-naming)
/* Return a string name of the given error */
const char *http_errno_name(enum http_errno err); //NOLINT(readability-identifier-naming)
/* Return a string description of the given error */
const char *http_errno_description(enum http_errno err); //NOLINT(readability-identifier-naming)
/* Initialize all http_parser_url members to 0 */
void http_parser_url_init(struct http_parser_url *u); //NOLINT(readability-identifier-naming)
/* Parse a URL; return nonzero on failure */
int http_parser_parse_url(const char *buf, size_t buflen, //NOLINT(readability-identifier-naming)
int is_connect, //NOLINT(readability-identifier-naming)
struct http_parser_url *u);
/* Pause or un-pause the parser; a nonzero value pauses */
void http_parser_pause(http_parser *parser, int paused); //NOLINT(readability-identifier-naming)
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser); //NOLINT(readability-identifier-naming)
#ifdef __cplusplus
}
#endif
#endif // #if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
#endif

View File

@@ -0,0 +1,822 @@
#include "inspector_agent.h"
#if (SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8) && SE_ENABLE_INSPECTOR
#include "env.h"
#include "inspector_io.h"
#include "node.h"
#include "util.h"
#include "v8-inspector.h"
#include "v8-platform.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, static_cast<int>(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(),
static_cast<int>(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(),
static_cast<int>(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],
static_cast<int>(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) {
#if V8_MAJOR_VERSION > 10 || (V8_MAJOR_VERSION == 10 && V8_MINOR_VERSION > 3)
session_ = inspector->connect(1, this, StringView(), v8_inspector::V8Inspector::ClientTrustLevel::kFullyTrusted);
#else
session_ = inspector->connect(1, this, StringView());
#endif
}
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) {
if (channel_ == nullptr) return;
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();
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(parent_env_->context(), 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"))
.Check();
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) {
if (client_ == nullptr) return;
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_->GetForegroundTaskRunner(isolate)->PostTask(std::make_unique<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,116 @@
#ifndef SRC_INSPECTOR_AGENT_H_
#define SRC_INSPECTOR_AGENT_H_
#include "../../config.h"
#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,533 @@
#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/UTF8.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;
cc::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;
cc::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);
}
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_->GetForegroundTaskRunner(isolate)->PostTask(std::make_unique<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,181 @@
#ifndef SRC_INSPECTOR_IO_H_
#define SRC_INSPECTOR_IO_H_
#include "../../config.h"
#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,643 @@
#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], (unsigned int)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 = (int)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, (int)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], (unsigned int)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,109 @@
#ifndef SRC_INSPECTOR_SOCKET_H_
#define SRC_INSPECTOR_SOCKET_H_
#include "../../config.h"
#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,700 @@
#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;
}
std::vector<std::tuple<std::string, bool, std::string>> ipList;
{
char buf[512];
uv_interface_address_t *info = nullptr;
int count = 0;
int i = 0;
uv_interface_addresses(&info, &count);
i = count;
if (errno) {
SE_LOGE("failed to get addresses %s", strerror(errno));
}
SE_LOGD("Number of interfaces: %d", count);
while (i--) {
auto &networkInterface = info[i];
if (networkInterface.address.address4.sin_family == AF_INET) {
uv_ip4_name(&networkInterface.address.address4, buf, sizeof(buf));
ipList.emplace_back(networkInterface.name, networkInterface.is_internal, buf);
}
}
uv_free_interface_addresses(info, count);
}
// failed to query device interfaces,
if (ipList.empty()) {
#if ANDROID
SE_LOGD("Please query IP by running the following command in terminal: adb shell ip -4 -br addr");
#endif
ipList.emplace_back("none", false, "IP_ADDR_OF_THIS_DEVICE");
}
for (const std::string &id : ids) {
if (host != "0.0.0.0") {
SE_LOGD("Debugger listening..., visit [ devtools://devtools/bundled/js_app.html?v8only=true&ws=%s ] in chrome browser to debug!",
FormatWsAddress(host, port, id, false).c_str());
} else {
SE_LOGD("Debugger listening..., visit [");
for (auto &nif : ipList) {
SE_LOGD(" devtools://devtools/bundled/js_app.html?v8only=true&ws=%s",
FormatWsAddress(std::get<2>(nif), port, id, false).c_str());
}
SE_LOGD(" ] in chrome browser to debug!");
}
}
SE_LOGD("For help see %s",
"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"] = "Cocos 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 = static_cast<uInt>(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,105 @@
#ifndef SRC_INSPECTOR_SOCKET_SERVER_H_
#define SRC_INSPECTOR_SOCKET_SERVER_H_
#include "../../config.h"
#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,160 @@
#pragma once
#include "../../config.h"
#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.h"
#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,196 @@
#ifndef SRC_NODE_MUTEX_H_
#define SRC_NODE_MUTEX_H_
#include "../../config.h"
#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,432 @@
// 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,117 @@
// 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,449 @@
// 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.h"
#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