no message
BIN
cocos/bindings/docs/JSB2.0-Architecture.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
1197
cocos/bindings/docs/JSB2.0-learning-en.md
Normal file
1184
cocos/bindings/docs/JSB2.0-learning-zh.md
Normal file
BIN
cocos/bindings/docs/jsc-breakpoint.png
Normal file
|
After Width: | Height: | Size: 482 KiB |
BIN
cocos/bindings/docs/jsc-entitlements-check.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
cocos/bindings/docs/jsc-entitlements.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
cocos/bindings/docs/jsc-mac-debug.png
Normal file
|
After Width: | Height: | Size: 233 KiB |
BIN
cocos/bindings/docs/jsc-mac-simulator-sign.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
cocos/bindings/docs/jsc-security-key.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
cocos/bindings/docs/jsc-timeline.png
Normal file
|
After Width: | Height: | Size: 464 KiB |
BIN
cocos/bindings/docs/v8-win32-debug.jpg
Normal file
|
After Width: | Height: | Size: 193 KiB |
BIN
cocos/bindings/docs/v8-win32-memory.jpg
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
cocos/bindings/docs/v8-win32-profile.jpg
Normal file
|
After Width: | Height: | Size: 130 KiB |
66
cocos/bindings/dop/BufferAllocator.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/****************************************************************************
|
||||
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 "BufferAllocator.h"
|
||||
#include "base/Log.h"
|
||||
#include "base/memory/Memory.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
BufferAllocator::BufferAllocator(PoolType type)
|
||||
: _type(type) {
|
||||
}
|
||||
|
||||
BufferAllocator::~BufferAllocator() {
|
||||
for (auto buffer : _buffers) {
|
||||
buffer.second->decRef();
|
||||
}
|
||||
_buffers.clear();
|
||||
}
|
||||
|
||||
se::Object *BufferAllocator::alloc(uint32_t index, uint32_t bytes) {
|
||||
if (_buffers.count(index)) {
|
||||
se::Object *oldObj = _buffers[index];
|
||||
oldObj->decRef();
|
||||
}
|
||||
se::Object *obj = se::Object::createArrayBufferObject(nullptr, bytes);
|
||||
|
||||
_buffers[index] = obj;
|
||||
|
||||
uint8_t *ret = nullptr;
|
||||
size_t len;
|
||||
obj->getArrayBufferData(static_cast<uint8_t **>(&ret), &len);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void BufferAllocator::free(uint32_t index) {
|
||||
if (_buffers.count(index)) {
|
||||
se::Object *oldObj = _buffers[index];
|
||||
oldObj->decRef();
|
||||
_buffers.erase(index);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
49
cocos/bindings/dop/BufferAllocator.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/****************************************************************************
|
||||
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 "PoolType.h"
|
||||
#include "cocos/base/Macros.h"
|
||||
#include "cocos/base/std/container/unordered_map.h"
|
||||
#include "cocos/bindings/jswrapper/Object.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class CC_DLL BufferAllocator final {
|
||||
public:
|
||||
explicit BufferAllocator(PoolType type);
|
||||
~BufferAllocator();
|
||||
|
||||
se::Object *alloc(uint32_t index, uint32_t bytes);
|
||||
void free(uint32_t index);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t BUFFER_MASK = ~(1 << 30);
|
||||
|
||||
ccstd::unordered_map<uint32_t, se::Object *> _buffers;
|
||||
PoolType _type = PoolType::UNKNOWN;
|
||||
};
|
||||
|
||||
} // namespace se
|
||||
56
cocos/bindings/dop/BufferPool.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/****************************************************************************
|
||||
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 "BufferPool.h"
|
||||
#include "base/Macros.h"
|
||||
#include "base/memory/Memory.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
BufferPool::BufferPool(PoolType type, uint32_t entryBits, uint32_t bytesPerEntry)
|
||||
: _allocator(type),
|
||||
_entryBits(entryBits),
|
||||
_bytesPerEntry(bytesPerEntry),
|
||||
_type(type) {
|
||||
_entriesPerChunk = 1 << entryBits;
|
||||
_entryMask = _entriesPerChunk - 1;
|
||||
_chunkMask = 0xffffffff & ~(_entryMask | POOL_FLAG);
|
||||
|
||||
_bytesPerChunk = _bytesPerEntry * _entriesPerChunk;
|
||||
}
|
||||
|
||||
BufferPool::~BufferPool() = default;
|
||||
|
||||
se::Object *BufferPool::allocateNewChunk() {
|
||||
se::Object *jsObj = _allocator.alloc(static_cast<uint32_t>(_chunks.size()), _bytesPerChunk);
|
||||
|
||||
uint8_t *realPtr = nullptr;
|
||||
size_t len = 0;
|
||||
jsObj->getArrayBufferData(&realPtr, &len);
|
||||
_chunks.push_back(realPtr);
|
||||
|
||||
return jsObj;
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
68
cocos/bindings/dop/BufferPool.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/****************************************************************************
|
||||
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 "BufferAllocator.h"
|
||||
#include "PoolType.h"
|
||||
#include "base/std/container/vector.h"
|
||||
#include "cocos/base/Macros.h"
|
||||
#include "cocos/bindings/jswrapper/Object.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class CC_DLL BufferPool final {
|
||||
public:
|
||||
using Chunk = uint8_t *;
|
||||
|
||||
inline static uint32_t getPoolFlag() { return POOL_FLAG; }
|
||||
|
||||
BufferPool(PoolType type, uint32_t entryBits, uint32_t bytesPerEntry);
|
||||
~BufferPool();
|
||||
|
||||
template <class T>
|
||||
T *getTypedObject(uint32_t id) const {
|
||||
uint32_t chunk = (_chunkMask & id) >> _entryBits;
|
||||
uint32_t entry = _entryMask & id;
|
||||
CC_ASSERT(chunk < _chunks.size() && entry < _entriesPerChunk);
|
||||
return reinterpret_cast<T *>(_chunks[chunk] + (entry * _bytesPerEntry));
|
||||
}
|
||||
|
||||
se::Object *allocateNewChunk();
|
||||
|
||||
private:
|
||||
static constexpr uint32_t POOL_FLAG{1 << 30};
|
||||
|
||||
BufferAllocator _allocator;
|
||||
ccstd::vector<Chunk> _chunks;
|
||||
uint32_t _entryBits{1 << 8};
|
||||
uint32_t _chunkMask{0};
|
||||
uint32_t _entryMask{0};
|
||||
uint32_t _bytesPerChunk{0};
|
||||
uint32_t _entriesPerChunk{0};
|
||||
uint32_t _bytesPerEntry{0};
|
||||
PoolType _type{PoolType::UNKNOWN};
|
||||
};
|
||||
|
||||
} // namespace se
|
||||
37
cocos/bindings/dop/PoolType.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/****************************************************************************
|
||||
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
|
||||
|
||||
namespace se {
|
||||
|
||||
#define CAST_POOL_TYPE(type) static_cast<uint32_t>(type)
|
||||
#define GET_BUFFER_POOL_ID(type) CAST_POOL_TYPE(type)
|
||||
|
||||
enum class PoolType {
|
||||
// Buffers
|
||||
NODE,
|
||||
UNKNOWN
|
||||
};
|
||||
} // namespace se
|
||||
185
cocos/bindings/dop/jsb_dop.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
/****************************************************************************
|
||||
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 "jsb_dop.h"
|
||||
|
||||
#include "BufferAllocator.h"
|
||||
#include "BufferPool.h"
|
||||
#include "cocos/bindings/manual/jsb_classtype.h"
|
||||
#include "cocos/bindings/manual/jsb_conversions.h"
|
||||
#include "cocos/bindings/manual/jsb_global.h"
|
||||
|
||||
/********************************************************
|
||||
BufferPool binding
|
||||
*******************************************************/
|
||||
se::Class *jsb_BufferPool_class = nullptr; // NOLINT
|
||||
|
||||
static bool jsb_BufferPool_allocateNewChunk(se::State &s) { // NOLINT
|
||||
auto *pool = static_cast<se::BufferPool *>(s.nativeThisObject());
|
||||
SE_PRECONDITION2(pool, false, "Invalid Native Object");
|
||||
s.rval().setObject(pool->allocateNewChunk());
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FUNC(jsb_BufferPool_allocateNewChunk);
|
||||
|
||||
SE_DECLARE_FINALIZE_FUNC(jsb_BufferPool_finalize)
|
||||
|
||||
static bool jsb_BufferPool_constructor(se::State &s) { // NOLINT
|
||||
const auto &args = s.args();
|
||||
size_t argc = args.size();
|
||||
if (argc == 3) {
|
||||
uint32_t poolType{0};
|
||||
uint32_t entryBits{0};
|
||||
uint32_t bytesPerEntry{0};
|
||||
|
||||
bool ok = true;
|
||||
ok &= sevalue_to_native(args[0], &poolType);
|
||||
ok &= sevalue_to_native(args[1], &entryBits);
|
||||
ok &= sevalue_to_native(args[2], &bytesPerEntry);
|
||||
if (!ok) {
|
||||
SE_REPORT_ERROR("jsb_BufferPool_constructor: argument convertion error");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *pool = JSB_ALLOC(se::BufferPool, (se::PoolType)poolType, entryBits, bytesPerEntry);
|
||||
s.thisObject()->setPrivateData(pool);
|
||||
return true;
|
||||
}
|
||||
|
||||
SE_REPORT_ERROR("jsb_BufferPool_constructor: wrong number of arguments: %d", (int)argc);
|
||||
return false;
|
||||
}
|
||||
SE_BIND_CTOR(jsb_BufferPool_constructor, jsb_BufferPool_class, jsb_BufferPool_finalize) // NOLINT
|
||||
|
||||
static bool jsb_BufferPool_finalize(se::State &s) { // NOLINT
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FINALIZE_FUNC(jsb_BufferPool_finalize)
|
||||
|
||||
static bool js_register_se_BufferPool(se::Object *obj) { // NOLINT
|
||||
se::Class *cls = se::Class::create("NativeBufferPool", obj, nullptr, _SE(jsb_BufferPool_constructor));
|
||||
|
||||
cls->defineFunction("allocateNewChunk", _SE(jsb_BufferPool_allocateNewChunk));
|
||||
cls->install();
|
||||
JSBClassType::registerClass<se::BufferPool>(cls);
|
||||
|
||||
jsb_BufferPool_class = cls; // NOLINT
|
||||
|
||||
se::ScriptEngine::getInstance()->clearException();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*****************************************************
|
||||
Array binding
|
||||
******************************************************/
|
||||
static se::Class *jsb_BufferAllocator_class = nullptr; // NOLINT
|
||||
|
||||
SE_DECLARE_FINALIZE_FUNC(jsb_BufferAllocator_finalize)
|
||||
|
||||
static bool jsb_BufferAllocator_constructor(se::State &s) { // NOLINT
|
||||
const auto &args = s.args();
|
||||
size_t argc = args.size();
|
||||
if (argc == 1) {
|
||||
uint32_t type{0};
|
||||
|
||||
auto *bufferAllocator = JSB_ALLOC(se::BufferAllocator, static_cast<se::PoolType>(type));
|
||||
s.thisObject()->setPrivateData(bufferAllocator);
|
||||
return true;
|
||||
}
|
||||
|
||||
SE_REPORT_ERROR("wrong number of arguments: %d", (int)argc);
|
||||
return false;
|
||||
}
|
||||
SE_BIND_CTOR(jsb_BufferAllocator_constructor, jsb_BufferAllocator_class, jsb_BufferAllocator_finalize)
|
||||
|
||||
static bool jsb_BufferAllocator_finalize(se::State &s) { // NOLINT
|
||||
return true;
|
||||
}
|
||||
SE_BIND_FINALIZE_FUNC(jsb_BufferAllocator_finalize)
|
||||
|
||||
static bool jsb_BufferAllocator_alloc(se::State &s) { // NOLINT
|
||||
auto *bufferAllocator = static_cast<se::BufferAllocator *>(s.nativeThisObject());
|
||||
SE_PRECONDITION2(bufferAllocator, false, "Invalid Native Object");
|
||||
|
||||
const auto &args = s.args();
|
||||
size_t argc = args.size();
|
||||
if (argc == 2) {
|
||||
uint32_t index{0};
|
||||
sevalue_to_native(args[0], &index);
|
||||
uint32_t bytes{0};
|
||||
sevalue_to_native(args[1], &bytes);
|
||||
s.rval().setObject(bufferAllocator->alloc(index, bytes));
|
||||
return true;
|
||||
}
|
||||
|
||||
SE_REPORT_ERROR("wrong number of arguments: %d", (int)argc);
|
||||
return false;
|
||||
}
|
||||
SE_BIND_FUNC(jsb_BufferAllocator_alloc);
|
||||
|
||||
static bool jsb_BufferAllocator_free(se::State &s) { // NOLINT
|
||||
auto *bufferAllocator = static_cast<se::BufferAllocator *>(s.nativeThisObject());
|
||||
SE_PRECONDITION2(bufferAllocator, false, "Invalid Native Object");
|
||||
|
||||
const auto &args = s.args();
|
||||
size_t argc = args.size();
|
||||
if (argc == 1) {
|
||||
uint32_t index{0};
|
||||
sevalue_to_native(args[0], &index);
|
||||
bufferAllocator->free(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
SE_REPORT_ERROR("wrong number of arguments: %d", (int)argc);
|
||||
return false;
|
||||
}
|
||||
SE_BIND_FUNC(jsb_BufferAllocator_free);
|
||||
|
||||
static bool js_register_se_BufferAllocator(se::Object *obj) { // NOLINT
|
||||
se::Class *cls = se::Class::create("NativeBufferAllocator", obj, nullptr, _SE(jsb_BufferAllocator_constructor));
|
||||
cls->defineFunction("alloc", _SE(jsb_BufferAllocator_alloc));
|
||||
cls->defineFunction("free", _SE(jsb_BufferAllocator_free));
|
||||
cls->install();
|
||||
JSBClassType::registerClass<se::BufferAllocator>(cls);
|
||||
|
||||
jsb_BufferAllocator_class = cls; // NOLINT
|
||||
|
||||
se::ScriptEngine::getInstance()->clearException();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool register_all_dop_bindings(se::Object *obj) { // NOLINT
|
||||
// TODO(liuhang): Don't make dop into jsb namespace. Currently put it into jsb namesapce just to test codes.
|
||||
se::Value nsVal;
|
||||
if (!obj->getProperty("jsb", &nsVal)) {
|
||||
se::HandleObject jsobj(se::Object::createPlainObject());
|
||||
nsVal.setObject(jsobj);
|
||||
obj->setProperty("jsb", nsVal);
|
||||
}
|
||||
se::Object *ns = nsVal.toObject();
|
||||
|
||||
js_register_se_BufferAllocator(ns); // NOLINT
|
||||
js_register_se_BufferPool(ns); // NOLINT
|
||||
return true;
|
||||
}
|
||||
31
cocos/bindings/dop/jsb_dop.h
Normal 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
|
||||
|
||||
namespace se {
|
||||
class Object;
|
||||
}
|
||||
|
||||
bool register_all_dop_bindings(se::Object *obj); // NOLINT
|
||||
459
cocos/bindings/event/EventDispatcher.cpp
Normal file
@@ -0,0 +1,459 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018-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 "EventDispatcher.h"
|
||||
#include <cstdarg>
|
||||
#include "cocos/application/ApplicationManager.h"
|
||||
#include "cocos/bindings/jswrapper/HandleObject.h"
|
||||
#include "cocos/bindings/jswrapper/SeApi.h"
|
||||
#include "cocos/bindings/manual/jsb_global_init.h"
|
||||
#include "cocos/platform/interfaces/modules/ISystemWindow.h"
|
||||
#include "cocos/platform/interfaces/modules/ISystemWindowManager.h"
|
||||
|
||||
namespace {
|
||||
se::Value tickVal;
|
||||
se::ValueArray tickArgsValArr(1);
|
||||
ccstd::vector<se::Object *> jsTouchObjPool;
|
||||
se::Object *jsTouchObjArray = nullptr;
|
||||
se::Object *jsMouseEventObj = nullptr;
|
||||
se::Object *jsKeyboardEventObj = nullptr;
|
||||
se::Object *jsControllerEventArray = nullptr;
|
||||
se::Object *jsControllerChangeEventArray = nullptr;
|
||||
se::Object *jsResizeEventObj = nullptr;
|
||||
bool inited = false;
|
||||
bool busListenerInited = false;
|
||||
|
||||
// attach the argument object to the function
|
||||
void accessCacheArgObj(se::Object *func, se::Value *argObj, const char *cacheKey = "__reusedArgumentObject") {
|
||||
func->getProperty(cacheKey, argObj);
|
||||
if (argObj->isUndefined()) {
|
||||
se::HandleObject argumentObj(se::Object::createPlainObject());
|
||||
argObj->setObject(argumentObj);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
namespace cc {
|
||||
|
||||
events::EnterForeground::Listener EventDispatcher::listenerEnterForeground;
|
||||
events::EnterBackground::Listener EventDispatcher::listenerEnterBackground;
|
||||
events::WindowChanged::Listener EventDispatcher::listenerWindowChanged;
|
||||
events::LowMemory::Listener EventDispatcher::listenerLowMemory;
|
||||
events::Touch::Listener EventDispatcher::listenerTouch;
|
||||
events::Mouse::Listener EventDispatcher::listenerMouse;
|
||||
events::Keyboard::Listener EventDispatcher::listenerKeyboard;
|
||||
events::Controller::Listener EventDispatcher::listenerConroller;
|
||||
events::ControllerChange::Listener EventDispatcher::listenerConrollerChange;
|
||||
events::Tick::Listener EventDispatcher::listenerTick;
|
||||
events::Resize::Listener EventDispatcher::listenerResize;
|
||||
events::Orientation::Listener EventDispatcher::listenerOrientation;
|
||||
events::RestartVM::Listener EventDispatcher::listenerRestartVM;
|
||||
events::Close::Listener EventDispatcher::listenerClose;
|
||||
events::PointerLock::Listener EventDispatcher::listenerPointerLock;
|
||||
|
||||
uint32_t EventDispatcher::hashListenerId = 1;
|
||||
|
||||
bool EventDispatcher::initialized() {
|
||||
return inited && se::ScriptEngine::getInstance()->isValid();
|
||||
}
|
||||
|
||||
void EventDispatcher::init() {
|
||||
inited = true;
|
||||
se::ScriptEngine::getInstance()->addBeforeCleanupHook([]() {
|
||||
EventDispatcher::destroy();
|
||||
});
|
||||
|
||||
if (!busListenerInited) {
|
||||
listenerTouch.bind(&dispatchTouchEvent);
|
||||
listenerMouse.bind(&dispatchMouseEvent);
|
||||
listenerKeyboard.bind(&dispatchKeyboardEvent);
|
||||
listenerConroller.bind(&dispatchControllerEvent);
|
||||
listenerConrollerChange.bind(&dispatchControllerChangeEvent);
|
||||
listenerTick.bind(&dispatchTickEvent);
|
||||
listenerResize.bind(&dispatchResizeEvent);
|
||||
listenerOrientation.bind(&dispatchOrientationChangeEvent);
|
||||
listenerEnterBackground.bind(&dispatchEnterBackgroundEvent);
|
||||
listenerEnterForeground.bind(&dispatchEnterForegroundEvent);
|
||||
listenerLowMemory.bind(&dispatchMemoryWarningEvent);
|
||||
listenerClose.bind(&dispatchCloseEvent);
|
||||
listenerRestartVM.bind(&dispatchRestartVM);
|
||||
listenerPointerLock.bind(&dispatchPointerlockChangeEvent);
|
||||
busListenerInited = true;
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::destroy() {
|
||||
for (auto *touchObj : jsTouchObjPool) {
|
||||
touchObj->unroot();
|
||||
touchObj->decRef();
|
||||
}
|
||||
jsTouchObjPool.clear();
|
||||
|
||||
if (jsTouchObjArray != nullptr) {
|
||||
jsTouchObjArray->unroot();
|
||||
jsTouchObjArray->decRef();
|
||||
jsTouchObjArray = nullptr;
|
||||
}
|
||||
|
||||
if (jsControllerEventArray != nullptr) {
|
||||
jsControllerEventArray->unroot();
|
||||
jsControllerEventArray->decRef();
|
||||
jsControllerEventArray = nullptr;
|
||||
}
|
||||
|
||||
if (jsControllerChangeEventArray != nullptr) {
|
||||
jsControllerChangeEventArray->unroot();
|
||||
jsControllerChangeEventArray->decRef();
|
||||
jsControllerChangeEventArray = nullptr;
|
||||
}
|
||||
|
||||
if (jsMouseEventObj != nullptr) {
|
||||
jsMouseEventObj->unroot();
|
||||
jsMouseEventObj->decRef();
|
||||
jsMouseEventObj = nullptr;
|
||||
}
|
||||
|
||||
if (jsKeyboardEventObj != nullptr) {
|
||||
jsKeyboardEventObj->unroot();
|
||||
jsKeyboardEventObj->decRef();
|
||||
jsKeyboardEventObj = nullptr;
|
||||
}
|
||||
|
||||
if (jsResizeEventObj != nullptr) {
|
||||
jsResizeEventObj->unroot();
|
||||
jsResizeEventObj->decRef();
|
||||
jsResizeEventObj = nullptr;
|
||||
}
|
||||
|
||||
inited = false;
|
||||
tickVal.setUndefined();
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchTouchEvent(const TouchEvent &touchEvent) {
|
||||
se::AutoHandleScope scope;
|
||||
if (!jsTouchObjArray) {
|
||||
jsTouchObjArray = se::Object::createArrayObject(0);
|
||||
jsTouchObjArray->root();
|
||||
}
|
||||
|
||||
jsTouchObjArray->setProperty("length", se::Value(static_cast<uint32_t>(touchEvent.touches.size())));
|
||||
|
||||
while (jsTouchObjPool.size() < touchEvent.touches.size()) {
|
||||
se::Object *touchObj = se::Object::createPlainObject();
|
||||
touchObj->root();
|
||||
jsTouchObjPool.emplace_back(touchObj);
|
||||
}
|
||||
|
||||
uint32_t touchIndex = 0;
|
||||
int poolIndex = 0;
|
||||
for (const auto &touch : touchEvent.touches) {
|
||||
se::Object *jsTouch = jsTouchObjPool.at(poolIndex++);
|
||||
jsTouch->setProperty("identifier", se::Value(touch.index));
|
||||
jsTouch->setProperty("clientX", se::Value(touch.x));
|
||||
jsTouch->setProperty("clientY", se::Value(touch.y));
|
||||
jsTouch->setProperty("pageX", se::Value(touch.x));
|
||||
jsTouch->setProperty("pageY", se::Value(touch.y));
|
||||
|
||||
jsTouchObjArray->setArrayElement(touchIndex, se::Value(jsTouch));
|
||||
++touchIndex;
|
||||
}
|
||||
|
||||
const char *eventName = nullptr;
|
||||
switch (touchEvent.type) {
|
||||
case TouchEvent::Type::BEGAN:
|
||||
eventName = "onTouchStart";
|
||||
break;
|
||||
case TouchEvent::Type::MOVED:
|
||||
eventName = "onTouchMove";
|
||||
break;
|
||||
case TouchEvent::Type::ENDED:
|
||||
eventName = "onTouchEnd";
|
||||
break;
|
||||
case TouchEvent::Type::CANCELLED:
|
||||
eventName = "onTouchCancel";
|
||||
break;
|
||||
default:
|
||||
CC_ABORT();
|
||||
break;
|
||||
}
|
||||
|
||||
se::ValueArray args;
|
||||
args.emplace_back(se::Value(jsTouchObjArray));
|
||||
args.emplace_back(se::Value(touchEvent.windowId));
|
||||
EventDispatcher::doDispatchJsEvent(eventName, args);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchMouseEvent(const MouseEvent &mouseEvent) {
|
||||
se::AutoHandleScope scope;
|
||||
if (!jsMouseEventObj) {
|
||||
jsMouseEventObj = se::Object::createPlainObject();
|
||||
jsMouseEventObj->root();
|
||||
}
|
||||
|
||||
const auto &xVal = se::Value(mouseEvent.x);
|
||||
const auto &yVal = se::Value(mouseEvent.y);
|
||||
const MouseEvent::Type type = mouseEvent.type;
|
||||
|
||||
if (type == MouseEvent::Type::WHEEL) {
|
||||
jsMouseEventObj->setProperty("wheelDeltaX", xVal);
|
||||
jsMouseEventObj->setProperty("wheelDeltaY", yVal);
|
||||
} else {
|
||||
if (type == MouseEvent::Type::DOWN || type == MouseEvent::Type::UP) {
|
||||
jsMouseEventObj->setProperty("button", se::Value(mouseEvent.button));
|
||||
}
|
||||
jsMouseEventObj->setProperty("x", xVal);
|
||||
jsMouseEventObj->setProperty("y", yVal);
|
||||
if (type == MouseEvent::Type::MOVE) {
|
||||
const auto &xDelta = se::Value(mouseEvent.xDelta);
|
||||
const auto &yDelta = se::Value(mouseEvent.yDelta);
|
||||
jsMouseEventObj->setProperty("xDelta", xDelta);
|
||||
jsMouseEventObj->setProperty("yDelta", yDelta);
|
||||
}
|
||||
}
|
||||
|
||||
jsMouseEventObj->setProperty("windowId", se::Value(mouseEvent.windowId));
|
||||
|
||||
const char *eventName = nullptr;
|
||||
const char *jsFunctionName = nullptr;
|
||||
switch (type) {
|
||||
case MouseEvent::Type::DOWN:
|
||||
jsFunctionName = "onMouseDown";
|
||||
break;
|
||||
case MouseEvent::Type::MOVE:
|
||||
jsFunctionName = "onMouseMove";
|
||||
break;
|
||||
case MouseEvent::Type::UP:
|
||||
jsFunctionName = "onMouseUp";
|
||||
break;
|
||||
case MouseEvent::Type::WHEEL:
|
||||
jsFunctionName = "onMouseWheel";
|
||||
break;
|
||||
default:
|
||||
CC_ABORT();
|
||||
break;
|
||||
}
|
||||
|
||||
se::ValueArray args;
|
||||
args.emplace_back(se::Value(jsMouseEventObj));
|
||||
EventDispatcher::doDispatchJsEvent(jsFunctionName, args);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchKeyboardEvent(const KeyboardEvent &keyboardEvent) {
|
||||
se::AutoHandleScope scope;
|
||||
if (!jsKeyboardEventObj) {
|
||||
jsKeyboardEventObj = se::Object::createPlainObject();
|
||||
jsKeyboardEventObj->root();
|
||||
}
|
||||
|
||||
const char *eventName = nullptr;
|
||||
switch (keyboardEvent.action) {
|
||||
case KeyboardEvent::Action::PRESS:
|
||||
case KeyboardEvent::Action::REPEAT:
|
||||
eventName = "onKeyDown";
|
||||
break;
|
||||
case KeyboardEvent::Action::RELEASE:
|
||||
eventName = "onKeyUp";
|
||||
break;
|
||||
default:
|
||||
CC_ABORT();
|
||||
break;
|
||||
}
|
||||
|
||||
jsKeyboardEventObj->setProperty("altKey", se::Value(keyboardEvent.altKeyActive));
|
||||
jsKeyboardEventObj->setProperty("ctrlKey", se::Value(keyboardEvent.ctrlKeyActive));
|
||||
jsKeyboardEventObj->setProperty("metaKey", se::Value(keyboardEvent.metaKeyActive));
|
||||
jsKeyboardEventObj->setProperty("shiftKey", se::Value(keyboardEvent.shiftKeyActive));
|
||||
jsKeyboardEventObj->setProperty("repeat", se::Value(keyboardEvent.action == KeyboardEvent::Action::REPEAT));
|
||||
jsKeyboardEventObj->setProperty("keyCode", se::Value(keyboardEvent.key));
|
||||
jsKeyboardEventObj->setProperty("windowId", se::Value(keyboardEvent.windowId));
|
||||
jsKeyboardEventObj->setProperty("code", se::Value(keyboardEvent.code));
|
||||
|
||||
se::ValueArray args;
|
||||
args.emplace_back(se::Value(jsKeyboardEventObj));
|
||||
EventDispatcher::doDispatchJsEvent(eventName, args);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchControllerEvent(const ControllerEvent &controllerEvent) {
|
||||
se::AutoHandleScope scope;
|
||||
if (!jsControllerEventArray) {
|
||||
jsControllerEventArray = se::Object::createArrayObject(0);
|
||||
jsControllerEventArray->root();
|
||||
}
|
||||
|
||||
const char *eventName = "onControllerInput";
|
||||
if (controllerEvent.type == ControllerEvent::Type::HANDLE) {
|
||||
eventName = "onHandleInput";
|
||||
}
|
||||
uint32_t controllerIndex = 0;
|
||||
jsControllerEventArray->setProperty("length", se::Value(static_cast<uint32_t>(controllerEvent.controllerInfos.size())));
|
||||
|
||||
for (const auto &controller : controllerEvent.controllerInfos) {
|
||||
se::HandleObject jsController{se::Object::createPlainObject()};
|
||||
jsController->setProperty("id", se::Value(controller->napdId));
|
||||
|
||||
se::HandleObject jsButtonInfoList{se::Object::createArrayObject(static_cast<uint32_t>(controller->buttonInfos.size()))};
|
||||
|
||||
uint32_t buttonIndex = 0;
|
||||
for (const auto &buttonInfo : controller->buttonInfos) {
|
||||
se::HandleObject jsButtonInfo{se::Object::createPlainObject()};
|
||||
jsButtonInfo->setProperty("code", se::Value(static_cast<uint32_t>(buttonInfo.key)));
|
||||
jsButtonInfo->setProperty("isPressed", se::Value(static_cast<uint32_t>(buttonInfo.isPress)));
|
||||
jsButtonInfoList->setArrayElement(buttonIndex, se::Value(jsButtonInfo));
|
||||
buttonIndex++;
|
||||
}
|
||||
|
||||
se::HandleObject jsAxisInfoList{se::Object::createArrayObject(static_cast<uint32_t>(controller->axisInfos.size()))};
|
||||
|
||||
uint32_t axisIndex = 0;
|
||||
for (const auto &axisInfo : controller->axisInfos) {
|
||||
se::HandleObject jsAxisInfo{se::Object::createPlainObject()};
|
||||
jsAxisInfo->setProperty("code", se::Value(static_cast<uint32_t>(axisInfo.axis)));
|
||||
jsAxisInfo->setProperty("value", se::Value(axisInfo.value));
|
||||
jsAxisInfoList->setArrayElement(axisIndex, se::Value(jsAxisInfo));
|
||||
axisIndex++;
|
||||
}
|
||||
|
||||
se::HandleObject jsTouchInfoList{se::Object::createArrayObject(static_cast<uint32_t>(controller->touchInfos.size()))};
|
||||
|
||||
uint32_t touchIndex = 0;
|
||||
for (const auto &touchInfo : controller->touchInfos) {
|
||||
se::HandleObject jsTouchInfo{se::Object::createPlainObject()};
|
||||
jsTouchInfo->setProperty("code", se::Value(static_cast<uint32_t>(touchInfo.key)));
|
||||
jsTouchInfo->setProperty("value", se::Value(touchInfo.value));
|
||||
jsTouchInfoList->setArrayElement(touchIndex, se::Value(jsTouchInfo));
|
||||
touchIndex++;
|
||||
}
|
||||
jsController->setProperty("axisInfoList", se::Value(jsAxisInfoList));
|
||||
jsController->setProperty("buttonInfoList", se::Value(jsButtonInfoList));
|
||||
jsController->setProperty("touchInfoList", se::Value(jsTouchInfoList));
|
||||
|
||||
jsControllerEventArray->setArrayElement(controllerIndex, se::Value(jsController));
|
||||
controllerIndex++;
|
||||
}
|
||||
se::ValueArray args;
|
||||
args.emplace_back(se::Value(jsControllerEventArray));
|
||||
EventDispatcher::doDispatchJsEvent(eventName, args);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchControllerChangeEvent(const ControllerChangeEvent &changeEvent) {
|
||||
se::AutoHandleScope scope;
|
||||
if (!jsControllerChangeEventArray) {
|
||||
jsControllerChangeEventArray = se::Object::createArrayObject(0);
|
||||
jsControllerChangeEventArray->root();
|
||||
}
|
||||
|
||||
const char *eventName = "onControllerChange";
|
||||
jsControllerChangeEventArray->setProperty("length", se::Value(static_cast<uint32_t>(changeEvent.controllerIds.size())));
|
||||
|
||||
int index = 0;
|
||||
for (const auto id : changeEvent.controllerIds) {
|
||||
jsControllerChangeEventArray->setArrayElement(index++, se::Value(id));
|
||||
}
|
||||
se::ValueArray args;
|
||||
args.emplace_back(se::Value(jsControllerChangeEventArray));
|
||||
EventDispatcher::doDispatchJsEvent(eventName, args);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchTickEvent(float /*dt*/) {
|
||||
if (!se::ScriptEngine::getInstance()->isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
se::AutoHandleScope scope;
|
||||
if (tickVal.isUndefined()) {
|
||||
se::ScriptEngine::getInstance()->getGlobalObject()->getProperty("gameTick", &tickVal);
|
||||
}
|
||||
|
||||
static std::chrono::steady_clock::time_point prevTime;
|
||||
prevTime = std::chrono::steady_clock::now();
|
||||
|
||||
int64_t milliSeconds = std::chrono::duration_cast<std::chrono::milliseconds>(prevTime - se::ScriptEngine::getInstance()->getStartTime()).count();
|
||||
tickArgsValArr[0].setDouble(static_cast<double>(milliSeconds));
|
||||
|
||||
if (!tickVal.isUndefined()) {
|
||||
tickVal.toObject()->call(tickArgsValArr, nullptr);
|
||||
}
|
||||
}
|
||||
// NOLINTNEXTLINE
|
||||
void EventDispatcher::dispatchResizeEvent(int width, int height, uint32_t windowId) {
|
||||
se::AutoHandleScope scope;
|
||||
if (!jsResizeEventObj) {
|
||||
jsResizeEventObj = se::Object::createPlainObject();
|
||||
jsResizeEventObj->root();
|
||||
}
|
||||
|
||||
jsResizeEventObj->setProperty("windowId", se::Value(windowId));
|
||||
jsResizeEventObj->setProperty("width", se::Value(width));
|
||||
jsResizeEventObj->setProperty("height", se::Value(height));
|
||||
|
||||
se::ValueArray args;
|
||||
args.emplace_back(se::Value(jsResizeEventObj));
|
||||
EventDispatcher::doDispatchJsEvent("onResize", args);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchOrientationChangeEvent(int orientation) {
|
||||
// Ts's logic is same as the 'onResize', so remove code here temporary.
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchEnterBackgroundEvent() {
|
||||
EventDispatcher::doDispatchJsEvent("onPause", se::EmptyValueArray);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchEnterForegroundEvent() {
|
||||
EventDispatcher::doDispatchJsEvent("onResume", se::EmptyValueArray);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchMemoryWarningEvent() {
|
||||
EventDispatcher::doDispatchJsEvent("onMemoryWarning", se::EmptyValueArray);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchRestartVM() {
|
||||
EventDispatcher::doDispatchJsEvent("onRestartVM", se::EmptyValueArray);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchCloseEvent() {
|
||||
EventDispatcher::doDispatchJsEvent("onClose", se::EmptyValueArray);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatchPointerlockChangeEvent(bool value) {
|
||||
se::ValueArray args;
|
||||
args.emplace_back(se::Value(value));
|
||||
EventDispatcher::doDispatchJsEvent("onPointerlockChange", args);
|
||||
}
|
||||
|
||||
void EventDispatcher::doDispatchJsEvent(const char *jsFunctionName, const std::vector<se::Value> &args) {
|
||||
if (!se::ScriptEngine::getInstance()->isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
se::AutoHandleScope scope;
|
||||
CC_ASSERT(inited);
|
||||
|
||||
se::Value func;
|
||||
__jsbObj->getProperty(jsFunctionName, &func);
|
||||
if (func.isObject() && func.toObject()->isFunction()) {
|
||||
func.toObject()->call(args, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace cc
|
||||
79
cocos/bindings/event/EventDispatcher.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2018-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 <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "engine/EngineEvents.h"
|
||||
|
||||
namespace se {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace cc {
|
||||
class EventDispatcher {
|
||||
public:
|
||||
static void init();
|
||||
static void destroy();
|
||||
static bool initialized();
|
||||
|
||||
static void doDispatchJsEvent(const char *jsFunctionName, const std::vector<se::Value> &args);
|
||||
|
||||
private:
|
||||
static void dispatchTouchEvent(const TouchEvent &touchEvent);
|
||||
static void dispatchMouseEvent(const MouseEvent &mouseEvent);
|
||||
static void dispatchKeyboardEvent(const KeyboardEvent &keyboardEvent);
|
||||
static void dispatchControllerEvent(const ControllerEvent &controllerEvent);
|
||||
static void dispatchControllerChangeEvent(const ControllerChangeEvent &changeEvent);
|
||||
static void dispatchTickEvent(float dt);
|
||||
static void dispatchResizeEvent(int width, int height, uint32_t windowId = UINT32_MAX);
|
||||
static void dispatchOrientationChangeEvent(int orientation);
|
||||
static void dispatchEnterBackgroundEvent();
|
||||
static void dispatchEnterForegroundEvent();
|
||||
static void dispatchMemoryWarningEvent();
|
||||
static void dispatchRestartVM();
|
||||
static void dispatchCloseEvent();
|
||||
static void dispatchPointerlockChangeEvent(bool value);
|
||||
static uint32_t hashListenerId; // simple increment hash
|
||||
|
||||
static events::EnterForeground::Listener listenerEnterForeground;
|
||||
static events::EnterBackground::Listener listenerEnterBackground;
|
||||
static events::WindowChanged::Listener listenerWindowChanged;
|
||||
static events::LowMemory::Listener listenerLowMemory;
|
||||
static events::Touch::Listener listenerTouch;
|
||||
static events::Mouse::Listener listenerMouse;
|
||||
static events::Keyboard::Listener listenerKeyboard;
|
||||
static events::Controller::Listener listenerConroller;
|
||||
static events::ControllerChange::Listener listenerConrollerChange;
|
||||
static events::Tick::Listener listenerTick;
|
||||
static events::Resize::Listener listenerResize;
|
||||
static events::Orientation::Listener listenerOrientation;
|
||||
static events::RestartVM::Listener listenerRestartVM;
|
||||
static events::Close::Listener listenerClose;
|
||||
static events::PointerLock::Listener listenerPointerLock;
|
||||
};
|
||||
|
||||
} // end of namespace cc
|
||||
47
cocos/bindings/jswrapper/Define.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2022-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 "base/TypeDef.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
/**
|
||||
* PropertyAttribute.
|
||||
* @note Use the same value as which defined in v8, we convert it to v8::PropertyAttribute directly, so don't change the enum value.
|
||||
*/
|
||||
enum class PropertyAttribute : uint8_t {
|
||||
/** NONE. **/
|
||||
NONE = 0,
|
||||
/** READ_ONLY, i.e., not writable. **/
|
||||
READ_ONLY = 1 << 0,
|
||||
/** DONT_ENUM, i.e., not enumerable. **/
|
||||
DONT_ENUM = 1 << 1,
|
||||
/** DONT_DELETE, i.e., not configurable. **/
|
||||
DONT_DELETE = 1 << 2
|
||||
};
|
||||
CC_ENUM_BITWISE_OPERATORS(PropertyAttribute);
|
||||
|
||||
} // namespace se
|
||||
58
cocos/bindings/jswrapper/HandleObject.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/****************************************************************************
|
||||
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 "HandleObject.h"
|
||||
#include "Object.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
HandleObject::HandleObject(Object *obj)
|
||||
: _obj(obj) {
|
||||
if (_obj != nullptr) {
|
||||
// se::HandleObject could not be used for native binding object.
|
||||
CC_ASSERT(!_obj->_getClass());
|
||||
_obj->root();
|
||||
}
|
||||
}
|
||||
|
||||
HandleObject::HandleObject(HandleObject &&o) noexcept{
|
||||
_obj = o._obj;
|
||||
o._obj = nullptr;
|
||||
}
|
||||
|
||||
HandleObject::~HandleObject() {
|
||||
if (_obj != nullptr) {
|
||||
_obj->unroot();
|
||||
_obj->decRef();
|
||||
}
|
||||
}
|
||||
|
||||
HandleObject& HandleObject::operator=(HandleObject &&o) noexcept {
|
||||
_obj = o._obj;
|
||||
o._obj = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
114
cocos/bindings/jswrapper/HandleObject.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/****************************************************************************
|
||||
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 <cstddef>
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* HandleObject is a helper class for easily release, root and unroot an non-native-binding se::Object.
|
||||
|
||||
{
|
||||
se::HandleObject obj(se::Object::createPlainObject());
|
||||
obj->setProperty(...);
|
||||
otherObject->setProperty("foo", se::Value(obj));
|
||||
}
|
||||
|
||||
is equal to:
|
||||
|
||||
{
|
||||
se::Object* obj = se::Object::createPlainObject();
|
||||
obj->root(); // root after object created to avoid object is garbage collected
|
||||
|
||||
obj->setProperty(...);
|
||||
otherObject->setProperty("foo", se::Value(obj));
|
||||
|
||||
obj->unroot(); // unroot object after obj is used.
|
||||
obj->decRef(); // Decrement referent count to avoid memory leak.
|
||||
}
|
||||
|
||||
HandleObject should not be used to create a native binding object since the created binding object
|
||||
should be holded by JavaScript VM and released in finalize callback internally.
|
||||
|
||||
*/
|
||||
class HandleObject {
|
||||
public:
|
||||
/**
|
||||
* @brief The constructor of HandleObject
|
||||
* @param[in] obj The se::Object to attach.
|
||||
*/
|
||||
explicit HandleObject(Object *obj);
|
||||
|
||||
/**
|
||||
* @brief The destructor of HandleObject
|
||||
*/
|
||||
~HandleObject();
|
||||
|
||||
/**
|
||||
* @brief The pointer operator
|
||||
* @return The se::Object attached.
|
||||
*/
|
||||
inline Object *operator->() const {
|
||||
return _obj;
|
||||
}
|
||||
|
||||
inline operator Object *() const { // NOLINT
|
||||
return _obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the se::Object attached.
|
||||
* @return The se::Object attached.
|
||||
*/
|
||||
inline Object *get() const {
|
||||
return _obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests whether HandleObject holds an invalid se::Object.
|
||||
* @return true if HandleObject holds an invalid se::Object, otherwise false.
|
||||
*/
|
||||
inline bool isEmpty() const {
|
||||
return (_obj == nullptr);
|
||||
}
|
||||
HandleObject(HandleObject &&) noexcept;
|
||||
HandleObject& operator=(HandleObject &&) noexcept;
|
||||
|
||||
HandleObject(const HandleObject &) = delete;
|
||||
HandleObject& operator=(const HandleObject &) = delete;
|
||||
|
||||
void *operator new(size_t size) = delete;
|
||||
void operator delete(void *, size_t) = delete;
|
||||
|
||||
private:
|
||||
Object *_obj;
|
||||
friend class Object;
|
||||
};
|
||||
|
||||
} // namespace se
|
||||
104
cocos/bindings/jswrapper/MappingUtils.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/****************************************************************************
|
||||
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 "MappingUtils.h"
|
||||
#include "base/memory/Memory.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
// NativePtrToObjectMap
|
||||
NativePtrToObjectMap::Map *NativePtrToObjectMap::__nativePtrToObjectMap = nullptr; // NOLINT
|
||||
bool NativePtrToObjectMap::__isValid = false; // NOLINT
|
||||
|
||||
bool NativePtrToObjectMap::init() {
|
||||
if (__nativePtrToObjectMap == nullptr) {
|
||||
__nativePtrToObjectMap = ccnew NativePtrToObjectMap::Map();
|
||||
}
|
||||
__isValid = true;
|
||||
return __nativePtrToObjectMap != nullptr;
|
||||
}
|
||||
|
||||
void NativePtrToObjectMap::destroy() {
|
||||
if (__nativePtrToObjectMap != nullptr) {
|
||||
delete __nativePtrToObjectMap;
|
||||
__nativePtrToObjectMap = nullptr;
|
||||
}
|
||||
__isValid = false;
|
||||
}
|
||||
|
||||
bool NativePtrToObjectMap::isValid() {
|
||||
return __isValid;
|
||||
}
|
||||
|
||||
CC_DEPRECATED(3.7)
|
||||
NativePtrToObjectMap::Map::iterator NativePtrToObjectMap::find(void *v) {
|
||||
return __nativePtrToObjectMap->find(v);
|
||||
}
|
||||
|
||||
CC_DEPRECATED(3.7)
|
||||
NativePtrToObjectMap::Map::iterator NativePtrToObjectMap::begin() {
|
||||
return __nativePtrToObjectMap->begin();
|
||||
}
|
||||
|
||||
CC_DEPRECATED(3.7)
|
||||
NativePtrToObjectMap::Map::iterator NativePtrToObjectMap::end() {
|
||||
return __nativePtrToObjectMap->end();
|
||||
}
|
||||
|
||||
void NativePtrToObjectMap::emplace(void *nativeObj, Object *seObj) {
|
||||
__nativePtrToObjectMap->emplace(nativeObj, seObj);
|
||||
}
|
||||
|
||||
NativePtrToObjectMap::Map::iterator NativePtrToObjectMap::erase(Map::iterator iter) {
|
||||
return __nativePtrToObjectMap->erase(iter);
|
||||
}
|
||||
|
||||
void NativePtrToObjectMap::erase(void *nativeObj) {
|
||||
__nativePtrToObjectMap->erase(nativeObj);
|
||||
}
|
||||
|
||||
void NativePtrToObjectMap::erase(void *nativeObj, se::Object *obj) {
|
||||
auto range = __nativePtrToObjectMap->equal_range(nativeObj);
|
||||
for (auto itr = range.first; itr != range.second; itr++) {
|
||||
if (itr->second == obj) {
|
||||
__nativePtrToObjectMap->erase(itr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NativePtrToObjectMap::clear() {
|
||||
__nativePtrToObjectMap->clear();
|
||||
}
|
||||
|
||||
size_t NativePtrToObjectMap::size() {
|
||||
return __nativePtrToObjectMap->size();
|
||||
}
|
||||
|
||||
const NativePtrToObjectMap::Map &NativePtrToObjectMap::instance() {
|
||||
return *__nativePtrToObjectMap;
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
190
cocos/bindings/jswrapper/MappingUtils.h
Normal file
@@ -0,0 +1,190 @@
|
||||
/****************************************************************************
|
||||
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 <type_traits>
|
||||
#include "base/std/container/unordered_map.h"
|
||||
#include "bindings/manual/jsb_classtype.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
class NativePtrToObjectMap {
|
||||
public:
|
||||
// key: native ptr, value: se::Object
|
||||
using Map = ccstd::unordered_multimap<void *, Object *>;
|
||||
|
||||
struct OptionalCallback {
|
||||
se::Object *seObj{nullptr};
|
||||
/**
|
||||
* @brief Invoke callback function when object is empty
|
||||
*/
|
||||
template <typename Fn>
|
||||
OptionalCallback orElse(const Fn &fn) {
|
||||
if (!seObj) {
|
||||
fn();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
/**
|
||||
* @brief Invoke callback function when object is **NOT** empty
|
||||
*/
|
||||
template <typename Fn>
|
||||
OptionalCallback forEach(const Fn &fn) {
|
||||
if (seObj) {
|
||||
fn(seObj);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
static bool init();
|
||||
static void destroy();
|
||||
static bool isValid();
|
||||
|
||||
/**
|
||||
* @deprecated Use `contains` or `filter` to query or manipulate the elements of the map,
|
||||
*/
|
||||
CC_DEPRECATED(3.7)
|
||||
static Map::iterator find(void *v);
|
||||
/**
|
||||
* @deprecated Use `contains` or `filter` to query or manipulate the elements of the map,
|
||||
*/
|
||||
CC_DEPRECATED(3.7)
|
||||
static Map::iterator begin();
|
||||
/**
|
||||
* @deprecated Use `contains` or `filter` to query or manipulate the elements of the map,
|
||||
*/
|
||||
CC_DEPRECATED(3.7)
|
||||
static Map::iterator end();
|
||||
|
||||
static Map::iterator erase(Map::iterator iter);
|
||||
static void erase(void *nativeObj);
|
||||
static void erase(void *nativeObj, se::Object *);
|
||||
static void clear();
|
||||
static size_t size();
|
||||
|
||||
static const Map &instance();
|
||||
|
||||
/**
|
||||
* @brief Return the first element of the specified key
|
||||
*/
|
||||
template <typename T>
|
||||
static se::Object *findFirst(T *nativeObj) {
|
||||
auto itr = __nativePtrToObjectMap->find(nativeObj);
|
||||
return itr == __nativePtrToObjectMap->end() ? nullptr : itr->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the key exists in the map
|
||||
*/
|
||||
template <typename T>
|
||||
static bool contains(T *nativeObj) {
|
||||
if constexpr (std::is_void_v<T>) {
|
||||
return __nativePtrToObjectMap->count(nativeObj) > 0;
|
||||
} else {
|
||||
auto *kls = JSBClassType::findClass(nativeObj);
|
||||
auto range = __nativePtrToObjectMap->equal_range(nativeObj);
|
||||
for (auto itr = range.first; itr != range.second; itr++) {
|
||||
if (itr->second->_getClass() == kls) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterate se::Object with specified key
|
||||
*/
|
||||
template <typename T, typename Fn>
|
||||
static void forEach(T *nativeObj, const Fn &func) {
|
||||
se::Class *kls = nullptr;
|
||||
if constexpr (!std::is_void_v<T>) {
|
||||
kls = JSBClassType::findClass(nativeObj);
|
||||
}
|
||||
auto range = __nativePtrToObjectMap->equal_range(nativeObj);
|
||||
for (auto itr = range.first; itr != range.second; itr++) {
|
||||
if (kls != nullptr && kls != itr->second->_getClass()) {
|
||||
continue;
|
||||
}
|
||||
func(itr->second);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief Filter se::Object* with key and se::Class value
|
||||
*/
|
||||
template <typename T>
|
||||
static OptionalCallback filter(T *nativeObj, se::Class *kls) {
|
||||
se::Object *target{nullptr};
|
||||
findWithCallback(
|
||||
nativeObj, kls,
|
||||
[&](se::Object *seObj) {
|
||||
target = seObj;
|
||||
},
|
||||
nullptr);
|
||||
return OptionalCallback{target};
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Iterate se::Object with specified se::Class
|
||||
*/
|
||||
template <typename T, typename Fn1, typename Fn2>
|
||||
static void findWithCallback(T *nativeObj, se::Class *kls, const Fn1 &eachCallback, const Fn2 &&emptyCallback) {
|
||||
int eleCount = 0;
|
||||
auto range = __nativePtrToObjectMap->equal_range(const_cast<std::remove_const_t<T> *>(nativeObj));
|
||||
constexpr bool hasEmptyCallback = std::is_invocable<Fn2>::value;
|
||||
|
||||
if (range.first == range.second) { // empty
|
||||
if constexpr (hasEmptyCallback) {
|
||||
emptyCallback();
|
||||
}
|
||||
} else {
|
||||
for (auto itr = range.first; itr != range.second; ++itr) {
|
||||
if (kls != nullptr && kls != itr->second->_getClass()) {
|
||||
continue;
|
||||
}
|
||||
eleCount++;
|
||||
CC_ASSERT_LT(eleCount, 2);
|
||||
eachCallback(itr->second);
|
||||
}
|
||||
if constexpr (hasEmptyCallback) {
|
||||
if (eleCount == 0) {
|
||||
emptyCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static void emplace(void *nativeObj, Object *seObj);
|
||||
static Map *__nativePtrToObjectMap; // NOLINT
|
||||
static bool __isValid; // NOLINT
|
||||
|
||||
friend class Object;
|
||||
};
|
||||
|
||||
} // namespace se
|
||||
49
cocos/bindings/jswrapper/Object.h
Normal 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 "config.h"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
#include "sm/Object.h"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
#include "v8/Object.h"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
#include "jsc/Object.h"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
#include "chakracore/Object.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_NAPI
|
||||
#include "napi/Object.h"
|
||||
#endif
|
||||
248
cocos/bindings/jswrapper/PrivateObject.h
Normal file
@@ -0,0 +1,248 @@
|
||||
/****************************************************************************
|
||||
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.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include "base/Ptr.h"
|
||||
#include "base/RefCounted.h"
|
||||
#include "base/memory/Memory.h"
|
||||
#include "base/HasMemberFunction.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
class State;
|
||||
class ScriptEngine;
|
||||
|
||||
template <typename T>
|
||||
class TypedPrivateObject;
|
||||
|
||||
class PrivateObjectBase {
|
||||
public:
|
||||
virtual ~PrivateObjectBase() = default;
|
||||
|
||||
template <typename T>
|
||||
inline T *get() const {
|
||||
return reinterpret_cast<T *>(getRaw());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline TypedPrivateObject<T> *typed() {
|
||||
return reinterpret_cast<TypedPrivateObject<T> *>(this);
|
||||
}
|
||||
virtual const char *getName() const = 0;
|
||||
virtual void *getRaw() const = 0;
|
||||
virtual void allowDestroyInGC() const {
|
||||
CC_ABORT();
|
||||
}
|
||||
virtual void tryAllowDestroyInGC() const {}
|
||||
|
||||
virtual bool isSharedPtr() const { return false; }
|
||||
virtual bool isCCIntrusivePtr() const { return false; }
|
||||
|
||||
friend se::Object;
|
||||
friend se::State;
|
||||
friend se::ScriptEngine;
|
||||
|
||||
void *finalizerData{nullptr};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class TypedPrivateObject : public PrivateObjectBase {
|
||||
public:
|
||||
inline std::shared_ptr<T> share();
|
||||
inline cc::IntrusivePtr<T> &ccShared();
|
||||
inline const char *getName() const override {
|
||||
static_assert(!std::is_base_of<PrivateObjectBase, T>::value, ""); // NOLINT // remove after using c++17
|
||||
return typeid(T).name();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class SharedPtrPrivateObject final : public TypedPrivateObject<T> {
|
||||
public:
|
||||
SharedPtrPrivateObject() = default;
|
||||
explicit SharedPtrPrivateObject(const std::shared_ptr<T> &ptr) : _data(ptr) {}
|
||||
explicit SharedPtrPrivateObject(std::shared_ptr<T> &&ptr) : _data(std::move(ptr)) {}
|
||||
inline const std::shared_ptr<T> &getData() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
inline std::shared_ptr<T> &getData() {
|
||||
return _data;
|
||||
}
|
||||
constexpr bool isSharedPtr() const override { return true; }
|
||||
|
||||
void *getRaw() const override {
|
||||
if constexpr (std::is_const_v<T>) {
|
||||
return reinterpret_cast<void *>(const_cast<std::remove_const_t<T> *>(_data.get()));
|
||||
} else {
|
||||
return reinterpret_cast<void *>(_data.get());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<T> _data{nullptr};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class CCIntrusivePtrPrivateObject final : public TypedPrivateObject<T> {
|
||||
public:
|
||||
CCIntrusivePtrPrivateObject() = default;
|
||||
explicit CCIntrusivePtrPrivateObject(const cc::IntrusivePtr<T> &p) : _ptr(p) {}
|
||||
explicit CCIntrusivePtrPrivateObject(cc::IntrusivePtr<T> &&p) : _ptr(std::move(p)) {}
|
||||
~CCIntrusivePtrPrivateObject() override {
|
||||
if constexpr (cc::has_setScriptObject<T,void(se::Object *)>::value) {
|
||||
_ptr->setScriptObject(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
inline const cc::IntrusivePtr<T> &getData() const { return _ptr; }
|
||||
inline cc::IntrusivePtr<T> &getData() { return _ptr; }
|
||||
|
||||
inline void *getRaw() const override {
|
||||
if constexpr (std::is_const_v<T>) {
|
||||
return reinterpret_cast<void *>(const_cast<std::remove_const_t<T> *>(_ptr.get()));
|
||||
} else {
|
||||
return reinterpret_cast<void *>(_ptr.get());
|
||||
}
|
||||
}
|
||||
inline bool isCCIntrusivePtr() const override { return true; }
|
||||
|
||||
private:
|
||||
cc::IntrusivePtr<T> _ptr;
|
||||
friend TypedPrivateObject<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class RawRefPrivateObject final : public TypedPrivateObject<T> {
|
||||
public:
|
||||
RawRefPrivateObject() = default;
|
||||
explicit RawRefPrivateObject(T *p) : _ptr(p) {}
|
||||
~RawRefPrivateObject() override {
|
||||
static_assert(!std::is_same<T, void>::value, "void is not allowed!");
|
||||
if constexpr (std::is_destructible<T>::value) {
|
||||
if (_allowGC) {
|
||||
delete _ptr;
|
||||
}
|
||||
}
|
||||
_ptr = nullptr;
|
||||
}
|
||||
|
||||
void allowDestroyInGC() const override {
|
||||
_allowGC = true;
|
||||
}
|
||||
void tryAllowDestroyInGC() const override {
|
||||
allowDestroyInGC();
|
||||
}
|
||||
|
||||
void *getRaw() const override {
|
||||
// CC_ASSERT(_validate);
|
||||
if constexpr (std::is_const_v<T>) {
|
||||
return reinterpret_cast<void *>(const_cast<std::remove_const_t<T> *>(_ptr));
|
||||
} else {
|
||||
return reinterpret_cast<void *>(_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
T *_ptr = nullptr;
|
||||
// bool _validate = true;
|
||||
mutable bool _allowGC = false;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline std::shared_ptr<T> TypedPrivateObject<T>::share() {
|
||||
if (isSharedPtr()) {
|
||||
return reinterpret_cast<SharedPtrPrivateObject<T> *>(this)->getData();
|
||||
}
|
||||
CC_ABORT();
|
||||
return std::shared_ptr<T>(nullptr);
|
||||
}
|
||||
template <typename T>
|
||||
inline cc::IntrusivePtr<T> &TypedPrivateObject<T>::ccShared() {
|
||||
CC_ASSERT(isCCIntrusivePtr());
|
||||
return reinterpret_cast<CCIntrusivePtrPrivateObject<T> *>(this)->_ptr;
|
||||
}
|
||||
|
||||
#if CC_DEBUG
|
||||
inline void inHeap(void *ptr) {
|
||||
constexpr size_t r = 4 * 1024; // 4K
|
||||
char a;
|
||||
auto anchor = reinterpret_cast<intptr_t>(&a);
|
||||
auto p = reinterpret_cast<intptr_t>(ptr);
|
||||
// must be in heaps
|
||||
CC_ASSERT(std::abs(anchor - p) > r);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline auto *make_shared_private_object(T *cobj) { // NOLINT
|
||||
static_assert(!std::is_same<T, void>::value, "void * is not allowed");
|
||||
// static_assert(!std::is_pointer_v<T> && !std::is_null_pointer_v<decltype(cobj)>, "bad pointer");
|
||||
#if CC_DEBUG
|
||||
inHeap(cobj);
|
||||
#endif
|
||||
if constexpr (std::is_base_of<cc::RefCounted, T>::value) {
|
||||
return ccnew CCIntrusivePtrPrivateObject<T>(cc::IntrusivePtr<T>(cobj));
|
||||
} else {
|
||||
return ccnew SharedPtrPrivateObject<T>(std::shared_ptr<T>(cobj));
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
inline auto *shared_ptr_private_object(std::shared_ptr<T> &&ptr) { // NOLINT
|
||||
static_assert(!std::is_base_of<cc::RefCounted, T>::value, "cc::RefCounted is not acceptable for shared_ptr");
|
||||
return ccnew SharedPtrPrivateObject<T>(std::forward<std::shared_ptr<T>>(ptr));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto *shared_ptr_private_object(const std::shared_ptr<T> &ptr) { // NOLINT
|
||||
static_assert(!std::is_base_of<cc::RefCounted, T>::value, "cc::RefCounted is not acceptable for shared_ptr");
|
||||
return ccnew SharedPtrPrivateObject<T>(ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto *rawref_private_object(T *ptr) { // NOLINT
|
||||
// static_assert(false, "always fail");
|
||||
// static_assert(!std::is_base_of<cc::RefCounted, T>::value, "cc::RefCounted is not acceptable for shared_ptr");
|
||||
#if CC_DEBUG
|
||||
inHeap(ptr);
|
||||
#endif
|
||||
return ccnew RawRefPrivateObject<T>(ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto *ccintrusive_ptr_private_object(const cc::IntrusivePtr<T> &ptr) { // NOLINT
|
||||
static_assert(std::is_base_of<cc::RefCounted, T>::value, "cc::RefCounted expected!");
|
||||
return ccnew CCIntrusivePtrPrivateObject<T>(ptr);
|
||||
}
|
||||
template <typename T>
|
||||
inline auto *ccintrusive_ptr_private_object(T *cobj) { // NOLINT
|
||||
static_assert(std::is_base_of<cc::RefCounted, T>::value, "cc::RefCounted expected!");
|
||||
return ccnew CCIntrusivePtrPrivateObject<T>(cc::IntrusivePtr<T>(cobj));
|
||||
}
|
||||
} // namespace se
|
||||
52
cocos/bindings/jswrapper/RefCounter.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/****************************************************************************
|
||||
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 "RefCounter.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
void RefCounter::incRef() {
|
||||
++_refCount;
|
||||
}
|
||||
|
||||
void RefCounter::decRef() {
|
||||
--_refCount;
|
||||
if (_refCount == 0) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int RefCounter::getRefCount() {
|
||||
return _refCount;
|
||||
}
|
||||
|
||||
RefCounter::RefCounter()
|
||||
: _refCount(1) {
|
||||
}
|
||||
|
||||
RefCounter::~RefCounter() {
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
65
cocos/bindings/jswrapper/RefCounter.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/****************************************************************************
|
||||
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
|
||||
|
||||
namespace se {
|
||||
|
||||
/**
|
||||
* This class is used to manage reference-counting providing a simple interface and a counter.
|
||||
*
|
||||
*/
|
||||
class RefCounter {
|
||||
public:
|
||||
/**
|
||||
* @brief Increases reference count by one.
|
||||
*/
|
||||
void incRef();
|
||||
|
||||
/**
|
||||
* @brief Decrements the reference count, if it reaches zero, destroys this instance of RefCounter to release its memory.
|
||||
* @note Please note that after calling this function, the caller should absolutely avoid to use the pointer to this instance since it may not be valid anymore.
|
||||
*/
|
||||
void decRef();
|
||||
|
||||
/**
|
||||
* @brief Gets reference count.
|
||||
* @return The reference count.
|
||||
* @note When this goes to zero during a decRef() call, the object will auto-delete itself.
|
||||
*/
|
||||
unsigned int getRefCount();
|
||||
|
||||
protected:
|
||||
// Default constructor
|
||||
// Initialises the internal reference count to 1.
|
||||
RefCounter();
|
||||
// Destructor
|
||||
virtual ~RefCounter();
|
||||
|
||||
private:
|
||||
unsigned int _refCount;
|
||||
};
|
||||
|
||||
} // namespace se
|
||||
53
cocos/bindings/jswrapper/SeApi.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
#include "sm/SeApi.h"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_V8
|
||||
#include "v8/SeApi.h"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_JSC
|
||||
#include "jsc/SeApi.h"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_CHAKRACORE
|
||||
#include "chakracore/SeApi.h"
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_NAPI
|
||||
#include "napi/SeApi.h"
|
||||
#endif
|
||||
|
||||
#include "HandleObject.h"
|
||||
#include "Object.h"
|
||||
#include "State.h"
|
||||
#include "Value.h"
|
||||
111
cocos/bindings/jswrapper/State.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/****************************************************************************
|
||||
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 "Object.h"
|
||||
#include "PrivateObject.h"
|
||||
#include "Value.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* State represents an environment while a function or an accesstor is invoked from JavaScript.
|
||||
*/
|
||||
class State final {
|
||||
public:
|
||||
/**
|
||||
* @brief Gets void* pointer of `this` object's private data.
|
||||
* @return A void* pointer of `this` object's private data.
|
||||
*/
|
||||
inline void *nativeThisObject() const {
|
||||
return _thisObject != nullptr ? _thisObject->getPrivateData() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the arguments of native binding functions or accesstors.
|
||||
* @return The arguments of native binding functions or accesstors.
|
||||
*/
|
||||
inline const ValueArray &args() const {
|
||||
return _args != nullptr ? (*_args) : EmptyValueArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the JavaScript `this` object wrapped in se::Object.
|
||||
* @return The JavaScript `this` object wrapped in se::Object.
|
||||
*/
|
||||
inline Object *thisObject() const {
|
||||
return _thisObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the return value reference. Used for setting return value for a function.
|
||||
* @return The return value reference.
|
||||
*/
|
||||
inline const Value &rval() const {
|
||||
return _retVal;
|
||||
}
|
||||
|
||||
inline Value &rval() {
|
||||
return _retVal;
|
||||
}
|
||||
|
||||
// Private API used in wrapper
|
||||
/**
|
||||
* @brief
|
||||
* @param[in]
|
||||
* @return
|
||||
*/
|
||||
~State() {
|
||||
// Inline to speed up high-frequency calls without significant impact on code size
|
||||
SAFE_DEC_REF(_thisObject);
|
||||
}
|
||||
|
||||
explicit State(Object *thisObject) : _thisObject(thisObject) {
|
||||
if (_thisObject != nullptr) {
|
||||
_thisObject->incRef();
|
||||
}
|
||||
}
|
||||
State(Object *thisObject, const ValueArray &args) : _thisObject(thisObject),
|
||||
_args(&args) {
|
||||
if (_thisObject != nullptr) {
|
||||
_thisObject->incRef();
|
||||
}
|
||||
}
|
||||
|
||||
// Disable copy/move constructor, copy/move assigment
|
||||
State(const State &) = delete;
|
||||
State(State &&) noexcept = delete;
|
||||
State &operator=(const State &) = delete;
|
||||
State &operator=(State &&) noexcept = delete;
|
||||
|
||||
private:
|
||||
Object *_thisObject{nullptr}; // weak ref
|
||||
const ValueArray *_args{nullptr}; // weak ref
|
||||
Value _retVal; // weak ref
|
||||
};
|
||||
} // namespace se
|
||||
576
cocos/bindings/jswrapper/Value.cpp
Normal file
@@ -0,0 +1,576 @@
|
||||
/****************************************************************************
|
||||
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 "Value.h"
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include "Object.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace se {
|
||||
|
||||
#ifndef LIKELY
|
||||
#ifdef __GNUC__
|
||||
#define LIKELY(expr) __builtin_expect(!!(expr), 1)
|
||||
#else
|
||||
#define LIKELY(expr) expr
|
||||
#endif
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
inline
|
||||
typename std::enable_if<std::is_unsigned<T>::value, T>::type
|
||||
fromDoubleToIntegral(double d) {
|
||||
return static_cast<T>(static_cast<int64_t>(d));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline
|
||||
typename std::enable_if<std::is_signed<T>::value, T>::type
|
||||
fromDoubleToIntegral(double d) {
|
||||
return static_cast<T>(d);
|
||||
}
|
||||
|
||||
#define CONVERT_TO_TYPE(type) fromDoubleToIntegral<type>(toDouble())
|
||||
|
||||
ValueArray EmptyValueArray; // NOLINT(readability-identifier-naming)
|
||||
|
||||
Value Value::Null = Value(Type::Null); //NOLINT(readability-identifier-naming)
|
||||
Value Value::Undefined = Value(Type::Undefined); //NOLINT(readability-identifier-naming)
|
||||
|
||||
Value::Value()
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
memset(&_u, 0, sizeof(_u));
|
||||
}
|
||||
|
||||
Value::Value(Type type)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
memset(&_u, 0, sizeof(_u));
|
||||
reset(type);
|
||||
}
|
||||
|
||||
Value::Value(const Value &v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
*this = v;
|
||||
}
|
||||
|
||||
Value::Value(Value &&v) noexcept
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
*this = std::move(v);
|
||||
}
|
||||
|
||||
Value::Value(bool v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setBoolean(v);
|
||||
}
|
||||
|
||||
Value::Value(int8_t v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setInt8(v);
|
||||
}
|
||||
|
||||
Value::Value(uint8_t v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setUint8(v);
|
||||
}
|
||||
|
||||
Value::Value(int32_t v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setInt32(v);
|
||||
}
|
||||
|
||||
Value::Value(uint32_t v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setUint32(v);
|
||||
}
|
||||
|
||||
Value::Value(int16_t v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setInt16(v);
|
||||
}
|
||||
|
||||
Value::Value(uint16_t v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setUint16(v);
|
||||
}
|
||||
|
||||
Value::Value(int64_t v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setInt64(v);
|
||||
}
|
||||
|
||||
Value::Value(uint64_t v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setUint64(v);
|
||||
}
|
||||
|
||||
Value::Value(float v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setFloat(v);
|
||||
}
|
||||
|
||||
Value::Value(double v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setDouble(v);
|
||||
}
|
||||
|
||||
Value::Value(const char *v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setString(v);
|
||||
}
|
||||
|
||||
Value::Value(const ccstd::string &v)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setString(v);
|
||||
}
|
||||
|
||||
Value::Value(Object *o, bool autoRootUnroot /* = false*/)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setObject(o, autoRootUnroot);
|
||||
}
|
||||
|
||||
Value::Value(const HandleObject &o, bool autoRootUnroot /* = false*/)
|
||||
: _type(Type::Undefined),
|
||||
_autoRootUnroot(false) {
|
||||
setObject(o, autoRootUnroot);
|
||||
}
|
||||
|
||||
Value::~Value() {
|
||||
reset(Type::Undefined);
|
||||
}
|
||||
|
||||
Value &Value::operator=(const Value &v) {
|
||||
if (this != &v) {
|
||||
reset(v.getType());
|
||||
|
||||
switch (_type) {
|
||||
case Type::Null:
|
||||
case Type::Undefined: {
|
||||
memset(&_u, 0, sizeof(_u));
|
||||
break;
|
||||
}
|
||||
case Type::Number:
|
||||
_u._number = v._u._number;
|
||||
break;
|
||||
case Type::BigInt:
|
||||
_u._bigint = v._u._bigint;
|
||||
break;
|
||||
case Type::String:
|
||||
*_u._string = *v._u._string;
|
||||
break;
|
||||
case Type::Boolean:
|
||||
_u._boolean = v._u._boolean;
|
||||
break;
|
||||
case Type::Object: {
|
||||
setObject(v._u._object, v._autoRootUnroot);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Value &Value::operator=(Value &&v) noexcept {
|
||||
if (this != &v) {
|
||||
reset(v.getType());
|
||||
|
||||
switch (_type) {
|
||||
case Type::Null:
|
||||
case Type::Undefined: {
|
||||
memset(&_u, 0, sizeof(_u));
|
||||
break;
|
||||
}
|
||||
case Type::Number:
|
||||
_u._number = v._u._number;
|
||||
break;
|
||||
case Type::BigInt:
|
||||
_u._bigint = v._u._bigint;
|
||||
break;
|
||||
case Type::String:
|
||||
*_u._string = std::move(*v._u._string);
|
||||
break;
|
||||
case Type::Boolean:
|
||||
_u._boolean = v._u._boolean;
|
||||
break;
|
||||
case Type::Object: {
|
||||
if (_u._object != nullptr) // When old value is also an Object, reset will take no effect, therefore, _u._object may not be nullptr.
|
||||
{
|
||||
if (_autoRootUnroot) {
|
||||
_u._object->unroot();
|
||||
}
|
||||
_u._object->decRef();
|
||||
}
|
||||
_u._object = v._u._object;
|
||||
_autoRootUnroot = v._autoRootUnroot;
|
||||
v._u._object = nullptr; // Reset to nullptr here to avoid 'release' operation in v.reset(Type::Undefined) since it's a move operation here.
|
||||
v._autoRootUnroot = false;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
v.reset(Type::Undefined);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Value& Value::operator=(bool v)
|
||||
// {
|
||||
// setBoolean(v);
|
||||
// return *this;
|
||||
// }
|
||||
//
|
||||
// Value& Value::operator=(double v)
|
||||
// {
|
||||
// setNumber(v);
|
||||
// return *this;
|
||||
// }
|
||||
//
|
||||
// Value& Value::operator=(const ccstd::string& v)
|
||||
// {
|
||||
// setString(v);
|
||||
// return *this;
|
||||
// }
|
||||
//
|
||||
// Value& Value::operator=(Object* o)
|
||||
// {
|
||||
// setObject(o);
|
||||
// return *this;
|
||||
// }
|
||||
//
|
||||
// Value& Value::operator=(const HandleObject& o)
|
||||
// {
|
||||
// setObject(o);
|
||||
// return *this;
|
||||
// }
|
||||
|
||||
void Value::setUndefined() {
|
||||
reset(Type::Undefined);
|
||||
}
|
||||
|
||||
void Value::setNull() {
|
||||
reset(Type::Null);
|
||||
}
|
||||
|
||||
void Value::setBoolean(bool v) {
|
||||
reset(Type::Boolean);
|
||||
_u._boolean = v;
|
||||
}
|
||||
|
||||
void Value::setInt8(int8_t v) {
|
||||
reset(Type::Number);
|
||||
_u._number = static_cast<double>(v);
|
||||
}
|
||||
|
||||
void Value::setUint8(uint8_t v) {
|
||||
reset(Type::Number);
|
||||
_u._number = static_cast<double>(v);
|
||||
}
|
||||
|
||||
void Value::setInt32(int32_t v) {
|
||||
reset(Type::Number);
|
||||
_u._number = static_cast<double>(v);
|
||||
}
|
||||
|
||||
void Value::setUint32(uint32_t v) {
|
||||
reset(Type::Number);
|
||||
_u._number = static_cast<double>(v);
|
||||
}
|
||||
|
||||
void Value::setInt16(int16_t v) {
|
||||
reset(Type::Number);
|
||||
_u._number = static_cast<double>(v);
|
||||
}
|
||||
|
||||
void Value::setUint16(uint16_t v) {
|
||||
reset(Type::Number);
|
||||
_u._number = static_cast<double>(v);
|
||||
}
|
||||
|
||||
void Value::setInt64(int64_t v) {
|
||||
reset(Type::BigInt);
|
||||
_u._bigint = v;
|
||||
}
|
||||
|
||||
void Value::setUint64(uint64_t v) {
|
||||
reset(Type::BigInt);
|
||||
_u._bigint = static_cast<int64_t>(v);
|
||||
}
|
||||
|
||||
void Value::setFloat(float v) {
|
||||
reset(Type::Number);
|
||||
_u._number = static_cast<double>(v);
|
||||
}
|
||||
|
||||
void Value::setDouble(double v) {
|
||||
reset(Type::Number);
|
||||
_u._number = v;
|
||||
}
|
||||
|
||||
void Value::setString(const char *v) {
|
||||
if (v != nullptr) {
|
||||
reset(Type::String);
|
||||
*_u._string = v;
|
||||
} else {
|
||||
reset(Type::Null);
|
||||
}
|
||||
}
|
||||
|
||||
void Value::setString(const ccstd::string &v) {
|
||||
reset(Type::String);
|
||||
*_u._string = v;
|
||||
}
|
||||
|
||||
void Value::setString(const std::string_view &v) {
|
||||
reset(Type::String);
|
||||
_u._string->assign(v.data(), 0, v.length());
|
||||
}
|
||||
|
||||
void Value::setObject(Object *object, bool autoRootUnroot /* = false*/) {
|
||||
if (object == nullptr) {
|
||||
reset(Type::Null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_type != Type::Object) {
|
||||
reset(Type::Object);
|
||||
}
|
||||
|
||||
if (_u._object != object) {
|
||||
if (object != nullptr) {
|
||||
object->incRef();
|
||||
if (autoRootUnroot) {
|
||||
object->root();
|
||||
}
|
||||
}
|
||||
|
||||
if (_u._object != nullptr) // When old value is also an Object, reset will take no effect, therefore, _u._object may not be nullptr.
|
||||
{
|
||||
if (_autoRootUnroot) {
|
||||
_u._object->unroot();
|
||||
}
|
||||
_u._object->decRef();
|
||||
}
|
||||
_u._object = object;
|
||||
_autoRootUnroot = autoRootUnroot;
|
||||
} else {
|
||||
_autoRootUnroot = autoRootUnroot;
|
||||
if (_autoRootUnroot) {
|
||||
_u._object->root();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Value::setObject(const HandleObject &o, bool autoRootUnroot /* = false*/) {
|
||||
setObject(o.get(), autoRootUnroot);
|
||||
}
|
||||
|
||||
int8_t Value::toInt8() const {
|
||||
return CONVERT_TO_TYPE(int8_t);
|
||||
}
|
||||
|
||||
uint8_t Value::toUint8() const {
|
||||
return CONVERT_TO_TYPE(uint8_t);
|
||||
}
|
||||
|
||||
int16_t Value::toInt16() const {
|
||||
return CONVERT_TO_TYPE(int16_t);
|
||||
}
|
||||
|
||||
uint16_t Value::toUint16() const {
|
||||
return CONVERT_TO_TYPE(uint16_t);
|
||||
}
|
||||
|
||||
int32_t Value::toInt32() const {
|
||||
return CONVERT_TO_TYPE(int32_t);
|
||||
}
|
||||
|
||||
uint32_t Value::toUint32() const {
|
||||
return CONVERT_TO_TYPE(uint32_t);
|
||||
}
|
||||
|
||||
int64_t Value::toInt64() const {
|
||||
CC_ASSERT(isBigInt() || isNumber());
|
||||
return _type == Type::BigInt ? _u._bigint : CONVERT_TO_TYPE(int64_t);
|
||||
}
|
||||
|
||||
uint64_t Value::toUint64() const {
|
||||
CC_ASSERT(isBigInt() || isNumber());
|
||||
return _type == Type::BigInt ? static_cast<uint64_t>(_u._bigint) : CONVERT_TO_TYPE(uint64_t);
|
||||
}
|
||||
|
||||
float Value::toFloat() const {
|
||||
return static_cast<float>(toDouble());
|
||||
}
|
||||
|
||||
double Value::toDouble() const {
|
||||
CC_ASSERT(_type == Type::Number || _type == Type::Boolean || _type == Type::BigInt || _type == Type::String);
|
||||
if (LIKELY(_type == Type::Number)) {
|
||||
return _u._number;
|
||||
}
|
||||
if (_type == Type::BigInt) {
|
||||
// CC_LOG_WARNING("convert int64 to double");
|
||||
return static_cast<double>(_u._bigint);
|
||||
}
|
||||
|
||||
if (_type == Type::String) {
|
||||
return std::stod(*_u._string);
|
||||
}
|
||||
|
||||
return _u._boolean ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
bool Value::toBoolean() const {
|
||||
CC_ASSERT_EQ(_type, Type::Boolean);
|
||||
return _u._boolean;
|
||||
}
|
||||
|
||||
const ccstd::string &Value::toString() const {
|
||||
CC_ASSERT_EQ(_type, Type::String);
|
||||
return *_u._string;
|
||||
}
|
||||
|
||||
ccstd::string Value::toStringForce() const {
|
||||
std::stringstream ss;
|
||||
if (_type == Type::String) {
|
||||
ss << *_u._string;
|
||||
} else if (_type == Type::Boolean) {
|
||||
ss << (_u._boolean ? "true" : "false");
|
||||
} else if (_type == Type::Number) {
|
||||
char tmp[50] = {0};
|
||||
snprintf(tmp, sizeof(tmp), "%.17g", _u._number);
|
||||
ss << tmp;
|
||||
} else if (_type == Type::BigInt) {
|
||||
ss << _u._bigint;
|
||||
} else if (_type == Type::Object) {
|
||||
ss << toObject()->toString();
|
||||
} else if (_type == Type::Null) {
|
||||
ss << "null";
|
||||
} else if (_type == Type::Undefined) {
|
||||
ss << "undefined";
|
||||
} else {
|
||||
CC_ABORT();
|
||||
ss << "[[BadValueType]]";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Object *Value::toObject() const {
|
||||
assert(isObject());
|
||||
return _u._object;
|
||||
}
|
||||
|
||||
void Value::reset(Type type) {
|
||||
if (_type != type) {
|
||||
switch (_type) {
|
||||
case Type::String:
|
||||
delete _u._string;
|
||||
break;
|
||||
case Type::Object: {
|
||||
if (_u._object != nullptr) {
|
||||
if (_autoRootUnroot) {
|
||||
_u._object->unroot();
|
||||
}
|
||||
_u._object->decRef();
|
||||
_u._object = nullptr;
|
||||
}
|
||||
|
||||
_autoRootUnroot = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&_u, 0, sizeof(_u));
|
||||
|
||||
switch (type) {
|
||||
case Type::String:
|
||||
_u._string = ccnew ccstd::string();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_type = type;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////// deprecated methods ////////////////////
|
||||
|
||||
void Value::setLong(long v) { // NOLINT(google-runtime-int)
|
||||
setDouble(static_cast<double>(v));
|
||||
}
|
||||
|
||||
void Value::setUIntptr_t(uintptr_t v) {
|
||||
setDouble(static_cast<double>(v));
|
||||
}
|
||||
|
||||
void Value::setUlong(unsigned long v) { // NOLINT(google-runtime-int)
|
||||
setDouble(static_cast<double>(v));
|
||||
}
|
||||
|
||||
void Value::setNumber(double v) {
|
||||
setDouble(v);
|
||||
}
|
||||
|
||||
unsigned int Value::toUint() const {
|
||||
return CONVERT_TO_TYPE(unsigned int);
|
||||
}
|
||||
|
||||
long Value::toLong() const { // NOLINT(google-runtime-int)
|
||||
return CONVERT_TO_TYPE(long);
|
||||
}
|
||||
|
||||
unsigned long Value::toUlong() const { // NOLINT(google-runtime-int)
|
||||
return CONVERT_TO_TYPE(unsigned long);
|
||||
}
|
||||
|
||||
double Value::toNumber() const {
|
||||
return toDouble();
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
498
cocos/bindings/jswrapper/Value.h
Normal file
@@ -0,0 +1,498 @@
|
||||
/****************************************************************************
|
||||
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 <string_view>
|
||||
#include "HandleObject.h"
|
||||
#include "base/Macros.h"
|
||||
#include "base/std/container/string.h"
|
||||
#include "base/std/container/vector.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* se::Value represents a JavaScript Value.
|
||||
* It could be undefined, null, number, boolean, string and object which exists in JavaScript.
|
||||
*/
|
||||
class Value final {
|
||||
public:
|
||||
enum class Type : char {
|
||||
Undefined = 0, // NOLINT(readability-identifier-naming)
|
||||
Null, // NOLINT(readability-identifier-naming)
|
||||
Number, // NOLINT(readability-identifier-naming)
|
||||
Boolean, // NOLINT(readability-identifier-naming)
|
||||
String, // NOLINT(readability-identifier-naming)
|
||||
Object, // NOLINT(readability-identifier-naming)
|
||||
BigInt, // NOLINT(readability-identifier-naming)
|
||||
};
|
||||
|
||||
static Value Null; // NOLINT(readability-identifier-naming)
|
||||
static Value Undefined; // NOLINT(readability-identifier-naming)
|
||||
|
||||
/**
|
||||
* @brief The default constructor.
|
||||
*/
|
||||
Value();
|
||||
|
||||
/**
|
||||
* @brief The copy constructor.
|
||||
*/
|
||||
Value(const Value &v);
|
||||
|
||||
/**
|
||||
* @brief The move constructor.
|
||||
*/
|
||||
Value(Value &&v) noexcept;
|
||||
|
||||
/**
|
||||
* @brief The constructor with a boolean argument.
|
||||
*/
|
||||
explicit Value(bool v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a int8_t argument.
|
||||
*/
|
||||
explicit Value(int8_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a uint8_t argument.
|
||||
*/
|
||||
explicit Value(uint8_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a int16_t argument.
|
||||
*/
|
||||
explicit Value(int16_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a uint16_t argument.
|
||||
*/
|
||||
explicit Value(uint16_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a int32_t argument.
|
||||
*/
|
||||
explicit Value(int32_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a uint32_t argument.
|
||||
*/
|
||||
explicit Value(uint32_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a uint64_t argument.
|
||||
*/
|
||||
explicit Value(uint64_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a int64_t argument.
|
||||
*/
|
||||
explicit Value(int64_t v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a float argument.
|
||||
*/
|
||||
explicit Value(float v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a double argument.
|
||||
*/
|
||||
explicit Value(double v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with an UTF8 null-terminated string argument.
|
||||
*/
|
||||
explicit Value(const char *v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with an UTF8 string argument.
|
||||
*/
|
||||
explicit Value(const ccstd::string &v);
|
||||
|
||||
/**
|
||||
* @brief The constructor with an Object.
|
||||
* @param o A se::Object to be set.
|
||||
* @param[in] autoRootUnroot Whether to root se::Object and unroot it in se::Value's destructor or unroot it while object is replaced. Default value is false.
|
||||
*/
|
||||
explicit Value(Object *o, bool autoRootUnroot = false);
|
||||
|
||||
/**
|
||||
* @brief The constructor with a HandleObject.
|
||||
* @param o A se::HandleObject to be set.
|
||||
* @param[in] autoRootUnroot Whether to root se::HandleObject and unroot it in se::Value's destructor or unroot it while object is replaced. Default value is false.
|
||||
*/
|
||||
explicit Value(const HandleObject &o, bool autoRootUnroot = false);
|
||||
|
||||
/**
|
||||
* @brief The destructor of se::Value
|
||||
*/
|
||||
~Value();
|
||||
|
||||
/**
|
||||
* @brief The copy assignment operator.
|
||||
*/
|
||||
Value &operator=(const Value &v);
|
||||
|
||||
/**
|
||||
* @brief The move assignment operator.
|
||||
*/
|
||||
Value &operator=(Value &&v) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a long value.
|
||||
* @param[in] v The long value to be set.
|
||||
*/
|
||||
CC_DEPRECATED(3.3)
|
||||
void setLong(long v); // NOLINT(google-runtime-int)
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a uintptr_t value.
|
||||
* @param[in] v The uintptr_t value to be set.
|
||||
*/
|
||||
CC_DEPRECATED(3.3)
|
||||
void setUIntptr_t(uintptr_t v); // NOLINT(readability-identifier-naming)
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a unsigned long value.
|
||||
* @param[in] v The unsigned long value to be set.
|
||||
*/
|
||||
CC_DEPRECATED(3.3)
|
||||
void setUlong(unsigned long v); // NOLINT(google-runtime-int)
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a double value.
|
||||
* @param[in] v The double value to be set.
|
||||
*/
|
||||
CC_DEPRECATED(3.3, "Use setDouble instead")
|
||||
void setNumber(double v);
|
||||
|
||||
CC_DEPRECATED(3.3)
|
||||
unsigned int toUint() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to long.
|
||||
* @return long integer.
|
||||
*/
|
||||
CC_DEPRECATED(3.3)
|
||||
long toLong() const; // NOLINT(google-runtime-int)
|
||||
/**
|
||||
* @brief Converts se::Value to unsigned long.
|
||||
* @return unsigned long integer.
|
||||
*/
|
||||
CC_DEPRECATED(3.3)
|
||||
unsigned long toUlong() const; // NOLINT(google-runtime-int)
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to double number.
|
||||
* @return double number.
|
||||
*/
|
||||
CC_DEPRECATED(3.3, "Use toDouble instead")
|
||||
double toNumber() const;
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to undefined.
|
||||
*/
|
||||
void setUndefined();
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to null.
|
||||
*/
|
||||
void setNull();
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a boolean value.
|
||||
* @param[in] v The boolean value to be set.
|
||||
*/
|
||||
void setBoolean(bool v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a int8_t value.
|
||||
* @param[in] v The int8_t value to be set.
|
||||
*/
|
||||
void setInt8(int8_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a uint8_t value.
|
||||
* @param[in] v The uint8_t value to be set.
|
||||
*/
|
||||
void setUint8(uint8_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a int16_t value.
|
||||
* @param[in] v The int16_t value to be set.
|
||||
*/
|
||||
void setInt16(int16_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a uint16_t value.
|
||||
* @param[in] v The uint16_t value to be set.
|
||||
*/
|
||||
void setUint16(uint16_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a int32_t value.
|
||||
* @param[in] v The int32_t value to be set.
|
||||
*/
|
||||
void setInt32(int32_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a uint32_t value.
|
||||
* @param[in] v The uint32_t value to be set.
|
||||
*/
|
||||
void setUint32(uint32_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a unsigned int64_t
|
||||
* @param[in] v The unsigned int64_t value to be set.
|
||||
*/
|
||||
void setUint64(uint64_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a int64_t value.
|
||||
* @param[in] v The int64_t value to be set.
|
||||
*/
|
||||
void setInt64(int64_t v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a float value.
|
||||
* @param[in] v The float value to be set.
|
||||
*/
|
||||
void setFloat(float v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to a double value.
|
||||
* @param[in] v The double value to be set.
|
||||
*/
|
||||
void setDouble(double v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to an UTF8 null-terminated string value.
|
||||
* @param[in] v The UTF8 null-terminated string value to be set.
|
||||
*/
|
||||
void setString(const char *v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to string value.
|
||||
* @param[in] v The string value to be set.
|
||||
*/
|
||||
void setString(const ccstd::string &v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to string value by string_view.
|
||||
* @param[in] v The string_view
|
||||
*/
|
||||
void setString(const std::string_view &v);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to se::Object value.
|
||||
* @param[in] o The se::Object to be set.
|
||||
* @param[in] autoRootUnroot Whether to root se::Object and unroot it in se::Value's destructor or unroot it while object is replaced. Default value is false.
|
||||
*/
|
||||
void setObject(Object *o, bool autoRootUnroot = false);
|
||||
|
||||
/**
|
||||
* @brief Sets se::Value to se::HandleObject value.
|
||||
* @param[in] o The se::Object to be set.
|
||||
* @param[in] autoRootUnroot Whether to root se::HandleObject and unroot it in se::Value's destructor or unroot it while object is replaced. Default value is false.
|
||||
*/
|
||||
void setObject(const HandleObject &o, bool autoRootUnroot = false);
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to int8_t.
|
||||
* @return int8_t integer.
|
||||
*/
|
||||
int8_t toInt8() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to uint8_t.
|
||||
* @return uint8_t integer.
|
||||
*/
|
||||
uint8_t toUint8() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to int16_t.
|
||||
* @return int16_t integer.
|
||||
*/
|
||||
int16_t toInt16() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to uint16_t.
|
||||
* @return uint16_t integer.
|
||||
*/
|
||||
uint16_t toUint16() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to int32_t.
|
||||
* @return int32_t integer.
|
||||
*/
|
||||
int32_t toInt32() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to uint32_t.
|
||||
* @return uint32_t integer.
|
||||
*/
|
||||
uint32_t toUint32() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to int64_t
|
||||
* @return signed int64
|
||||
*/
|
||||
int64_t toInt64() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to unsigned uint64_t.
|
||||
* @return unsigned int64.
|
||||
*/
|
||||
uint64_t toUint64() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to float number.
|
||||
* @return float number.
|
||||
*/
|
||||
float toFloat() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to double number.
|
||||
* @return double number.
|
||||
*/
|
||||
double toDouble() const;
|
||||
|
||||
/**
|
||||
* @brief Converts se::Value to boolean.
|
||||
* @return boolean.
|
||||
*/
|
||||
bool toBoolean() const;
|
||||
|
||||
/**
|
||||
* @brief Gets ccstd::string if se::Value stores a string. It will trigger an assertion if se::Value isn't a string.
|
||||
* @return A ccstd::string reference.
|
||||
* @see toStringForce
|
||||
*/
|
||||
const ccstd::string &toString() const;
|
||||
|
||||
/**
|
||||
* @brief Converts a se::Value to ccstd::string. Could be invoked even when se::Value isn't a string.
|
||||
* @return A copied ccstd::string value.
|
||||
* @see toString
|
||||
*/
|
||||
ccstd::string toStringForce() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the se::Object pointer if se::Value stores an object. It will trigger an assertion if se::Value isn't an object.
|
||||
* @return A se::Object pointer.
|
||||
*/
|
||||
Object *toObject() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the type of se::Value.
|
||||
* @return The type of se::Value.
|
||||
*/
|
||||
inline Type getType() const { return _type; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a number.
|
||||
* @return true if se::Value stores a number, otherwise false.
|
||||
*/
|
||||
inline bool isNumber() const { return _type == Type::Number; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a Bigint.
|
||||
* @return true if se::Value stores a uint64_t or a int64_t, otherwise false.
|
||||
*/
|
||||
inline bool isBigInt() const { return _type == Type::BigInt; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a string.
|
||||
* @return true if se::Value stores a string, otherwise false.
|
||||
*/
|
||||
inline bool isString() const { return _type == Type::String; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores an object.
|
||||
* @return true if se::Value stores an object, otherwise false.
|
||||
*/
|
||||
inline bool isObject() const { return _type == Type::Object; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a boolean.
|
||||
* @return true if se::Value stores a boolean, otherwise false.
|
||||
*/
|
||||
inline bool isBoolean() const { return _type == Type::Boolean; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores an undefined value.
|
||||
* @return true if se::Value stores an undefined value, otherwise false.
|
||||
*/
|
||||
inline bool isUndefined() const { return _type == Type::Undefined; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a null value.
|
||||
* @return true if se::Value stores a null value, otherwise false.
|
||||
*/
|
||||
inline bool isNull() const { return _type == Type::Null; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether se::Value stores a null or an undefined value.
|
||||
* @return true if se::Value stores a null or an undefined value, otherwise false.
|
||||
*/
|
||||
inline bool isNullOrUndefined() const { return (isNull() || isUndefined()); }
|
||||
|
||||
size_t toSize() const {
|
||||
return static_cast<size_t>(toDouble());
|
||||
}
|
||||
|
||||
void setSize(size_t v) {
|
||||
setDouble(static_cast<double>(v));
|
||||
}
|
||||
|
||||
uintptr_t asPtr() const {
|
||||
return static_cast<uintptr_t>(toUint64());
|
||||
}
|
||||
|
||||
private:
|
||||
explicit Value(Type type);
|
||||
void reset(Type type);
|
||||
|
||||
union {
|
||||
bool _boolean;
|
||||
double _number;
|
||||
ccstd::string *_string;
|
||||
Object *_object;
|
||||
int64_t _bigint;
|
||||
} _u;
|
||||
|
||||
Type _type;
|
||||
bool _autoRootUnroot;
|
||||
};
|
||||
|
||||
using ValueArray = ccstd::vector<Value>;
|
||||
extern ValueArray EmptyValueArray; // NOLINT(readability-identifier-naming)
|
||||
|
||||
} // namespace se
|
||||
|
||||
using se_object_ptr = se::Object *; // NOLINT(readability-identifier-naming)
|
||||
68
cocos/bindings/jswrapper/ValueArrayPool.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/****************************************************************************
|
||||
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 "ValueArrayPool.h"
|
||||
#include "base/Macros.h"
|
||||
#include "base/memory/Memory.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
ValueArrayPool gValueArrayPool;
|
||||
|
||||
#define SE_DEFAULT_MAX_DEPTH (5)
|
||||
|
||||
ValueArrayPool::ValueArrayPool() {
|
||||
_pools.resize(SE_DEFAULT_MAX_DEPTH);
|
||||
for (uint32_t i = 0; i < SE_DEFAULT_MAX_DEPTH; ++i) {
|
||||
initPool(i);
|
||||
}
|
||||
}
|
||||
|
||||
ValueArray &ValueArrayPool::get(uint32_t argc, bool &outNeedDelete) {
|
||||
if (SE_UNLIKELY(_depth >= _pools.size())) {
|
||||
outNeedDelete = true;
|
||||
auto *ret = ccnew ValueArray();
|
||||
ret->resize(argc);
|
||||
return *ret;
|
||||
}
|
||||
|
||||
outNeedDelete = false;
|
||||
CC_ASSERT_LE(argc, MAX_ARGS);
|
||||
auto &ret = _pools[_depth][argc];
|
||||
CC_ASSERT(ret.size() == argc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ValueArrayPool::initPool(uint32_t index) {
|
||||
auto &pool = _pools[index];
|
||||
uint32_t i = 0;
|
||||
for (auto &arr : pool) {
|
||||
arr.resize(i);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
73
cocos/bindings/jswrapper/ValueArrayPool.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/****************************************************************************
|
||||
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 "Value.h"
|
||||
#include "base/std/container/array.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class CallbackDepthGuard final {
|
||||
public:
|
||||
CallbackDepthGuard(ValueArray &arr, uint32_t &depth, bool needDelete)
|
||||
: _arr(arr), _depth(depth), _needDelete(needDelete) {
|
||||
++_depth;
|
||||
}
|
||||
|
||||
~CallbackDepthGuard() {
|
||||
--_depth;
|
||||
for (auto &e : _arr) {
|
||||
e.setUndefined();
|
||||
}
|
||||
if (_needDelete) {
|
||||
delete &_arr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ValueArray &_arr;
|
||||
uint32_t &_depth;
|
||||
const bool _needDelete{false};
|
||||
};
|
||||
|
||||
class ValueArrayPool final {
|
||||
public:
|
||||
static const uint32_t MAX_ARGS = 20;
|
||||
|
||||
ValueArrayPool();
|
||||
|
||||
ValueArray &get(uint32_t argc, bool &outNeedDelete);
|
||||
|
||||
uint32_t _depth{0};
|
||||
|
||||
private:
|
||||
void initPool(uint32_t index);
|
||||
ccstd::vector<ccstd::array<ValueArray, MAX_ARGS + 1>> _pools;
|
||||
};
|
||||
|
||||
extern ValueArrayPool gValueArrayPool;
|
||||
|
||||
} // namespace se
|
||||
36
cocos/bindings/jswrapper/config.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/****************************************************************************
|
||||
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 "bindings/jswrapper/config.h"
|
||||
#include <cstdarg>
|
||||
#include "base/Log.h"
|
||||
|
||||
void selogMessage(cc::LogLevel level, const char *tag, const char *format, ...) {
|
||||
char logbuf[512] = {0};
|
||||
va_list argp;
|
||||
va_start(argp, format);
|
||||
(void)std::vsnprintf(logbuf, sizeof(logbuf), format, argp);
|
||||
va_end(argp);
|
||||
cc::Log::logMessage(cc::LogType::KERNEL, level, "%s %s", tag, logbuf);
|
||||
}
|
||||
107
cocos/bindings/jswrapper/config.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/****************************************************************************
|
||||
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 <cstdio>
|
||||
|
||||
#include "base/Log.h"
|
||||
|
||||
#define SCRIPT_ENGINE_NONE 0
|
||||
#define SCRIPT_ENGINE_SM 1
|
||||
#define SCRIPT_ENGINE_V8 2
|
||||
#define SCRIPT_ENGINE_NAPI 5
|
||||
|
||||
#ifndef SCRIPT_ENGINE_TYPE
|
||||
#if CC_PLATFORM == CC_PLATFORM_OPENHARMONY
|
||||
#define SCRIPT_ENGINE_TYPE SCRIPT_ENGINE_NAPI
|
||||
#else
|
||||
#define SCRIPT_ENGINE_TYPE SCRIPT_ENGINE_V8
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define SE_LOG_TO_JS_ENV 0 // print log to JavaScript environment, for example DevTools
|
||||
|
||||
#if !defined(ANDROID_INSTANT) && defined(USE_V8_DEBUGGER) && USE_V8_DEBUGGER > 0
|
||||
#define SE_ENABLE_INSPECTOR 1
|
||||
#define SE_DEBUG 2
|
||||
#define HAVE_INSPECTOR 1
|
||||
#else
|
||||
#define SE_ENABLE_INSPECTOR 0
|
||||
#define SE_DEBUG 0
|
||||
#define HAVE_INSPECTOR 0
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define CC_FORMAT_HINT(si, fi) __attribute__((__format__(__printf__, si, fi)))
|
||||
#else
|
||||
#define CC_FORMAT_HINT(si, fi)
|
||||
#endif
|
||||
|
||||
CC_FORMAT_HINT(3, 4)
|
||||
void selogMessage(cc::LogLevel level, const char *tag, const char *format, ...);
|
||||
|
||||
#if CC_DEBUG
|
||||
#define SE_LOGD(...) selogMessage(cc::LogLevel::LEVEL_DEBUG, "D/", ##__VA_ARGS__)
|
||||
#else
|
||||
#define SE_LOGD(...)
|
||||
#endif
|
||||
#define SE_LOGE(...) selogMessage(cc::LogLevel::ERR, "E/", ##__VA_ARGS__)
|
||||
|
||||
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
||||
|
||||
#define __POSIX__ //NOLINT
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(_WINDOWS)
|
||||
#include <BaseTsd.h>
|
||||
|
||||
#if !defined(__SSIZE_T)
|
||||
typedef SSIZE_T ssize_t;
|
||||
#define __SSIZE_T
|
||||
#define _SSIZE_T_DEFINED // libuv also defines ssize_t, use the one defined here.
|
||||
#endif // __SSIZE_T
|
||||
|
||||
#endif // #if defined(_WIN32) && defined(_WINDOWS)
|
||||
|
||||
/** @def SE_DEPRECATED_ATTRIBUTE
|
||||
* Only certain compilers support __attribute__((deprecated)).
|
||||
*/
|
||||
#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)))
|
||||
#define SE_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
|
||||
#elif _MSC_VER >= 1400 //vs 2005 or higher
|
||||
#define SE_DEPRECATED_ATTRIBUTE __declspec(deprecated)
|
||||
#else
|
||||
#define SE_DEPRECATED_ATTRIBUTE
|
||||
#endif // SE_DEPRECATED_ATTRIBUTE
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
#define SE_LIKELY(x) (__builtin_expect((x), 1))
|
||||
#define SE_UNLIKELY(x) (__builtin_expect((x), 0))
|
||||
#else
|
||||
#define SE_LIKELY(x) (x)
|
||||
#define SE_UNLIKELY(x) (x)
|
||||
#endif
|
||||
248
cocos/bindings/jswrapper/napi/Class.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "Class.h"
|
||||
#include <string>
|
||||
#include "CommonHeader.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
napi_value *Class::_exports = nullptr;
|
||||
std::vector<Class *> __allClasses;
|
||||
|
||||
Class::Class() {
|
||||
__allClasses.push_back(this);
|
||||
};
|
||||
|
||||
Class::~Class() {
|
||||
}
|
||||
|
||||
/* static */
|
||||
Class *Class::create(const std::string &clsName, se::Object *parent, Object *parentProto, napi_callback ctor) {
|
||||
Class *cls = new Class();
|
||||
if (cls != nullptr && !cls->init(clsName, parent, parentProto, ctor)) {
|
||||
delete cls;
|
||||
cls = nullptr;
|
||||
}
|
||||
return cls;
|
||||
}
|
||||
|
||||
Class* Class::create(const std::initializer_list<const char *> &classPath, se::Object *parent, Object *parentProto, napi_callback ctor) {
|
||||
se::AutoHandleScope scope;
|
||||
se::Object *currentParent = parent;
|
||||
se::Value tmp;
|
||||
for (auto i = 0; i < classPath.size() - 1; i++) {
|
||||
bool ok = currentParent->getProperty(*(classPath.begin() + i), &tmp);
|
||||
CC_ASSERT(ok); // class or namespace in path is not defined
|
||||
currentParent = tmp.toObject();
|
||||
}
|
||||
return create(*(classPath.end() - 1), currentParent, parentProto, ctor);
|
||||
}
|
||||
|
||||
bool Class::init(const std::string &clsName, Object *parent, Object *parentProto, napi_callback ctor) {
|
||||
_name = clsName;
|
||||
_parent = parent;
|
||||
if (_parent != nullptr)
|
||||
_parent->incRef();
|
||||
_parentProto = parentProto;
|
||||
|
||||
if (_parentProto != nullptr)
|
||||
_parentProto->incRef();
|
||||
if (ctor) {
|
||||
_ctorFunc = ctor;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
napi_value Class::_defaultCtor(napi_env env, napi_callback_info info) {
|
||||
LOGE("check default ctor called");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Class::defineProperty(const char* name, napi_callback g, napi_callback s) {
|
||||
_properties.push_back({name, nullptr, nullptr, g, s, 0, napi_default_jsproperty, 0});
|
||||
}
|
||||
|
||||
void Class::defineProperty(const std::initializer_list<const char *> &names, napi_callback g, napi_callback s) {
|
||||
for (const auto *name : names) {
|
||||
defineProperty(name, g, s);
|
||||
}
|
||||
}
|
||||
|
||||
void Class::defineStaticProperty(const char* name, napi_callback g, napi_callback s) {
|
||||
if(g != nullptr && s != nullptr)
|
||||
_properties.push_back({name, nullptr, nullptr, g, s, 0, napi_static, 0});
|
||||
}
|
||||
|
||||
bool Class::defineStaticProperty(const char *name, const Value &v, PropertyAttribute attribute /* = PropertyAttribute::NONE */) {
|
||||
// TODO(qgh): Assigning get and set to nullptr in openharmony will cause a crash
|
||||
//napi_value value;
|
||||
//internal::seToJsValue(v, &value);
|
||||
//_properties.push_back({name, nullptr, nullptr, nullptr, nullptr, value, napi_static, 0});
|
||||
return true;
|
||||
}
|
||||
|
||||
void Class::defineFunction(const char* name, napi_callback func) {
|
||||
// When Napi defines a function, it needs to add the enum attribute, otherwise JS cannot traverse the function
|
||||
_properties.push_back({name, nullptr, func, nullptr, nullptr, nullptr, napi_default_jsproperty, nullptr});
|
||||
}
|
||||
|
||||
void Class::defineStaticFunction(const char* name, napi_callback func) {
|
||||
_properties.push_back({name, nullptr, func, nullptr, nullptr, 0, napi_static, 0});
|
||||
}
|
||||
|
||||
void Class::defineFinalizeFunction(napi_finalize func) {
|
||||
assert(func != nullptr);
|
||||
_finalizeFunc = func;
|
||||
}
|
||||
|
||||
napi_finalize Class::_getFinalizeFunction() const {
|
||||
return _finalizeFunc;
|
||||
}
|
||||
|
||||
void Class::install() {
|
||||
napi_value cons;
|
||||
napi_status status;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_define_class(ScriptEngine::getEnv(), _name.c_str(), -1, _ctorFunc, nullptr, _properties.size(), _properties.data(), &cons));
|
||||
if (_parentProto) {
|
||||
inherit(ScriptEngine::getEnv(), cons, _parentProto->_getJSObject());
|
||||
}
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(),
|
||||
napi_create_reference(ScriptEngine::getEnv(), cons, 1, &_constructor));
|
||||
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(),
|
||||
napi_set_named_property(ScriptEngine::getEnv(), _parent->_getJSObject(), _name.c_str(), cons));
|
||||
|
||||
napi_value proto;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_named_property(ScriptEngine::getEnv(), cons, "prototype", &proto));
|
||||
|
||||
if (status == napi_ok) {
|
||||
_proto = Object::_createJSObject(ScriptEngine::getEnv(), proto, nullptr);
|
||||
_proto->root();
|
||||
}
|
||||
}
|
||||
|
||||
napi_status Class::inherit(napi_env env, napi_value subclass, napi_value superProto) {
|
||||
napi_value global, objectClass, setProto;
|
||||
napi_value argv[2];
|
||||
napi_value callbackResult = nullptr;
|
||||
|
||||
napi_get_global(env, &global);
|
||||
napi_status status = napi_get_named_property(env, global, "Object", &objectClass);
|
||||
if (status != napi_ok) {
|
||||
LOGE("ace zbclog napi_get_named_property Object %{public}d", status);
|
||||
return napi_ok;
|
||||
}
|
||||
status = napi_get_named_property(env, objectClass, "setPrototypeOf", &setProto);
|
||||
if (status != napi_ok) {
|
||||
LOGE("ace zbclog napi_get_named_property setPrototypeOf %{public}d", status);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
status = napi_get_named_property(env, subclass, "prototype", &argv[0]);
|
||||
if (status != napi_ok) {
|
||||
LOGE("ace zbclog napi_get_named_property prototype arg0 %{public}d", status);
|
||||
return napi_ok;
|
||||
}
|
||||
argv[1] = superProto;
|
||||
status = napi_call_function(env, objectClass, setProto, 2, argv, &callbackResult);
|
||||
if (status != napi_ok) {
|
||||
LOGE("ace zbclog napi_call_function setProto 1 %{public}d", status);
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
napi_value Class::_createJSObjectWithClass(Class *cls) {
|
||||
napi_value obj = nullptr;
|
||||
napi_status status;
|
||||
assert(cls);
|
||||
napi_value clsCtor = cls->_getCtorFunc();
|
||||
if (!clsCtor) {
|
||||
LOGE("get ctor func err");
|
||||
return nullptr;
|
||||
}
|
||||
se::ScriptEngine::getInstance()->_setNeedCallConstructor(false);
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_new_instance( ScriptEngine::getEnv(), clsCtor, 0, nullptr, &obj));
|
||||
se::ScriptEngine::getInstance()->_setNeedCallConstructor(true);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object *Class::getProto() const {
|
||||
//not impl
|
||||
return _proto;
|
||||
}
|
||||
|
||||
napi_ref Class::_getCtorRef() const {
|
||||
return _constructor;
|
||||
}
|
||||
|
||||
napi_value Class::_getCtorFunc() const {
|
||||
assert(_constructor);
|
||||
napi_value result = nullptr;
|
||||
napi_status status;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_reference_value(ScriptEngine::getEnv(), _constructor, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void Class::cleanup() {
|
||||
for (auto cls : __allClasses) {
|
||||
cls->destroy();
|
||||
}
|
||||
|
||||
se::ScriptEngine::getInstance()->addAfterCleanupHook([]() {
|
||||
for (auto cls : __allClasses) {
|
||||
delete cls;
|
||||
}
|
||||
__allClasses.clear();
|
||||
});
|
||||
}
|
||||
}; // namespace se
|
||||
83
cocos/bindings/jswrapper/napi/Class.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "CommonHeader.h"
|
||||
#include "Object.h"
|
||||
#include "../Define.h"
|
||||
#include "base/std/optional.h"
|
||||
|
||||
namespace se {
|
||||
class Class {
|
||||
public:
|
||||
static Class *create(const std::string &clsName, se::Object *parent, Object *parentProto, napi_callback ctor = nullptr);
|
||||
static Class *create(const std::initializer_list<const char *> &classPath, se::Object *parent, Object *parentProto, napi_callback ctor = nullptr);
|
||||
|
||||
void defineFunction(const char* name, napi_callback func);
|
||||
void defineProperty(const char* name, napi_callback g, napi_callback s);
|
||||
void defineProperty(const std::initializer_list<const char *> &names, napi_callback g, napi_callback s);
|
||||
|
||||
void defineStaticFunction(const char* name, napi_callback func);
|
||||
void defineStaticProperty(const char* name, napi_callback g, napi_callback s);
|
||||
bool defineStaticProperty(const char *name, const Value &value, PropertyAttribute attribute = PropertyAttribute::NONE);
|
||||
|
||||
static napi_value _createJSObjectWithClass(Class *cls);
|
||||
|
||||
void defineFinalizeFunction(napi_finalize func);
|
||||
napi_finalize _getFinalizeFunction() const;
|
||||
|
||||
|
||||
Object * getProto() const;
|
||||
void install();
|
||||
napi_status inherit(napi_env env, napi_value subclass, napi_value superclass);
|
||||
napi_ref _getCtorRef() const;
|
||||
napi_value _getCtorFunc() const;
|
||||
const char * getName() const { return _name.c_str(); }
|
||||
static void setExports(napi_value *expPtr) { _exports = expPtr; }
|
||||
static void cleanup();
|
||||
// Private API used in wrapper
|
||||
void _setCtor(Object *obj); // NOLINT(readability-identifier-naming)
|
||||
inline const ccstd::optional<Object *> &_getCtor() const { return _ctor; } // NOLINT(readability-identifier-naming)
|
||||
private:
|
||||
Class();
|
||||
~Class();
|
||||
bool init(const std::string &clsName, Object *parent, Object *parentProto, napi_callback ctor = nullptr);
|
||||
void destroy();
|
||||
static napi_value _defaultCtor(napi_env env, napi_callback_info info);
|
||||
|
||||
private:
|
||||
ccstd::optional<Object *> _ctor;
|
||||
static napi_value * _exports;
|
||||
std::string _name;
|
||||
Object * _parent = nullptr;
|
||||
Object * _proto = nullptr;
|
||||
Object * _parentProto = nullptr;
|
||||
napi_callback _ctorFunc = Class::_defaultCtor;
|
||||
napi_ref _constructor = nullptr;
|
||||
std::vector<napi_property_descriptor> _properties;
|
||||
napi_finalize _finalizeFunc = nullptr;
|
||||
};
|
||||
}; // namespace se
|
||||
74
cocos/bindings/jswrapper/napi/CommonHeader.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <napi/native_api.h>
|
||||
#include "native_common.h"
|
||||
|
||||
// Empty value so that macros here are able to return NULL or void
|
||||
#define NODE_API_RETVAL_NOTHING // Intentionally blank #define
|
||||
|
||||
// Returns NULL on failed assertion.
|
||||
// This is meant to be used inside napi_callback methods.
|
||||
#define NODE_API_ASSERT(env, assertion, message) \
|
||||
NODE_API_ASSERT_BASE(env, assertion, message, NULL)
|
||||
|
||||
// Returns empty on failed assertion.
|
||||
// This is meant to be used inside functions with void return type.
|
||||
#define NODE_API_ASSERT_RETURN_VOID(env, assertion, message) \
|
||||
NODE_API_ASSERT_BASE(env, assertion, message, NODE_API_RETVAL_NOTHING)
|
||||
|
||||
#define NODE_API_CALL_BASE(env, the_call, ret_val) \
|
||||
do { \
|
||||
if ((the_call) != napi_ok) { \
|
||||
assert(false); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Returns nullptr if the_call doesn't return napi_ok.
|
||||
#define NODE_API_CALL(status, env, the_call) \
|
||||
status = the_call; \
|
||||
if (status != napi_ok) \
|
||||
LOGI("error:%d", status); \
|
||||
NODE_API_CALL_BASE(env, status, nullptr)
|
||||
|
||||
// Returns empty if the_call doesn't return napi_ok.
|
||||
#define NODE_API_CALL_RETURN_VOID(env, the_call) \
|
||||
NODE_API_CALL_BASE(env, the_call, NODE_API_RETVAL_NOTHING)
|
||||
|
||||
#define DECLARE_NODE_API_PROPERTY(name, func) \
|
||||
{ (name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr }
|
||||
|
||||
#define DECLARE_NODE_API_GETTER(name, func) \
|
||||
{ (name), nullptr, nullptr, (func), nullptr, nullptr, napi_default, nullptr }
|
||||
|
||||
void add_returned_status(napi_env env,
|
||||
const char* key,
|
||||
napi_value object,
|
||||
char* expected_message,
|
||||
napi_status expected_status,
|
||||
napi_status actual_status);
|
||||
|
||||
void add_last_status(napi_env env, const char* key, napi_value return_value);
|
||||
145
cocos/bindings/jswrapper/napi/HelperMacros.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 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"
|
||||
|
||||
SE_HOT napi_value jsbFunctionWrapper(napi_env env, napi_callback_info info, se_function_ptr func, const char *funcName) {
|
||||
napi_status status;
|
||||
bool ret = false;
|
||||
napi_value _this;
|
||||
se::ValueArray seArgs;
|
||||
seArgs.reserve(15);
|
||||
size_t argc = 15;
|
||||
napi_value args[15];
|
||||
NODE_API_CALL(status, env, napi_get_cb_info(env, info, &argc, args, &_this, NULL));
|
||||
se::Object *nativeThisObject = nullptr;
|
||||
status = napi_unwrap(env, _this, reinterpret_cast<void **>(&nativeThisObject));
|
||||
se::internal::jsToSeArgs(argc, args, &seArgs);
|
||||
se::State state(nativeThisObject, seArgs);
|
||||
ret = func(state);
|
||||
if (!ret) {
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", funcName, __FILE__, __LINE__);
|
||||
return nullptr;
|
||||
}
|
||||
napi_value retVal;
|
||||
if (se::internal::setReturnValue(state.rval(), retVal))
|
||||
return retVal;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SE_HOT void jsbFinalizeWrapper(void *thisObject, se_function_ptr func, const char *funcName) {
|
||||
se::State state(reinterpret_cast<se::Object *>(thisObject));
|
||||
bool ret = func(state);
|
||||
if (!ret) {
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", funcName, __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
SE_HOT napi_value jsbConstructorWrapper(napi_env env, napi_callback_info info, se_function_ptr func, se_finalize_ptr finalizeCb, se::Class *cls, const char *funcName) {
|
||||
napi_status status;
|
||||
bool ret = false;
|
||||
napi_value _this;
|
||||
se::ValueArray seArgs;
|
||||
seArgs.reserve(10);
|
||||
size_t argc = 10;
|
||||
napi_value args[10];
|
||||
NODE_API_CALL(status, env, napi_get_cb_info(env, info, &argc, args, &_this, NULL));
|
||||
if (!se::ScriptEngine::getInstance()->_needCallConstructor()) {
|
||||
return _this;
|
||||
}
|
||||
se::internal::jsToSeArgs(argc, args, &seArgs);
|
||||
se::Object *thisObject = se::Object::_createJSObject(env, _this, cls);
|
||||
thisObject->_setFinalizeCallback(finalizeCb);
|
||||
se::State state(thisObject, seArgs);
|
||||
ret = func(state);
|
||||
if (!ret) {
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", funcName, __FILE__, __LINE__);
|
||||
}
|
||||
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(seArgs, thisObject);
|
||||
}
|
||||
return _this;
|
||||
}
|
||||
|
||||
SE_HOT napi_value jsbGetterWrapper(napi_env env, napi_callback_info info, se_function_ptr func, const char *funcName) {
|
||||
napi_value _this;
|
||||
napi_status status;
|
||||
NODE_API_CALL(status, env,
|
||||
napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr));
|
||||
se::Object *obj;
|
||||
napi_unwrap(env, _this, reinterpret_cast<void **>(&obj));
|
||||
se::State state(obj);
|
||||
bool ret = func(state);
|
||||
if (!ret) {
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", funcName, __FILE__, __LINE__);
|
||||
return nullptr;
|
||||
}
|
||||
napi_value retVal;
|
||||
se::internal::setReturnValue(state.rval(), retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
SE_HOT napi_value jsbSetterWrapper(napi_env env, napi_callback_info info, se_function_ptr func, const char *funcName) {
|
||||
napi_status status;
|
||||
size_t argc = 1;
|
||||
napi_value args[1];
|
||||
napi_value _this;
|
||||
se::Value data;
|
||||
NODE_API_CALL(status, env, napi_get_cb_info(env, info, &argc, args, &_this, nullptr));
|
||||
se::internal::jsToSeValue(args[0], &data);
|
||||
se::ValueArray args2;
|
||||
args2.reserve(10);
|
||||
args2.push_back(std::move(data));
|
||||
se::Object *nativeThisObject;
|
||||
napi_unwrap(env, _this, reinterpret_cast<void **>(&nativeThisObject));
|
||||
se::State state(nativeThisObject, args2);
|
||||
bool ret = func(state);
|
||||
if (!ret) {
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", funcName, __FILE__, __LINE__);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
153
cocos/bindings/jswrapper/napi/HelperMacros.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CommonHeader.h"
|
||||
#if !defined(_WIN)
|
||||
#include <hilog/log.h>
|
||||
|
||||
#ifndef LOGI
|
||||
#define LOGI(...) ((void) OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "HMG_LOG", __VA_ARGS__))
|
||||
#define LOGW(...) ((void) OH_LOG_Print(LOG_APP, LOG_WARN, LOG_DOMAIN, "HMG_LOG", __VA_ARGS__))
|
||||
#define LOGE(...) ((void) OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, "HMG_LOG", __VA_ARGS__))
|
||||
#define LOGD(...) ((void) OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, "HMG_LOG", __VA_ARGS__))
|
||||
#endif
|
||||
#else
|
||||
#define LOGI
|
||||
#define LOGW
|
||||
#define LOGE
|
||||
#endif
|
||||
namespace se {
|
||||
class Class;
|
||||
class Object;
|
||||
class State;
|
||||
} // namespace se
|
||||
using se_function_ptr = bool (*)(se::State &state);
|
||||
using se_finalize_ptr = void (*)(napi_env env, void *nativeObject, void *hint);
|
||||
|
||||
napi_value jsbFunctionWrapper(napi_env, napi_callback_info,
|
||||
se_function_ptr,
|
||||
const char *);
|
||||
void jsbFinalizeWrapper(void *thisObject,
|
||||
se_function_ptr,
|
||||
const char *);
|
||||
napi_value jsbConstructorWrapper(napi_env, napi_callback_info,
|
||||
se_function_ptr,
|
||||
se_finalize_ptr finalizeCb,
|
||||
se::Class *,
|
||||
const char *);
|
||||
napi_value jsbGetterWrapper(napi_env, napi_callback_info,
|
||||
se_function_ptr,
|
||||
const char *);
|
||||
napi_value jsbSetterWrapper(napi_env, napi_callback_info,
|
||||
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
|
||||
|
||||
template <typename T, typename STATE>
|
||||
constexpr inline T *SE_THIS_OBJECT(STATE &s) { // NOLINT(readability-identifier-naming)
|
||||
return reinterpret_cast<T *>(s.nativeThisObject());
|
||||
}
|
||||
|
||||
#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_QUOTEME_(x) #x // NOLINT(readability-identifier-naming)
|
||||
#define SE_QUOTEME(x) SE_QUOTEME_(x)
|
||||
#define SE_REPORT_ERROR(fmt, ...) SE_LOGE("[ERROR] (" __FILE__ ", " SE_QUOTEME(__LINE__) "): " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#define SE_BIND_PROP_GET_IMPL(funcName, postFix) \
|
||||
napi_value funcName##postFix##Registry(napi_env env, napi_callback_info info) { \
|
||||
return jsbGetterWrapper(env, info, 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) \
|
||||
napi_value funcName##postFix##Registry(napi_env env, napi_callback_info info) { \
|
||||
return jsbSetterWrapper(env, info, 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_DECLARE_FUNC(funcName) \
|
||||
napi_value funcName##Registry(napi_env env, napi_callback_info info)
|
||||
|
||||
#define SE_BIND_FUNC(funcName) \
|
||||
napi_value funcName##Registry( \
|
||||
napi_env env, napi_callback_info info) { \
|
||||
return jsbFunctionWrapper(env, info, funcName, #funcName); \
|
||||
}
|
||||
|
||||
#define SE_BIND_FUNC_FAST(funcName) \
|
||||
napi_value funcName##Registry(napi_env env, napi_callback_info info) { \
|
||||
napi_status status; \
|
||||
napi_value _this; \
|
||||
size_t argc = 10; \
|
||||
napi_value args[10]; \
|
||||
se::Object *nativeThisObject = nullptr; \
|
||||
NODE_API_CALL(status, env, napi_get_cb_info(env, info, &argc, args, &_this, NULL)); \
|
||||
status = napi_unwrap(env, _this, reinterpret_cast<void **>(&nativeThisObject)); \
|
||||
auto *nativeObject = nativeThisObject != nullptr ? nativeThisObject->getPrivateData() : nullptr; \
|
||||
funcName(nativeObject); \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
#define SE_BIND_CTOR(funcName, cls, finalizeCb) \
|
||||
napi_value funcName##Registry( \
|
||||
napi_env env, napi_callback_info info) { \
|
||||
return jsbConstructorWrapper(env, info, funcName, _SE(finalizeCb), cls, #funcName); \
|
||||
}
|
||||
|
||||
#define SE_BIND_SUB_CLS_CTOR SE_BIND_CTOR
|
||||
|
||||
#define SE_DECLARE_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry( \
|
||||
napi_env env, void *nativeObject, void * /*finalize_hint*/);
|
||||
|
||||
#define SE_BIND_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry( \
|
||||
napi_env env, void *nativeObject, void *hint /*finalize_hint*/) { \
|
||||
if (nativeObject == nullptr) { \
|
||||
return; \
|
||||
} \
|
||||
jsbFinalizeWrapper(nativeObject, funcName, #funcName); \
|
||||
}
|
||||
|
||||
744
cocos/bindings/jswrapper/napi/Object.cpp
Normal file
@@ -0,0 +1,744 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "Object.h"
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include "../MappingUtils.h"
|
||||
#include "Class.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#define MAX_STRING_LEN 512
|
||||
namespace se {
|
||||
std::unique_ptr<std::unordered_map<Object*, void*>> __objectMap; // Currently, the value `void*` is always nullptr
|
||||
|
||||
Object::Object() {}
|
||||
Object::~Object() {
|
||||
if (__objectMap) {
|
||||
__objectMap->erase(this);
|
||||
}
|
||||
}
|
||||
|
||||
Object* Object::createObjectWithClass(Class* cls) {
|
||||
napi_value jsobj = Class::_createJSObjectWithClass(cls);
|
||||
Object* obj = Object::_createJSObject(ScriptEngine::getEnv(), jsobj, cls);
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool Object::setProperty(const char* name, const Value& data) {
|
||||
napi_status status;
|
||||
napi_value jsVal;
|
||||
internal::seToJsValue(data, &jsVal);
|
||||
NODE_API_CALL(status, _env, napi_set_named_property(_env, _objRef.getValue(_env), name, jsVal));
|
||||
return status == napi_ok;
|
||||
}
|
||||
|
||||
bool Object::getProperty(const char* name, Value* d) {
|
||||
napi_status status;
|
||||
napi_value jsVal;
|
||||
Value data;
|
||||
NODE_API_CALL(status, _env, napi_get_named_property(_env, _objRef.getValue(_env), name, &jsVal));
|
||||
if (status == napi_ok) {
|
||||
internal::jsToSeValue(jsVal, &data);
|
||||
*d = data;
|
||||
if (data.isUndefined()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::isArray() const {
|
||||
napi_status status;
|
||||
bool ret = false;
|
||||
NODE_API_CALL(status, _env, napi_is_array(_env, _objRef.getValue(_env), &ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Object::getArrayLength(uint32_t* length) const {
|
||||
napi_status status;
|
||||
uint32_t len = 0;
|
||||
NODE_API_CALL(status, _env, napi_get_array_length(_env, _objRef.getValue(_env), &len));
|
||||
if (length) {
|
||||
*length = len;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::getArrayElement(uint32_t index, Value* data) const {
|
||||
napi_status status;
|
||||
napi_value val;
|
||||
NODE_API_CALL(status, _env, napi_get_element(_env, _objRef.getValue(_env), index, &val));
|
||||
internal::jsToSeValue(val, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::setArrayElement(uint32_t index, const Value& data) {
|
||||
napi_status status;
|
||||
napi_value val;
|
||||
internal::seToJsValue(data, &val);
|
||||
NODE_API_CALL(status, _env, napi_set_element(_env, _objRef.getValue(_env), index, val));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::isTypedArray() const {
|
||||
napi_status status;
|
||||
bool ret = false;
|
||||
NODE_API_CALL(status, _env, napi_is_typedarray(_env, _objRef.getValue(_env), &ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Object::isProxy() const {
|
||||
//return const_cast<Object *>(this)->_obj.handle(__isolate)->IsProxy();
|
||||
// todo:
|
||||
return false;
|
||||
}
|
||||
|
||||
Object *Object::createProxyTarget(se::Object *proxy) {
|
||||
// SE_ASSERT(proxy->isProxy(), "parameter is not a Proxy object");
|
||||
// v8::Local<v8::Object> jsobj = proxy->getProxyTarget().As<v8::Object>();
|
||||
// Object *obj = Object::_createJSObject(nullptr, jsobj);
|
||||
// return obj;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Object::TypedArrayType Object::getTypedArrayType() const {
|
||||
napi_status status;
|
||||
napi_typedarray_type type;
|
||||
napi_value inputBuffer;
|
||||
size_t byteOffset;
|
||||
size_t length;
|
||||
NODE_API_CALL(status, _env, napi_get_typedarray_info(_env, _objRef.getValue(_env), &type, &length, NULL, &inputBuffer, &byteOffset));
|
||||
|
||||
TypedArrayType ret = TypedArrayType::NONE;
|
||||
switch (type) {
|
||||
case napi_int8_array:
|
||||
ret = TypedArrayType::INT8;
|
||||
break;
|
||||
case napi_uint8_array:
|
||||
ret = TypedArrayType::UINT8;
|
||||
break;
|
||||
case napi_uint8_clamped_array:
|
||||
ret = TypedArrayType::UINT8_CLAMPED;
|
||||
break;
|
||||
case napi_int16_array:
|
||||
ret = TypedArrayType::INT16;
|
||||
break;
|
||||
case napi_uint16_array:
|
||||
ret = TypedArrayType::UINT16;
|
||||
break;
|
||||
case napi_int32_array:
|
||||
ret = TypedArrayType::INT32;
|
||||
break;
|
||||
case napi_uint32_array:
|
||||
ret = TypedArrayType::UINT32;
|
||||
break;
|
||||
case napi_float32_array:
|
||||
ret = TypedArrayType::FLOAT32;
|
||||
break;
|
||||
case napi_float64_array:
|
||||
ret = TypedArrayType::FLOAT64;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Object::getTypedArrayData(uint8_t** ptr, size_t* length) const {
|
||||
napi_status status;
|
||||
napi_typedarray_type type;
|
||||
napi_value inputBuffer;
|
||||
size_t byteOffset;
|
||||
size_t byteLength;
|
||||
void* data = nullptr;
|
||||
NODE_API_CALL(status, _env, napi_get_typedarray_info(_env, _objRef.getValue(_env), &type, &byteLength, &data, &inputBuffer, &byteOffset));
|
||||
*ptr = (uint8_t*)(data);
|
||||
if (length) {
|
||||
*length = byteLength;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::isArrayBuffer() const {
|
||||
bool ret = false;
|
||||
napi_status status;
|
||||
NODE_API_CALL(status, _env, napi_is_arraybuffer(_env, _objRef.getValue(_env), &ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Object::getArrayBufferData(uint8_t** ptr, size_t* length) const {
|
||||
napi_status status;
|
||||
size_t len = 0;
|
||||
NODE_API_CALL(status, _env, napi_get_arraybuffer_info(_env, _objRef.getValue(_env), reinterpret_cast<void**>(ptr), &len));
|
||||
if (length) {
|
||||
*length = len;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Object* Object::createTypedArray(Object::TypedArrayType type, const void* data, size_t byteLength) {
|
||||
napi_status status;
|
||||
if (type == TypedArrayType::NONE) {
|
||||
SE_LOGE("Don't pass se::Object::TypedArrayType::NONE to createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type == TypedArrayType::UINT8_CLAMPED) {
|
||||
SE_LOGE("Doesn't support to create Uint8ClampedArray with Object::createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
napi_typedarray_type napiType;
|
||||
napi_value outputBuffer;
|
||||
void* outputPtr = nullptr;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_arraybuffer(ScriptEngine::getEnv(), byteLength, &outputPtr, &outputBuffer));
|
||||
if (outputPtr && data && byteLength > 0) {
|
||||
memcpy(outputPtr, data, byteLength);
|
||||
}
|
||||
size_t sizeOfEle = 0;
|
||||
switch (type) {
|
||||
case TypedArrayType::INT8:
|
||||
napiType = napi_int8_array;
|
||||
sizeOfEle = 1;
|
||||
break;
|
||||
case TypedArrayType::UINT8:
|
||||
napiType = napi_uint8_array;
|
||||
sizeOfEle = 1;
|
||||
break;
|
||||
case TypedArrayType::INT16:
|
||||
napiType = napi_int16_array;
|
||||
sizeOfEle = 2;
|
||||
break;
|
||||
case TypedArrayType::UINT16:
|
||||
napiType = napi_uint16_array;
|
||||
sizeOfEle = 2;
|
||||
break;
|
||||
case TypedArrayType::INT32:
|
||||
napiType = napi_int32_array;
|
||||
sizeOfEle = 4;
|
||||
break;
|
||||
case TypedArrayType::UINT32:
|
||||
napiType = napi_uint32_array;
|
||||
sizeOfEle = 4;
|
||||
break;
|
||||
case TypedArrayType::FLOAT32:
|
||||
napiType = napi_float32_array;
|
||||
sizeOfEle = 4;
|
||||
break;
|
||||
case TypedArrayType::FLOAT64:
|
||||
napiType = napi_float64_array;
|
||||
sizeOfEle = 8;
|
||||
break;
|
||||
default:
|
||||
assert(false); // Should never go here.
|
||||
break;
|
||||
}
|
||||
size_t eleCounts = byteLength / sizeOfEle;
|
||||
napi_value outputArray;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_typedarray(ScriptEngine::getEnv(), napiType, eleCounts, outputBuffer, 0, &outputArray));
|
||||
|
||||
Object* obj = Object::_createJSObject(ScriptEngine::getEnv(), outputArray, nullptr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createTypedArrayWithBuffer(TypedArrayType type, const Object *obj) {
|
||||
return Object::createTypedArrayWithBuffer(type, obj, 0);
|
||||
}
|
||||
|
||||
Object* Object::createTypedArrayWithBuffer(TypedArrayType type, const Object *obj, size_t offset) {
|
||||
size_t byteLength{0};
|
||||
uint8_t *skip{nullptr};
|
||||
|
||||
if (obj->getArrayBufferData(&skip, &byteLength)) {
|
||||
return Object::createTypedArrayWithBuffer(type, obj, offset, byteLength - offset);
|
||||
}
|
||||
|
||||
CC_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Object* Object::createTypedArrayWithBuffer(TypedArrayType type, const Object *obj, size_t offset, size_t byteLength) {
|
||||
if (type == TypedArrayType::NONE) {
|
||||
SE_LOGE("Don't pass se::Object::TypedArrayType::NONE to createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type == TypedArrayType::UINT8_CLAMPED) {
|
||||
SE_LOGE("Doesn't support to create Uint8ClampedArray with Object::createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CC_ASSERT(obj->isArrayBuffer());
|
||||
napi_status status;
|
||||
napi_value outputBuffer = obj->_getJSObject();
|
||||
napi_typedarray_type napiType;
|
||||
|
||||
size_t sizeOfEle = 0;
|
||||
switch (type) {
|
||||
case TypedArrayType::INT8:
|
||||
napiType = napi_int8_array;
|
||||
sizeOfEle = 1;
|
||||
break;
|
||||
case TypedArrayType::UINT8:
|
||||
napiType = napi_uint8_array;
|
||||
sizeOfEle = 1;
|
||||
break;
|
||||
case TypedArrayType::INT16:
|
||||
napiType = napi_int16_array;
|
||||
sizeOfEle = 2;
|
||||
break;
|
||||
case TypedArrayType::UINT16:
|
||||
napiType = napi_uint16_array;
|
||||
sizeOfEle = 2;
|
||||
break;
|
||||
case TypedArrayType::INT32:
|
||||
napiType = napi_int32_array;
|
||||
sizeOfEle = 4;
|
||||
break;
|
||||
case TypedArrayType::UINT32:
|
||||
napiType = napi_uint32_array;
|
||||
sizeOfEle = 4;
|
||||
break;
|
||||
case TypedArrayType::FLOAT32:
|
||||
napiType = napi_float32_array;
|
||||
sizeOfEle = 4;
|
||||
break;
|
||||
case TypedArrayType::FLOAT64:
|
||||
napiType = napi_float64_array;
|
||||
sizeOfEle = 8;
|
||||
break;
|
||||
default:
|
||||
assert(false); // Should never go here.
|
||||
break;
|
||||
}
|
||||
size_t eleCounts = byteLength / sizeOfEle;
|
||||
napi_value outputArray;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_typedarray(ScriptEngine::getEnv(), napiType, eleCounts, outputBuffer, offset, &outputArray));
|
||||
|
||||
return Object::_createJSObject(ScriptEngine::getEnv(), outputArray, nullptr);
|
||||
}
|
||||
|
||||
Object* Object::createExternalArrayBufferObject(void *contents, size_t byteLength, BufferContentsFreeFunc freeFunc, void *freeUserData) {
|
||||
napi_status status;
|
||||
napi_value result;
|
||||
if (freeFunc) {
|
||||
struct ExternalArrayBufferCallbackParams* param = new (struct ExternalArrayBufferCallbackParams);
|
||||
param->func = freeFunc;
|
||||
param->contents = contents;
|
||||
param->byteLength = byteLength;
|
||||
param->userData = freeUserData;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_external_arraybuffer(
|
||||
ScriptEngine::getEnv(), contents, byteLength, [](napi_env env, void* finalize_data, void* finalize_hint) {
|
||||
if (finalize_hint) {
|
||||
struct ExternalArrayBufferCallbackParams* param = reinterpret_cast<struct ExternalArrayBufferCallbackParams *>(finalize_hint);
|
||||
param->func(param->contents, param->byteLength, param->userData);
|
||||
delete param;
|
||||
}
|
||||
},
|
||||
reinterpret_cast<void*>(param), &result));
|
||||
} else {
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_external_arraybuffer(
|
||||
ScriptEngine::getEnv(), contents, byteLength, nullptr,
|
||||
freeUserData, &result));
|
||||
}
|
||||
|
||||
Object* obj = Object::_createJSObject(ScriptEngine::getEnv(), result, nullptr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool Object::isFunction() const {
|
||||
napi_valuetype valuetype0;
|
||||
napi_status status;
|
||||
NODE_API_CALL(status, _env, napi_typeof(_env, _objRef.getValue(_env), &valuetype0));
|
||||
return (valuetype0 == napi_function);
|
||||
}
|
||||
|
||||
bool Object::defineFunction(const char* funcName, napi_callback func) {
|
||||
napi_value fn;
|
||||
napi_status status;
|
||||
NODE_API_CALL(status, _env, napi_create_function(_env, funcName, NAPI_AUTO_LENGTH, func, NULL, &fn));
|
||||
NODE_API_CALL(status, _env, napi_set_named_property(_env, _objRef.getValue(_env), funcName, fn));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::defineProperty(const char* name, napi_callback getter, napi_callback setter) {
|
||||
napi_status status;
|
||||
napi_property_descriptor properties[] = {{name, nullptr, nullptr, getter, setter, 0, napi_default, 0}};
|
||||
status = napi_define_properties(_env, _objRef.getValue(_env), sizeof(properties) / sizeof(napi_property_descriptor), properties);
|
||||
if (status == napi_ok) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Object* Object::_createJSObject(napi_env env, napi_value js_object, Class* cls) { // NOLINT(readability-identifier-naming)
|
||||
auto* ret = new Object();
|
||||
if (!ret->init(env, js_object, cls)) {
|
||||
delete ret;
|
||||
ret = nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Object* Object::createPlainObject() {
|
||||
napi_value result;
|
||||
napi_status status;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_object(ScriptEngine::getEnv(), &result));
|
||||
Object* obj = _createJSObject(ScriptEngine::getEnv(), result, nullptr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createArrayObject(size_t length) {
|
||||
napi_value result;
|
||||
napi_status status;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_array_with_length(ScriptEngine::getEnv(), length, &result));
|
||||
Object* obj = _createJSObject(ScriptEngine::getEnv(), result, nullptr);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object* Object::createArrayBufferObject(const void* data, size_t byteLength) {
|
||||
napi_value result;
|
||||
napi_status status;
|
||||
void* retData;
|
||||
Object* obj = nullptr;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_arraybuffer(ScriptEngine::getEnv(), byteLength, &retData, &result));
|
||||
if (status == napi_ok) {
|
||||
if (data) {
|
||||
memcpy(retData, data, byteLength);
|
||||
}
|
||||
obj = _createJSObject(ScriptEngine::getEnv(), result, nullptr);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool Object::getAllKeys(std::vector<std::string>* allKeys) const {
|
||||
napi_status status;
|
||||
napi_value names;
|
||||
|
||||
NODE_API_CALL(status, _env, napi_get_property_names(_env, _objRef.getValue(_env), &names));
|
||||
if (status != napi_ok) {
|
||||
return false;
|
||||
}
|
||||
uint32_t name_len = 0;
|
||||
NODE_API_CALL(status, _env, napi_get_array_length(_env, names, &name_len));
|
||||
for (uint32_t i = 0; i < name_len; i++) {
|
||||
napi_value val;
|
||||
NODE_API_CALL(status, _env, napi_get_element(_env, names, i, &val));
|
||||
if (status == napi_ok) {
|
||||
char buffer[MAX_STRING_LEN];
|
||||
size_t result = 0;
|
||||
NODE_API_CALL(status, _env, napi_get_value_string_utf8(_env, val, buffer, MAX_STRING_LEN, &result));
|
||||
if (result > 0) {
|
||||
allKeys->push_back(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::init(napi_env env, napi_value js_object, Class* cls) {
|
||||
assert(env);
|
||||
_cls = cls;
|
||||
_env = env;
|
||||
_objRef.initWeakref(env, js_object);
|
||||
|
||||
if (__objectMap) {
|
||||
assert(__objectMap->find(this) == __objectMap->end());
|
||||
__objectMap->emplace(this, nullptr);
|
||||
}
|
||||
|
||||
napi_status status;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::call(const ValueArray& args, Object* thisObject, Value* rval) {
|
||||
size_t argc = 0;
|
||||
std::vector<napi_value> argv;
|
||||
argv.reserve(10);
|
||||
argc = args.size();
|
||||
internal::seToJsArgs(_env, args, &argv);
|
||||
napi_value return_val;
|
||||
napi_status status;
|
||||
assert(isFunction());
|
||||
napi_value thisObj = thisObject ? thisObject->_getJSObject() : nullptr;
|
||||
status =
|
||||
napi_call_function(_env, thisObj, _getJSObject(), argc, argv.data(), &return_val);
|
||||
if (rval) {
|
||||
internal::jsToSeValue(return_val, rval);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Object::_setFinalizeCallback(napi_finalize finalizeCb) {
|
||||
assert(finalizeCb != nullptr);
|
||||
_finalizeCb = finalizeCb;
|
||||
}
|
||||
|
||||
PrivateObjectBase* Object::getPrivateObject() const {
|
||||
return _privateObject;
|
||||
}
|
||||
|
||||
void Object::setPrivateObject(PrivateObjectBase* data) {
|
||||
assert(_privateData == nullptr);
|
||||
#if CC_DEBUG
|
||||
if (data != nullptr) {
|
||||
NativePtrToObjectMap::filter(data->getRaw(), _getClass())
|
||||
.forEach([&](se::Object *seObj) {
|
||||
auto *pri = seObj->getPrivateObject();
|
||||
SE_LOGE("Already exists object %s/[%s], trying to add %s/[%s]\n", pri->getName(), typeid(*pri).name(), data->getName(), typeid(*data).name());
|
||||
#if JSB_TRACK_OBJECT_CREATION
|
||||
SE_LOGE(" previous object created at %s\n", it->second->_objectCreationStackFrame.c_str());
|
||||
#endif
|
||||
CC_ABORT();
|
||||
});
|
||||
}
|
||||
#endif
|
||||
napi_status status;
|
||||
if (data) {
|
||||
_privateData = data->getRaw();
|
||||
_privateObject = data;
|
||||
NativePtrToObjectMap::emplace(_privateData, this);
|
||||
}
|
||||
|
||||
//issue https://github.com/nodejs/node/issues/23999
|
||||
auto tmpThis = _objRef.getValue(_env);
|
||||
//_objRef.deleteRef();
|
||||
napi_ref result = nullptr;
|
||||
NODE_API_CALL(status, _env,
|
||||
napi_wrap(_env, tmpThis, this, weakCallback,
|
||||
(void*)this /* finalize_hint */, &result));
|
||||
//_objRef.setWeakref(_env, result);
|
||||
setProperty("__native_ptr__", se::Value(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(data))));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool Object::attachObject(Object* obj) {
|
||||
assert(obj);
|
||||
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal)) {
|
||||
return false;
|
||||
}
|
||||
Object* jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("registerNativeRef", &func)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::detachObject(Object* obj) {
|
||||
assert(obj);
|
||||
|
||||
Object* global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal)) {
|
||||
return false;
|
||||
}
|
||||
Object* jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("unregisterNativeRef", &func)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Object::toString() const {
|
||||
std::string ret;
|
||||
napi_status status;
|
||||
if (isFunction() || isArray() || isTypedArray()) {
|
||||
napi_value result;
|
||||
NODE_API_CALL(status, _env, napi_coerce_to_string(_env, _objRef.getValue(_env), &result));
|
||||
char buffer[MAX_STRING_LEN];
|
||||
size_t result_t = 0;
|
||||
NODE_API_CALL(status, _env, napi_get_value_string_utf8(_env, result, buffer, MAX_STRING_LEN, &result_t));
|
||||
ret = buffer;
|
||||
} else if (isArrayBuffer()) {
|
||||
ret = "[object ArrayBuffer]";
|
||||
} else {
|
||||
ret = "[object Object]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Object::root() {
|
||||
napi_status status;
|
||||
if (_rootCount == 0) {
|
||||
uint32_t result = 0;
|
||||
_objRef.incRef(_env);
|
||||
//NODE_API_CALL(status, _env, napi_reference_ref(_env, _wrapper, &result));
|
||||
}
|
||||
++_rootCount;
|
||||
}
|
||||
|
||||
void Object::unroot() {
|
||||
napi_status status;
|
||||
if (_rootCount > 0) {
|
||||
--_rootCount;
|
||||
if (_rootCount == 0) {
|
||||
_objRef.decRef(_env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Object::isRooted() const {
|
||||
return _rootCount > 0;
|
||||
}
|
||||
|
||||
Class* Object::_getClass() const {
|
||||
return _cls;
|
||||
}
|
||||
|
||||
Object* Object::getObjectWithPtr(void* ptr) {
|
||||
Object* obj = nullptr;
|
||||
auto iter = NativePtrToObjectMap::find(ptr);
|
||||
if (iter != NativePtrToObjectMap::end()) {
|
||||
obj = iter->second;
|
||||
obj->incRef();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
napi_value Object::_getJSObject() const {
|
||||
return _objRef.getValue(_env);
|
||||
}
|
||||
|
||||
void Object::weakCallback(napi_env env, void* nativeObject, void* finalizeHint /*finalize_hint*/) {
|
||||
if (finalizeHint) {
|
||||
if (nativeObject == nullptr) {
|
||||
return;
|
||||
}
|
||||
void *rawPtr = reinterpret_cast<Object*>(nativeObject)->_privateData;
|
||||
Object* seObj = reinterpret_cast<Object*>(nativeObject);
|
||||
if (seObj->_onCleaingPrivateData) { //called by cleanPrivateData, not release seObj;
|
||||
return;
|
||||
}
|
||||
if (seObj->_clearMappingInFinalizer && rawPtr != nullptr) {
|
||||
auto iter = NativePtrToObjectMap::find(rawPtr);
|
||||
if (iter != NativePtrToObjectMap::end()) {
|
||||
NativePtrToObjectMap::erase(iter);
|
||||
} else {
|
||||
CC_LOG_INFO("not find ptr in NativePtrToObjectMap");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove test code before releasing.
|
||||
const char* clsName = seObj->_getClass()->getName();
|
||||
CC_LOG_INFO("weakCallback class name:%s, ptr:%p", clsName, rawPtr);
|
||||
|
||||
if (seObj->_finalizeCb != nullptr) {
|
||||
seObj->_finalizeCb(env, nativeObject, finalizeHint);
|
||||
} else {
|
||||
assert(seObj->_getClass() != nullptr);
|
||||
if (seObj->_getClass()->_getFinalizeFunction() != nullptr) {
|
||||
seObj->_getClass()->_getFinalizeFunction()(env, nativeObject, finalizeHint);
|
||||
}
|
||||
}
|
||||
seObj->decRef();
|
||||
}
|
||||
}
|
||||
|
||||
void Object::setup() {
|
||||
__objectMap = std::make_unique<std::unordered_map<Object*, void*>>();
|
||||
}
|
||||
|
||||
void Object::cleanup() {
|
||||
void* nativeObj = nullptr;
|
||||
Object* obj = nullptr;
|
||||
Class* cls = nullptr;
|
||||
|
||||
const auto& nativePtrToObjectMap = NativePtrToObjectMap::instance();
|
||||
for (const auto& e : nativePtrToObjectMap) {
|
||||
nativeObj = e.first;
|
||||
obj = e.second;
|
||||
|
||||
if (obj->_finalizeCb != nullptr) {
|
||||
obj->_finalizeCb(ScriptEngine::getEnv(), nativeObj, nullptr);
|
||||
} else {
|
||||
if (obj->_getClass() != nullptr) {
|
||||
if (obj->_getClass()->_getFinalizeFunction() != nullptr) {
|
||||
obj->_getClass()->_getFinalizeFunction()(ScriptEngine::getEnv(), nativeObj, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
obj->decRef();
|
||||
}
|
||||
|
||||
NativePtrToObjectMap::clear();
|
||||
|
||||
if (__objectMap) {
|
||||
for (const auto& e : *__objectMap) {
|
||||
obj = e.first;
|
||||
cls = obj->_getClass();
|
||||
obj->_rootCount = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
__objectMap.reset();
|
||||
}
|
||||
|
||||
Object* Object::createJSONObject(const std::string& jsonStr) {
|
||||
//not impl
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Object::clearPrivateData(bool clearMapping) {
|
||||
if (_privateObject != nullptr) {
|
||||
napi_status status;
|
||||
void* result = nullptr;
|
||||
auto tmpThis = _objRef.getValue(_env);
|
||||
_onCleaingPrivateData = true;
|
||||
if (clearMapping) {
|
||||
NativePtrToObjectMap::erase(_privateData);
|
||||
}
|
||||
NODE_API_CALL(status, _env, napi_remove_wrap(_env, tmpThis, &result));
|
||||
delete _privateObject;
|
||||
_privateObject = nullptr;
|
||||
_privateData = nullptr;
|
||||
_onCleaingPrivateData = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
493
cocos/bindings/jswrapper/napi/Object.h
Normal file
@@ -0,0 +1,493 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <cassert>
|
||||
#include "../RefCounter.h"
|
||||
#include "../Value.h"
|
||||
#include "../config.h"
|
||||
#include "CommonHeader.h"
|
||||
#include "HelperMacros.h"
|
||||
#include "../PrivateObject.h"
|
||||
#include "base/std/container/unordered_map.h"
|
||||
|
||||
namespace se {
|
||||
class Class;
|
||||
class ObjectRef {
|
||||
private:
|
||||
napi_ref _ref = nullptr;
|
||||
int _refCounts = 0;
|
||||
napi_env _env = nullptr;
|
||||
napi_value _obj = nullptr;
|
||||
|
||||
public:
|
||||
~ObjectRef() {
|
||||
deleteRef();
|
||||
}
|
||||
napi_value getValue(napi_env env) const {
|
||||
napi_value result;
|
||||
napi_status status;
|
||||
NODE_API_CALL(status, env, napi_get_reference_value(env, _ref, &result));
|
||||
assert(status == napi_ok);
|
||||
assert(result != nullptr);
|
||||
return result;
|
||||
}
|
||||
void initWeakref(napi_env env, napi_value obj) {
|
||||
assert(_ref == nullptr);
|
||||
_obj = obj;
|
||||
_env = env;
|
||||
napi_create_reference(env, obj, 0, &_ref);
|
||||
}
|
||||
void setWeakref(napi_env env, napi_ref ref) {
|
||||
assert(_ref == nullptr);
|
||||
_ref = ref;
|
||||
}
|
||||
void initStrongRef(napi_env env, napi_value obj) {
|
||||
assert(_ref == nullptr);
|
||||
_refCounts = 1;
|
||||
_obj = obj;
|
||||
napi_create_reference(env, obj, _refCounts, &_ref);
|
||||
_env = env;
|
||||
}
|
||||
void incRef(napi_env env) {
|
||||
assert(_refCounts == 0);
|
||||
if (_refCounts == 0) {
|
||||
uint32_t result = 0;
|
||||
_refCounts = 1;
|
||||
napi_reference_ref(env, _ref, &result);
|
||||
}
|
||||
}
|
||||
void decRef(napi_env env) {
|
||||
assert(_refCounts == 1);
|
||||
uint32_t result = 0;
|
||||
if (_refCounts > 0) {
|
||||
_refCounts--;
|
||||
if (_refCounts == 0) {
|
||||
napi_reference_unref(env, _ref, &result);
|
||||
}
|
||||
}
|
||||
}
|
||||
void deleteRef() {
|
||||
_refCounts = 0;
|
||||
if (!_ref) {
|
||||
return;
|
||||
}
|
||||
napi_delete_reference(_env, _ref);
|
||||
_ref = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class Object;
|
||||
|
||||
class Object : public RefCounter {
|
||||
public:
|
||||
enum class TypedArrayType {
|
||||
NONE,
|
||||
INT8,
|
||||
INT16,
|
||||
INT32,
|
||||
UINT8,
|
||||
UINT8_CLAMPED,
|
||||
UINT16,
|
||||
UINT32,
|
||||
FLOAT32,
|
||||
FLOAT64
|
||||
};
|
||||
|
||||
using BufferContentsFreeFunc = void (*)(void *contents, size_t byteLength, void *userData);
|
||||
|
||||
struct ExternalArrayBufferCallbackParams {
|
||||
BufferContentsFreeFunc func{nullptr};
|
||||
void *contents{nullptr};
|
||||
size_t byteLength{0};
|
||||
void *userData{0};
|
||||
};
|
||||
|
||||
Object();
|
||||
~Object();
|
||||
/**
|
||||
* @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 std::string &name, const Value &value) {
|
||||
return setProperty(name.c_str(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a property from an object.
|
||||
* @param[in] name A utf-8 string containing the property's name.
|
||||
* @param[out] value The property's value if object has the property, otherwise the undefined value.
|
||||
* @return true if object has the property, otherwise false.
|
||||
*/
|
||||
bool getProperty(const char *name, Value *data);
|
||||
|
||||
inline bool getProperty(const char *name, Value *data, bool cachePropertyName) {
|
||||
return getProperty(name, data);
|
||||
}
|
||||
|
||||
inline bool getProperty(const std::string &name, Value *value) {
|
||||
return getProperty(name.c_str(), value);
|
||||
}
|
||||
|
||||
void setPrivateObject(PrivateObjectBase *data);
|
||||
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.
|
||||
* @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 Sets a pointer to private data on an object.
|
||||
* @param[in] data A void* to set as the object's private data.
|
||||
* @note This method will associate private data with se::Object by ccstd::unordered_map::emplace.
|
||||
* It's used for search a se::Object via a void* private data.
|
||||
*/
|
||||
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 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 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);
|
||||
|
||||
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);
|
||||
|
||||
static Object *createExternalArrayBufferObject(void *contents, size_t byteLength, BufferContentsFreeFunc freeFunc, void *freeUserData = nullptr);
|
||||
/**
|
||||
* @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 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, napi_callback func);
|
||||
|
||||
/**
|
||||
* @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, napi_callback getter, napi_callback setter);
|
||||
|
||||
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 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 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);
|
||||
|
||||
static Object *_createJSObject(napi_env env, napi_value js_object, Class *cls);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Object like `{} or new Object()`.
|
||||
* @return A JavaScript Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object *createPlainObject();
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Array Object like `[] or new Array()`.
|
||||
* @param[in] length The initical length of array.
|
||||
* @return A JavaScript Array Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object *createArrayObject(size_t length);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript 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);
|
||||
/**
|
||||
* 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 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 Creates a JavaScript Object from a JSON formatted string.
|
||||
* @param[in] jsonStr The utf-8 string containing the JSON string to be parsed.
|
||||
* @return A JavaScript Object containing the parsed value, or nullptr if the input is invalid.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object *createJSONObject(const std::string &jsonStr);
|
||||
|
||||
/**
|
||||
* @brief Gets all property names of an object.
|
||||
* @param[out] allKeys A string vector to store all property names.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getAllKeys(std::vector<std::string> *allKeys) const;
|
||||
|
||||
/**
|
||||
* @brief Gets a se::Object from an existing native object pointer.
|
||||
* @param[in] ptr The native object pointer associated with the se::Object
|
||||
* @return A JavaScript Native Binding Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object *getObjectWithPtr(void *ptr);
|
||||
|
||||
Class * _getClass() const; // NOLINT(readability-identifier-naming)
|
||||
napi_value _getJSObject() const;
|
||||
void _setFinalizeCallback(napi_finalize finalizeCb); // NOLINT(readability-identifier-naming)
|
||||
/**
|
||||
* @brief Returns the string for describing current object.
|
||||
* @return The string for describing current object.
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
bool init(napi_env env, napi_value js_object, Class *cls);
|
||||
|
||||
private:
|
||||
static void weakCallback(napi_env env, void *nativeObject, void * /*finalize_hint*/);
|
||||
static void setup();
|
||||
static void cleanup();
|
||||
|
||||
private:
|
||||
ObjectRef _objRef;
|
||||
napi_finalize _finalizeCb = nullptr;
|
||||
bool _clearMappingInFinalizer = true;
|
||||
void *_privateData = nullptr;
|
||||
PrivateObjectBase *_privateObject = nullptr;
|
||||
napi_env _env = nullptr;
|
||||
Class * _cls = nullptr;
|
||||
uint32_t _rootCount = 0;
|
||||
bool _onCleaingPrivateData = false;
|
||||
|
||||
friend class ScriptEngine;
|
||||
};
|
||||
}; // namespace se
|
||||
312
cocos/bindings/jswrapper/napi/ScriptEngine.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
#include <sstream>
|
||||
#include "../MappingUtils.h"
|
||||
#include "Class.h"
|
||||
#include "Utils.h"
|
||||
#include "CommonHeader.h"
|
||||
#include <napi/native_api.h>
|
||||
#include "base/std/container/array.h"
|
||||
|
||||
namespace se {
|
||||
AutoHandleScope::AutoHandleScope() {
|
||||
napi_open_handle_scope(ScriptEngine::getEnv(), &_handleScope);
|
||||
}
|
||||
|
||||
AutoHandleScope::~AutoHandleScope() {
|
||||
napi_close_handle_scope(ScriptEngine::getEnv(), _handleScope);
|
||||
}
|
||||
|
||||
ScriptEngine *gSriptEngineInstance = nullptr;
|
||||
|
||||
ScriptEngine::ScriptEngine() {};
|
||||
|
||||
ScriptEngine::~ScriptEngine() = default;
|
||||
|
||||
void ScriptEngine::setFileOperationDelegate(const FileOperationDelegate &delegate) {
|
||||
_fileOperationDelegate = delegate;
|
||||
}
|
||||
|
||||
const ScriptEngine::FileOperationDelegate &ScriptEngine::getFileOperationDelegate() const {
|
||||
return _fileOperationDelegate;
|
||||
}
|
||||
|
||||
ScriptEngine *ScriptEngine::getInstance() {
|
||||
if (gSriptEngineInstance == nullptr) {
|
||||
gSriptEngineInstance = new ScriptEngine();
|
||||
}
|
||||
|
||||
return gSriptEngineInstance;
|
||||
}
|
||||
|
||||
void ScriptEngine::destroyInstance() {
|
||||
if (gSriptEngineInstance) {
|
||||
gSriptEngineInstance->cleanup();
|
||||
delete gSriptEngineInstance;
|
||||
gSriptEngineInstance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptEngine::runScript(const std::string &path, Value *ret /* = nullptr */) {
|
||||
assert(!path.empty());
|
||||
napi_status status;
|
||||
napi_value result = nullptr;
|
||||
LOGI("run script : %{public}s", path.c_str());
|
||||
//NODE_API_CALL(status, ScriptEngine::getEnv(), napi_run_script_path(ScriptEngine::getEnv(), path.c_str(), &result));
|
||||
if (ret && result) {
|
||||
internal::jsToSeValue(result, ret);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptEngine::evalString(const char *scriptStr, ssize_t length, Value *ret, const char *fileName) {
|
||||
napi_status status;
|
||||
napi_value script;
|
||||
napi_value result;
|
||||
length = length < 0 ? NAPI_AUTO_LENGTH : length;
|
||||
status = napi_create_string_utf8(ScriptEngine::getEnv(), scriptStr, length, &script);
|
||||
LOGI("eval :%{public}s", scriptStr);
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_run_script(ScriptEngine::getEnv(), script, &result));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptEngine::init() {
|
||||
napi_status status;
|
||||
napi_value result;
|
||||
|
||||
for (const auto &hook : _beforeInitHookArray) {
|
||||
hook();
|
||||
}
|
||||
|
||||
Object::setup();
|
||||
NativePtrToObjectMap::init();
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_global(ScriptEngine::getEnv(), &result));
|
||||
_globalObj = Object::_createJSObject(ScriptEngine::getEnv(), result, nullptr);
|
||||
_globalObj->root();
|
||||
_globalObj->setProperty("window", Value(_globalObj));
|
||||
_globalObj->setProperty("scriptEngineType", se::Value("napi"));
|
||||
|
||||
_isValid = true;
|
||||
|
||||
for (const auto &hook : _afterInitHookArray) {
|
||||
hook();
|
||||
}
|
||||
_afterInitHookArray.clear();
|
||||
|
||||
return _isValid;
|
||||
}
|
||||
|
||||
Object *ScriptEngine::getGlobalObject() const {
|
||||
return _globalObj;
|
||||
}
|
||||
|
||||
bool ScriptEngine::start() {
|
||||
bool ok = true;
|
||||
if (!init()) {
|
||||
return false;
|
||||
}
|
||||
_startTime = std::chrono::steady_clock::now();
|
||||
for (auto cb : _permRegisterCallbackArray) {
|
||||
ok = cb(_globalObj);
|
||||
assert(ok);
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cb : _registerCallbackArray) {
|
||||
ok = cb(_globalObj);
|
||||
assert(ok);
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// After ScriptEngine is started, _registerCallbackArray isn't needed. Therefore, clear it here.
|
||||
_registerCallbackArray.clear();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void ScriptEngine::cleanup() {
|
||||
if (!_isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
SE_LOGD("ScriptEngine::cleanup begin ...\n");
|
||||
_isInCleanup = true;
|
||||
|
||||
for (const auto &hook : _beforeCleanupHookArray) {
|
||||
hook();
|
||||
}
|
||||
_beforeCleanupHookArray.clear();
|
||||
|
||||
SAFE_DEC_REF(_globalObj);
|
||||
Object::cleanup();
|
||||
Class::cleanup();
|
||||
garbageCollect();
|
||||
|
||||
_globalObj = nullptr;
|
||||
_isValid = false;
|
||||
|
||||
_registerCallbackArray.clear();
|
||||
|
||||
for (const auto &hook : _afterCleanupHookArray) {
|
||||
hook();
|
||||
}
|
||||
_afterCleanupHookArray.clear();
|
||||
|
||||
_isInCleanup = false;
|
||||
NativePtrToObjectMap::destroy();
|
||||
SE_LOGD("ScriptEngine::cleanup end ...\n");
|
||||
}
|
||||
|
||||
void ScriptEngine::addBeforeCleanupHook(const std::function<void()> &hook) {
|
||||
_beforeCleanupHookArray.push_back(hook);
|
||||
return;
|
||||
}
|
||||
|
||||
void ScriptEngine::addAfterCleanupHook(const std::function<void()> &hook) {
|
||||
_afterCleanupHookArray.push_back(hook);
|
||||
return;
|
||||
}
|
||||
|
||||
void ScriptEngine::addRegisterCallback(RegisterCallback cb) {
|
||||
assert(std::find(_registerCallbackArray.begin(), _registerCallbackArray.end(), cb) == _registerCallbackArray.end());
|
||||
_registerCallbackArray.push_back(cb);
|
||||
}
|
||||
|
||||
napi_env ScriptEngine::getEnv() {
|
||||
return getInstance()->_env;
|
||||
}
|
||||
|
||||
void ScriptEngine::setEnv(napi_env env) {
|
||||
getInstance()->_env = env;
|
||||
}
|
||||
|
||||
void ScriptEngine::addPermanentRegisterCallback(RegisterCallback cb) {
|
||||
if (std::find(_permRegisterCallbackArray.begin(), _permRegisterCallbackArray.end(), cb) == _permRegisterCallbackArray.end()) {
|
||||
_permRegisterCallbackArray.push_back(cb);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::setExceptionCallback(const ExceptionCallback &cb) {
|
||||
//not impl
|
||||
return;
|
||||
}
|
||||
|
||||
const std::chrono::steady_clock::time_point &ScriptEngine::getStartTime() const {
|
||||
return _startTime;
|
||||
}
|
||||
|
||||
bool ScriptEngine::isValid() const {
|
||||
return _isValid;
|
||||
}
|
||||
|
||||
void ScriptEngine::enableDebugger(const std::string &serverAddr, uint32_t port, bool isWait) {
|
||||
//not impl
|
||||
return;
|
||||
}
|
||||
|
||||
bool ScriptEngine::saveByteCodeToFile(const std::string &path, const std::string &pathBc) {
|
||||
//not impl
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptEngine::clearException() {
|
||||
//not impl
|
||||
return;
|
||||
}
|
||||
|
||||
void ScriptEngine::garbageCollect() {
|
||||
//not impl
|
||||
return;
|
||||
}
|
||||
|
||||
bool ScriptEngine::isGarbageCollecting() const {
|
||||
return _isGarbageCollecting;
|
||||
}
|
||||
|
||||
void ScriptEngine::_setGarbageCollecting(bool isGarbageCollecting) { //NOLINT(readability-identifier-naming)
|
||||
_isGarbageCollecting = isGarbageCollecting;
|
||||
}
|
||||
|
||||
void ScriptEngine::setJSExceptionCallback(const ExceptionCallback &cb) {
|
||||
//not impl
|
||||
return;
|
||||
}
|
||||
|
||||
void ScriptEngine::addAfterInitHook(const std::function<void()> &hook) {
|
||||
_afterInitHookArray.push_back(hook);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string ScriptEngine::getCurrentStackTrace() {
|
||||
//not impl
|
||||
return "";
|
||||
}
|
||||
|
||||
void ScriptEngine::_setNeedCallConstructor(bool need) {
|
||||
_isneedCallConstructor = need;
|
||||
}
|
||||
|
||||
bool ScriptEngine::_needCallConstructor() {
|
||||
return _isneedCallConstructor;
|
||||
}
|
||||
|
||||
bool ScriptEngine::callFunction(Object *targetObj, const char *funcName, uint32_t argc, Value *args, Value *rval) {
|
||||
Value objFunc;
|
||||
if (!targetObj->getProperty(funcName, &objFunc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ValueArray argv;
|
||||
|
||||
for (size_t i = 0; i < argc; ++i) {
|
||||
argv.push_back(args[i]);
|
||||
}
|
||||
|
||||
objFunc.toObject()->call(argv, targetObj, rval);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptEngine::handlePromiseExceptions() {
|
||||
//not impl
|
||||
assert(true);
|
||||
return;
|
||||
}
|
||||
|
||||
void ScriptEngine::mainLoopUpdate() {
|
||||
// empty implementation
|
||||
}
|
||||
|
||||
void ScriptEngine::throwException(const std::string &errorMessage) {
|
||||
napi_status status;
|
||||
NODE_API_CALL_RETURN_VOID(getEnv(), napi_throw_error(getEnv(), nullptr, errorMessage.c_str()));
|
||||
}
|
||||
}; // namespace se
|
||||
305
cocos/bindings/jswrapper/napi/ScriptEngine.h
Normal file
@@ -0,0 +1,305 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include "../Value.h"
|
||||
#include "../config.h"
|
||||
#include "CommonHeader.h"
|
||||
|
||||
using HandleScope = napi_handle_scope;
|
||||
namespace se {
|
||||
class AutoHandleScope {
|
||||
public:
|
||||
// This interface needs to be implemented in NAPI, similar to V8.
|
||||
// Ref:https://nodejs.org/docs/latest-v17.x/api/n-api.html#object-lifetime-management
|
||||
AutoHandleScope();
|
||||
~AutoHandleScope();
|
||||
|
||||
private:
|
||||
HandleScope _handleScope;
|
||||
};
|
||||
using RegisterCallback = bool (*)(Object *);
|
||||
using ExceptionCallback = std::function<void(const char *, const char *, const char *)>; // location, message, stack
|
||||
|
||||
class ScriptEngine {
|
||||
public:
|
||||
/**
|
||||
* Delegate class for file operation
|
||||
*/
|
||||
class FileOperationDelegate {
|
||||
public:
|
||||
FileOperationDelegate()
|
||||
: onGetDataFromFile(nullptr),
|
||||
onGetStringFromFile(nullptr),
|
||||
onCheckFileExist(nullptr),
|
||||
onGetFullPath(nullptr) {}
|
||||
|
||||
/**
|
||||
* @brief Tests whether delegate is valid.
|
||||
*/
|
||||
bool isValid() const {
|
||||
return onGetDataFromFile != nullptr && onGetStringFromFile != nullptr && onCheckFileExist != nullptr && onGetFullPath != nullptr;
|
||||
}
|
||||
|
||||
// path, buffer, buffer size
|
||||
std::function<void(const std::string &, const std::function<void(const uint8_t *, size_t)> &)> onGetDataFromFile;
|
||||
// path, return file string content.
|
||||
std::function<std::string(const std::string &)> onGetStringFromFile;
|
||||
// path
|
||||
std::function<bool(const std::string &)> onCheckFileExist;
|
||||
// path, return full path
|
||||
std::function<std::string(const std::string &)> onGetFullPath;
|
||||
};
|
||||
ScriptEngine();
|
||||
~ScriptEngine();
|
||||
/**
|
||||
* @brief Sets the delegate for file operation.
|
||||
* @param delegate[in] The delegate instance for file operation.
|
||||
*/
|
||||
void setFileOperationDelegate(const FileOperationDelegate &delegate);
|
||||
|
||||
/**
|
||||
* @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 std::string &path, const std::string &pathBc);
|
||||
|
||||
/**
|
||||
* @brief Gets the delegate for file operation.
|
||||
* @return The delegate for file operation
|
||||
*/
|
||||
const FileOperationDelegate &getFileOperationDelegate() const;
|
||||
|
||||
static ScriptEngine *getInstance();
|
||||
|
||||
/**
|
||||
* @brief Destroys the instance of script engine.
|
||||
*/
|
||||
static void destroyInstance();
|
||||
|
||||
/**
|
||||
* @brief Clears all exceptions.
|
||||
*/
|
||||
void clearException();
|
||||
|
||||
/**
|
||||
* @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 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 Grab a snapshot of the current JavaScript execution stack.
|
||||
* @return current stack trace string
|
||||
*/
|
||||
std::string getCurrentStackTrace();
|
||||
/**
|
||||
* @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, ssize_t length = -1, Value *ret = nullptr, const char *fileName = nullptr);
|
||||
|
||||
/**
|
||||
* @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 Initializes script engine.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note This method will create JavaScript context and global object.
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function before initializing script engine.
|
||||
* @param[in] hook A hook function to be invoked before initializing script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addBeforeInitHook(const std::function<void()> &hook);
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function after initializing script engine.
|
||||
* @param[in] hook A hook function to be invoked before initializing script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addAfterInitHook(const std::function<void()> &hook);
|
||||
|
||||
/**
|
||||
* @brief Cleanups script engine.
|
||||
* @note This method will removes all objects in JavaScript VM even whose are rooted, then shutdown JavaScript VMf.
|
||||
*/
|
||||
void cleanup();
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function before cleanuping script engine.
|
||||
* @param[in] hook A hook function to be invoked before cleanuping script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addBeforeCleanupHook(const std::function<void()> &hook);
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function after cleanuping script engine.
|
||||
* @param[in] hook A hook function to be invoked after cleanuping script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addAfterCleanupHook(const std::function<void()> &hook);
|
||||
/**
|
||||
* @brief Gets the global object of JavaScript VM.
|
||||
* @return The se::Object stores the global JavaScript object.
|
||||
*/
|
||||
Object *getGlobalObject() const;
|
||||
|
||||
static napi_env getEnv();
|
||||
static void setEnv(napi_env env);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
const std::chrono::steady_clock::time_point &getStartTime() const;
|
||||
|
||||
/**
|
||||
* @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 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 std::string &path, Value *ret = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Tests whether script engine is valid.
|
||||
* @return true if it's valid, otherwise false.
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/**
|
||||
* @brief Enables JavaScript debugger
|
||||
* @param[in] serverAddr The address of debugger server.
|
||||
* @param[in] isWait Whether wait debugger attach when loading.
|
||||
*/
|
||||
void enableDebugger(const std::string &serverAddr, uint32_t port, bool isWait = false);
|
||||
|
||||
/**
|
||||
* @brief Tests whether JavaScript debugger is enabled
|
||||
* @return true if JavaScript debugger is enabled, otherwise false.
|
||||
*/
|
||||
bool isDebuggerEnabled() const;
|
||||
|
||||
/**
|
||||
* @brief Main loop update trigger, it's need to invoked in main thread every frame.
|
||||
*/
|
||||
void mainLoopUpdate();
|
||||
|
||||
bool runByteCodeFile(const std::string &pathBc, Value *ret /* = nullptr */);
|
||||
|
||||
/**
|
||||
* @brief Throw JS exception
|
||||
*/
|
||||
void throwException(const std::string &errorMessage);
|
||||
|
||||
/**
|
||||
* @brief for napi_new_instance, skip constructor.
|
||||
*/
|
||||
void _setNeedCallConstructor(bool need);
|
||||
|
||||
/**
|
||||
* @brief for napi_new_instance, skip constructor.
|
||||
*/
|
||||
bool _needCallConstructor();
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
void _setGarbageCollecting(bool isGarbageCollecting); // NOLINT(readability-identifier-naming)
|
||||
|
||||
void handlePromiseExceptions();
|
||||
|
||||
private:
|
||||
FileOperationDelegate _fileOperationDelegate;
|
||||
std::vector<RegisterCallback> _registerCallbackArray;
|
||||
std::vector<RegisterCallback> _permRegisterCallbackArray;
|
||||
|
||||
std::vector<std::function<void()>> _beforeInitHookArray;
|
||||
std::vector<std::function<void()>> _afterInitHookArray;
|
||||
std::vector<std::function<void()>> _beforeCleanupHookArray;
|
||||
std::vector<std::function<void()>> _afterCleanupHookArray;
|
||||
|
||||
Object * _globalObj = nullptr;
|
||||
napi_env _env = nullptr;
|
||||
|
||||
bool _isValid{false};
|
||||
bool _isGarbageCollecting{false};
|
||||
bool _isInCleanup{false};
|
||||
bool _isErrorHandleWorking{false};
|
||||
bool _isneedCallConstructor{true};
|
||||
std::chrono::steady_clock::time_point _startTime;
|
||||
};
|
||||
}; // namespace se
|
||||
32
cocos/bindings/jswrapper/napi/SeApi.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Class.h"
|
||||
#include "HelperMacros.h"
|
||||
#include "Object.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "Utils.h"
|
||||
195
cocos/bindings/jswrapper/napi/Utils.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "Utils.h"
|
||||
#include "CommonHeader.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "Class.h"
|
||||
|
||||
#define MAX_STRING_LENS 1024
|
||||
|
||||
namespace se {
|
||||
namespace internal {
|
||||
void jsToSeValue(const target_value& value, Value* v) {
|
||||
assert(v != nullptr);
|
||||
napi_status status;
|
||||
napi_valuetype valType;
|
||||
int64_t iRet = 0;
|
||||
double dRet = 0.0;
|
||||
bool bRet = false;
|
||||
bool lossless = false;
|
||||
size_t len = 0;
|
||||
void* privateObjPtr = nullptr;
|
||||
void* nativePtr = nullptr;
|
||||
Object* obj = nullptr;
|
||||
|
||||
if (!value) {
|
||||
valType = napi_valuetype::napi_undefined;
|
||||
}else {
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_typeof(ScriptEngine::getEnv(), value, &valType));
|
||||
}
|
||||
|
||||
switch (valType) {
|
||||
case napi_valuetype::napi_undefined:
|
||||
v->setUndefined();
|
||||
break;
|
||||
case napi_valuetype::napi_null:
|
||||
v->setNull();
|
||||
break;
|
||||
case napi_valuetype::napi_number:
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_value_double(ScriptEngine::getEnv(), value, &dRet));
|
||||
if (status == napi_ok) {
|
||||
v->setDouble(dRet);
|
||||
} else {
|
||||
v->setUndefined();
|
||||
}
|
||||
break;
|
||||
case napi_valuetype::napi_bigint:
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_value_bigint_int64(ScriptEngine::getEnv(), value, &iRet, &lossless));
|
||||
if (lossless) {
|
||||
v->setInt64(iRet);
|
||||
} else {
|
||||
v->setUndefined();
|
||||
}
|
||||
|
||||
break;
|
||||
case napi_valuetype::napi_string:
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_value_string_utf8(ScriptEngine::getEnv(), value, nullptr, 0, &len));
|
||||
if (status == napi_ok) {
|
||||
std::string valueStr;
|
||||
len += 1;
|
||||
valueStr.resize(len);
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_value_string_utf8(ScriptEngine::getEnv(), value, const_cast<char*>(valueStr.data()), valueStr.size(), &len));
|
||||
if (valueStr.length() != len) {
|
||||
valueStr.resize(len);
|
||||
}
|
||||
v->setString(valueStr);
|
||||
} else {
|
||||
v->setUndefined();
|
||||
}
|
||||
break;
|
||||
case napi_valuetype::napi_boolean:
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_value_bool(ScriptEngine::getEnv(), value, &bRet));
|
||||
if (status == napi_ok) {
|
||||
v->setBoolean(bRet);
|
||||
} else {
|
||||
v->setUndefined();
|
||||
}
|
||||
break;
|
||||
case napi_valuetype::napi_object:
|
||||
case napi_valuetype::napi_function:
|
||||
status = napi_unwrap(ScriptEngine::getEnv(), value, &privateObjPtr);
|
||||
if ((status == napi_ok) && privateObjPtr) {
|
||||
nativePtr = reinterpret_cast<Object*>(privateObjPtr)->getPrivateData();
|
||||
}
|
||||
if (nativePtr) {
|
||||
obj = Object::getObjectWithPtr(nativePtr);
|
||||
}
|
||||
if (obj == nullptr) {
|
||||
obj = Object::_createJSObject(ScriptEngine::getEnv(), value, nullptr);
|
||||
}
|
||||
if (obj) {
|
||||
v->setObject(obj, true);
|
||||
obj->decRef();
|
||||
} else {
|
||||
v->setUndefined();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void jsToSeArgs(size_t argc, target_value* argv, ValueArray* outArr) {
|
||||
assert(outArr != nullptr);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
Value v;
|
||||
jsToSeValue(argv[i], &v);
|
||||
outArr->push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
bool seToJsValue(const Value& v, target_value* outJsVal) {
|
||||
assert(outJsVal != nullptr);
|
||||
bool ret = false;
|
||||
napi_status status = napi_ok;
|
||||
switch (v.getType()) {
|
||||
case Value::Type::Number:
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_double(ScriptEngine::getEnv(), v.toDouble(), outJsVal));
|
||||
ret = (status == napi_ok);
|
||||
break;
|
||||
case Value::Type::String: {
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_string_utf8(ScriptEngine::getEnv(), v.toString().c_str(), v.toString().length(), outJsVal));
|
||||
ret = (status == napi_ok);
|
||||
} break;
|
||||
case Value::Type::Boolean:
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_boolean(ScriptEngine::getEnv(), v.toBoolean(), outJsVal));
|
||||
ret = (status == napi_ok);
|
||||
break;
|
||||
case Value::Type::Object:
|
||||
*outJsVal = v.toObject()->_getJSObject();
|
||||
ret = (outJsVal != nullptr);
|
||||
break;
|
||||
case Value::Type::Null:
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_null(ScriptEngine::getEnv(), outJsVal));
|
||||
ret = (status == napi_ok);
|
||||
break;
|
||||
case Value::Type::Undefined:
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_get_undefined(ScriptEngine::getEnv(), outJsVal));
|
||||
ret = (status == napi_ok);
|
||||
break;
|
||||
case Value::Type::BigInt:
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_bigint_int64(ScriptEngine::getEnv(), v.toInt64(), outJsVal));
|
||||
ret = (status == napi_ok);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
//LOGI("type :%d", v.getType());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void seToJsArgs(napi_env env, const ValueArray& args, std::vector<target_value>* outArr) {
|
||||
assert(outArr != nullptr);
|
||||
for (const auto& data : args) {
|
||||
napi_value jsval;
|
||||
seToJsValue(data, &jsval);
|
||||
outArr->push_back(jsval);
|
||||
}
|
||||
}
|
||||
|
||||
bool setReturnValue(const Value& data, target_value& argv) {
|
||||
if (data.getType() == Value::Type::BigInt) {
|
||||
// TODO: fix 'TypeError: Cannot mix BigInt and other types, use explicit conversions' for spine & dragonbones
|
||||
napi_status status;
|
||||
NODE_API_CALL(status, ScriptEngine::getEnv(), napi_create_double(ScriptEngine::getEnv(), data.toDouble(), &argv));
|
||||
return true;
|
||||
}
|
||||
|
||||
return seToJsValue(data, &argv);
|
||||
}
|
||||
} // namespace internal
|
||||
}; // namespace se
|
||||
46
cocos/bindings/jswrapper/napi/Utils.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../config.h"
|
||||
#include "Object.h"
|
||||
namespace se {
|
||||
|
||||
namespace internal {
|
||||
|
||||
using target_value = napi_value;
|
||||
struct PrivateData {
|
||||
void * data;
|
||||
Object *seObj;
|
||||
};
|
||||
|
||||
bool setReturnValue(const Value &data, target_value &argv);
|
||||
void jsToSeValue(const target_value &value, Value *v);
|
||||
void jsToSeArgs(size_t argc, target_value *argv, ValueArray *outArr);
|
||||
bool seToJsValue(const Value &v, target_value *jsval);
|
||||
void seToJsArgs(napi_env env, const ValueArray &args, std::vector<target_value> *outArr);
|
||||
} // namespace internal
|
||||
} // namespace se
|
||||
115
cocos/bindings/jswrapper/napi/native_common.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/****************************************************************************
|
||||
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 engine source code (the "Software"), a limited,
|
||||
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
|
||||
to use Cocos Creator solely to develop games on your target platforms. You shall
|
||||
not use Cocos Creator software for developing other software or tools that's
|
||||
used for developing games. You are not granted to publish, distribute,
|
||||
sublicense, and/or sell copies of Cocos Creator.
|
||||
|
||||
The software or tools in this License Agreement are licensed, not sold.
|
||||
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#ifndef FOUNDATION_ACE_NAPI_INTERFACES_KITS_NAPI_NATIVE_COMMON_H
|
||||
#define FOUNDATION_ACE_NAPI_INTERFACES_KITS_NAPI_NATIVE_COMMON_H
|
||||
|
||||
#define DEPRECATED __attribute__((__deprecated__))
|
||||
|
||||
#ifndef NAPI_VERSION
|
||||
#define NAPI_VERSION 8
|
||||
#endif
|
||||
|
||||
#define NAPI_RETVAL_NOTHING
|
||||
|
||||
#define GET_AND_THROW_LAST_ERROR(env) \
|
||||
do { \
|
||||
const napi_extended_error_info* errorInfo = nullptr; \
|
||||
napi_get_last_error_info((env), &errorInfo); \
|
||||
bool isPending = false; \
|
||||
napi_is_exception_pending((env), &isPending); \
|
||||
if (!isPending && errorInfo != nullptr) { \
|
||||
const char* errorMessage = \
|
||||
errorInfo->error_message != nullptr ? errorInfo->error_message : "empty error message"; \
|
||||
napi_throw_error((env), nullptr, errorMessage); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define NAPI_ASSERT_BASE(env, assertion, message, retVal) \
|
||||
do { \
|
||||
if (!(assertion)) { \
|
||||
napi_throw_error((env), nullptr, "assertion (" #assertion ") failed: " message); \
|
||||
return retVal; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define NAPI_ASSERT(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, nullptr)
|
||||
|
||||
#define NAPI_ASSERT_RETURN_VOID(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, NAPI_RETVAL_NOTHING)
|
||||
|
||||
#define NAPI_CALL_BASE(env, theCall, retVal) \
|
||||
do { \
|
||||
if ((theCall) != napi_ok) { \
|
||||
GET_AND_THROW_LAST_ERROR((env)); \
|
||||
return retVal; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define NAPI_CALL(env, theCall) NAPI_CALL_BASE(env, theCall, nullptr)
|
||||
|
||||
#define NAPI_CALL_RETURN_VOID(env, theCall) NAPI_CALL_BASE(env, theCall, NAPI_RETVAL_NOTHING)
|
||||
|
||||
#define DECLARE_NAPI_PROPERTY(name, val) \
|
||||
{ \
|
||||
(name), nullptr, nullptr, nullptr, nullptr, val, napi_default, nullptr \
|
||||
}
|
||||
|
||||
#define DECLARE_NAPI_STATIC_PROPERTY(name, val) \
|
||||
{ \
|
||||
(name), nullptr, nullptr, nullptr, nullptr, val, napi_static, nullptr \
|
||||
}
|
||||
|
||||
#define DECLARE_NAPI_FUNCTION(name, func) \
|
||||
{ \
|
||||
(name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, nullptr \
|
||||
}
|
||||
|
||||
#define DECLARE_NAPI_FUNCTION_WITH_DATA(name, func, data) \
|
||||
{ \
|
||||
(name), nullptr, (func), nullptr, nullptr, nullptr, napi_default, data \
|
||||
}
|
||||
|
||||
#define DECLARE_NAPI_STATIC_FUNCTION(name, func) \
|
||||
{ \
|
||||
(name), nullptr, (func), nullptr, nullptr, nullptr, napi_static, nullptr \
|
||||
}
|
||||
|
||||
#define DECLARE_NAPI_GETTER(name, getter) \
|
||||
{ \
|
||||
(name), nullptr, nullptr, (getter), nullptr, nullptr, napi_default, nullptr \
|
||||
}
|
||||
|
||||
#define DECLARE_NAPI_SETTER(name, setter) \
|
||||
{ \
|
||||
(name), nullptr, nullptr, nullptr, (setter), nullptr, napi_default, nullptr \
|
||||
}
|
||||
|
||||
#define DECLARE_NAPI_GETTER_SETTER(name, getter, setter) \
|
||||
{ \
|
||||
(name), nullptr, nullptr, (getter), (setter), nullptr, napi_default, nullptr \
|
||||
}
|
||||
|
||||
#endif /* FOUNDATION_ACE_NAPI_INTERFACES_KITS_NAPI_NATIVE_COMMON_H */
|
||||
63
cocos/bindings/jswrapper/sm/Base.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/****************************************************************************
|
||||
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
|
||||
|
||||
#ifndef UINT64_C
|
||||
#define UINT64_C(value) __CONCAT(value, ULL)
|
||||
#endif
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "js/Array.h"
|
||||
#include "js/ArrayBuffer.h"
|
||||
#include "js/BigInt.h"
|
||||
#include "js/BuildId.h"
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/ContextOptions.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "js/Equality.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/Initialization.h"
|
||||
#include "js/JSON.h"
|
||||
#include "js/LocaleSensitive.h"
|
||||
#include "js/MemoryMetrics.h"
|
||||
#include "js/SourceText.h"
|
||||
#include "js/Warnings.h"
|
||||
#include "js/experimental/TypedData.h"
|
||||
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include "../PrivateObject.h"
|
||||
#include "HelperMacros.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "base/std/container/string.h"
|
||||
242
cocos/bindings/jswrapper/sm/Class.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
/****************************************************************************
|
||||
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 "Object.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
namespace se {
|
||||
|
||||
// --- Global Lookup for Constructor Functions
|
||||
|
||||
namespace {
|
||||
// std::unordered_map<std::string, Class *> __clsMap;
|
||||
JSContext *__cx = nullptr;
|
||||
|
||||
bool empty_constructor(JSContext *cx, uint32_t argc, JS::Value *vp) {
|
||||
assert(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
ccstd::vector<Class *> __allClasses;
|
||||
|
||||
} // namespace
|
||||
|
||||
Class::Class()
|
||||
: _parent(nullptr),
|
||||
_proto(nullptr),
|
||||
_parentProto(nullptr),
|
||||
_ctor(nullptr),
|
||||
_finalizeOp(nullptr) {
|
||||
memset(&_jsCls, 0, sizeof(_jsCls));
|
||||
memset(&_classOps, 0, sizeof(_classOps));
|
||||
|
||||
__allClasses.push_back(this);
|
||||
}
|
||||
|
||||
Class::~Class() {
|
||||
}
|
||||
|
||||
Class *Class::create(const char *className, Object *obj, Object *parentProto, JSNative ctor) {
|
||||
Class *cls = ccnew Class();
|
||||
if (cls != nullptr && !cls->init(className, obj, parentProto, ctor)) {
|
||||
delete cls;
|
||||
cls = nullptr;
|
||||
}
|
||||
return cls;
|
||||
}
|
||||
|
||||
Class *Class::create(const std::initializer_list<const char *> &classPath, se::Object *parent, Object *parentProto, JSNative ctor) {
|
||||
se::AutoHandleScope scope;
|
||||
se::Object *currentParent = parent;
|
||||
se::Value tmp;
|
||||
for (auto i = 0; i < classPath.size() - 1; i++) {
|
||||
bool ok = currentParent->getProperty(*(classPath.begin() + i), &tmp);
|
||||
CC_ASSERT(ok); // class or namespace in path is not defined
|
||||
currentParent = tmp.toObject();
|
||||
}
|
||||
return create(*(classPath.end() - 1), currentParent, parentProto, ctor);
|
||||
}
|
||||
|
||||
bool Class::init(const char *clsName, Object *parent, Object *parentProto, JSNative ctor) {
|
||||
_name = clsName;
|
||||
|
||||
_parent = parent;
|
||||
if (_parent != nullptr)
|
||||
_parent->incRef();
|
||||
|
||||
_parentProto = parentProto;
|
||||
if (_parentProto != nullptr)
|
||||
_parentProto->incRef();
|
||||
|
||||
_ctor = ctor;
|
||||
if (_ctor == nullptr) {
|
||||
_ctor = empty_constructor;
|
||||
}
|
||||
|
||||
// SE_LOGD("Class init ( %s ) ...\n", clsName);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Class::destroy() {
|
||||
SAFE_DEC_REF(_parent);
|
||||
SAFE_DEC_REF(_proto);
|
||||
SAFE_DEC_REF(_parentProto);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void Class::onTraceCallback(JSTracer *trc, JSObject *obj) {
|
||||
auto *seObj = reinterpret_cast<Object *>(internal::SE_JS_GetPrivate(obj, 1));
|
||||
if (seObj != nullptr) {
|
||||
JS::TraceEdge(trc, &seObj->_heap, "seObj");
|
||||
}
|
||||
}
|
||||
|
||||
bool Class::install() {
|
||||
// assert(__clsMap.find(_name) == __clsMap.end());
|
||||
//
|
||||
// __clsMap.emplace(_name, this);
|
||||
|
||||
_jsCls.name = _name;
|
||||
_jsCls.flags = JSCLASS_USERBIT1 | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_FOREGROUND_FINALIZE; //IDEA: Use JSCLASS_BACKGROUND_FINALIZE to improve GC performance
|
||||
if (_finalizeOp != nullptr) {
|
||||
_classOps.finalize = _finalizeOp;
|
||||
} else {
|
||||
_classOps.finalize = [](JSFreeOp *fop, JSObject *obj) {};
|
||||
}
|
||||
|
||||
_classOps.trace = Class::onTraceCallback;
|
||||
|
||||
_jsCls.cOps = &_classOps;
|
||||
|
||||
JSObject *parentObj = _parentProto != nullptr ? _parentProto->_getJSObject() : nullptr;
|
||||
JS::RootedObject parentProto(__cx, parentObj);
|
||||
JS::RootedObject parent(__cx, _parent->_getJSObject());
|
||||
|
||||
_funcs.push_back(JS_FS_END);
|
||||
_properties.push_back(JS_PS_END);
|
||||
_staticFuncs.push_back(JS_FS_END);
|
||||
_staticProperties.push_back(JS_PS_END);
|
||||
|
||||
JS::RootedObject jsobj(__cx, JS_InitClass(__cx, parent, parentProto, &_jsCls, _ctor, 0, _properties.data(), _funcs.data(), _staticProperties.data(), _staticFuncs.data()));
|
||||
if (jsobj != nullptr) {
|
||||
_proto = Object::_createJSObject(nullptr, jsobj);
|
||||
// SE_LOGD("_proto: %p, name: %s\n", _proto, _name);
|
||||
_proto->root();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Class::defineFunction(const char *name, JSNative func) {
|
||||
JSFunctionSpec cb = JS_FN(name, func, 0, JSPROP_ENUMERATE);
|
||||
_funcs.push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineProperty(const char *name, JSNative getter, JSNative setter) {
|
||||
JSPropertySpec property = JS_PSGS(name, getter, setter, JSPROP_ENUMERATE);
|
||||
_properties.push_back(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineProperty(const std::initializer_list<const char *> &names, JSNative getter, JSNative setter) {
|
||||
bool ret = true;
|
||||
for (const auto *name : names) {
|
||||
ret &= defineProperty(name, getter, setter);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Class::defineStaticFunction(const char *name, JSNative func) {
|
||||
JSFunctionSpec cb = JS_FN(name, func, 0, JSPROP_ENUMERATE);
|
||||
_staticFuncs.push_back(cb);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineStaticProperty(const char *name, JSNative getter, JSNative setter) {
|
||||
JSPropertySpec property = JS_PSGS(name, getter, setter, JSPROP_ENUMERATE);
|
||||
_staticProperties.push_back(property);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Class::defineFinalizeFunction(JSFinalizeOp func) {
|
||||
_finalizeOp = func;
|
||||
return true;
|
||||
}
|
||||
|
||||
// JSObject* Class::_createJSObject(const std::string &clsName, Class** outCls)
|
||||
// {
|
||||
// auto iter = __clsMap.find(clsName);
|
||||
// if (iter == __clsMap.end())
|
||||
// {
|
||||
// *outCls = nullptr;
|
||||
// return nullptr;
|
||||
// }
|
||||
//
|
||||
// Class* thiz = iter->second;
|
||||
// JS::RootedObject obj(__cx, _createJSObjectWithClass(thiz));
|
||||
// *outCls = thiz;
|
||||
// return obj;
|
||||
// }
|
||||
|
||||
void Class::_createJSObjectWithClass(Class *cls, JS::MutableHandleObject outObj) {
|
||||
JSObject *proto = cls->_proto != nullptr ? cls->_proto->_getJSObject() : nullptr;
|
||||
JS::RootedObject jsProto(__cx, proto);
|
||||
outObj.set(JS_NewObjectWithGivenProto(__cx, &cls->_jsCls, jsProto));
|
||||
}
|
||||
|
||||
void Class::setContext(JSContext *cx) {
|
||||
__cx = cx;
|
||||
}
|
||||
|
||||
Object *Class::getProto() {
|
||||
return _proto;
|
||||
}
|
||||
|
||||
JSFinalizeOp Class::_getFinalizeCb() const {
|
||||
return _finalizeOp;
|
||||
}
|
||||
|
||||
void Class::cleanup() {
|
||||
for (auto cls : __allClasses) {
|
||||
cls->destroy();
|
||||
}
|
||||
|
||||
se::ScriptEngine::getInstance()->addAfterCleanupHook([]() {
|
||||
for (auto cls : __allClasses) {
|
||||
delete cls;
|
||||
}
|
||||
__allClasses.clear();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
157
cocos/bindings/jswrapper/sm/Class.h
Normal file
@@ -0,0 +1,157 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Object;
|
||||
|
||||
/**
|
||||
* se::Class represents a definition of how to create a native binding object.
|
||||
*/
|
||||
class Class final {
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a class used for creating relevant native binding objects.
|
||||
* @param[in] className A null-terminated UTF8 string containing the class's name.
|
||||
* @param[in] obj The object that current class proto object attaches to. Should not be nullptr.
|
||||
* @param[in] parentProto The parent proto object that current class inherits from. Passing nullptr means a new class has no parent.
|
||||
* @param[in] ctor A callback to invoke when your constructor is used in a 'new' expression. Pass nullptr to use the default object constructor.
|
||||
* @return A class instance used for creating relevant native binding objects.
|
||||
* @note Don't need to delete the pointer return by this method, it's managed internally.
|
||||
*/
|
||||
static Class *create(const char *className, Object *obj, Object *parentProto, JSNative ctor);
|
||||
static Class *create(const std::initializer_list<const char *> &classPath, se::Object *parent, Object *parentProto, JSNative ctor);
|
||||
|
||||
/**
|
||||
* @brief Defines a member function with a callback. Each objects created by class will have this function property.
|
||||
* @param[in] name A null-terminated UTF8 string containing the function name.
|
||||
* @param[in] func A callback to invoke when the property is called as a function.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineFunction(const char *name, JSNative func);
|
||||
|
||||
/**
|
||||
* @brief Defines a property with accessor callbacks. Each objects created by class will have this property.
|
||||
* @param[in] name A null-terminated UTF8 string containing the property name.
|
||||
* @param[in] getter A callback to invoke when the property is read.
|
||||
* @param[in] setter A callback to invoke when the property is set.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineProperty(const char *name, JSNative getter, JSNative setter);
|
||||
bool defineProperty(const std::initializer_list<const char *> &names, JSNative getter, JSNative setter);
|
||||
|
||||
/**
|
||||
* @brief Defines a static function with a callback. Only JavaScript constructor object will have this function.
|
||||
* @param[in] name A null-terminated UTF8 string containing the function name.
|
||||
* @param[in] func A callback to invoke when the constructor's property is called as a function.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineStaticFunction(const char *name, JSNative func);
|
||||
|
||||
/**
|
||||
* @brief Defines a static property with accessor callbacks. Only JavaScript constructor object will have this property.
|
||||
* @param[in] name A null-terminated UTF8 string containing the property name.
|
||||
* @param[in] getter A callback to invoke when the constructor's property is read.
|
||||
* @param[in] setter A callback to invoke when the constructor's property is set.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineStaticProperty(const char *name, JSNative getter, JSNative setter);
|
||||
|
||||
/**
|
||||
* @brief Defines the finalize function with a callback.
|
||||
* @param[in] func The callback to invoke when a JavaScript object is garbage collected.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineFinalizeFunction(JSFinalizeOp func);
|
||||
|
||||
/**
|
||||
* @brief Installs class to JavaScript VM.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note After this method, an object could be created by `var foo = new Foo();`.
|
||||
*/
|
||||
bool install();
|
||||
|
||||
/**
|
||||
* @brief Gets the proto object of this class.
|
||||
* @return The proto object of this class.
|
||||
* @note Don't need to be released in user code.
|
||||
*/
|
||||
Object *getProto();
|
||||
|
||||
/**
|
||||
* @brief Gets the class name.
|
||||
* @return The class name.
|
||||
*/
|
||||
const char *getName() const { return _name; }
|
||||
|
||||
// Private API used in wrapper
|
||||
JSFinalizeOp _getFinalizeCb() const;
|
||||
//
|
||||
private:
|
||||
Class();
|
||||
~Class();
|
||||
|
||||
bool init(const char *clsName, Object *obj, Object *parentProto, JSNative ctor);
|
||||
void destroy();
|
||||
|
||||
// static JSObject* _createJSObject(const std::string &clsName, Class** outCls);
|
||||
static void _createJSObjectWithClass(Class *cls, JS::MutableHandleObject outObj);
|
||||
|
||||
static void setContext(JSContext *cx);
|
||||
static void cleanup();
|
||||
|
||||
static void onTraceCallback(JSTracer *trc, JSObject *obj);
|
||||
|
||||
const char *_name;
|
||||
Object *_parent;
|
||||
Object *_proto;
|
||||
Object *_parentProto;
|
||||
|
||||
JSNative _ctor;
|
||||
|
||||
JSClass _jsCls;
|
||||
JSClassOps _classOps;
|
||||
|
||||
ccstd::vector<JSFunctionSpec> _funcs;
|
||||
ccstd::vector<JSFunctionSpec> _staticFuncs;
|
||||
ccstd::vector<JSPropertySpec> _properties;
|
||||
ccstd::vector<JSPropertySpec> _staticProperties;
|
||||
JSFinalizeOp _finalizeOp;
|
||||
|
||||
friend class ScriptEngine;
|
||||
friend class Object;
|
||||
};
|
||||
|
||||
} // namespace se
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
81
cocos/bindings/jswrapper/sm/HelperMacros.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/****************************************************************************
|
||||
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"
|
||||
|
||||
#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
|
||||
}
|
||||
249
cocos/bindings/jswrapper/sm/HelperMacros.h
Normal 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.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../ValueArrayPool.h"
|
||||
#include "../config.h"
|
||||
|
||||
//#define RECORD_JSB_INVOKING
|
||||
|
||||
#ifndef CC_DEBUG
|
||||
#undef RECORD_JSB_INVOKING
|
||||
#endif
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
#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);
|
||||
|
||||
#define SAFE_INC_REF(obj) \
|
||||
if (obj != nullptr) obj->incRef()
|
||||
#define SAFE_DEC_REF(obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
(obj)->decRef(); \
|
||||
(obj) = nullptr; \
|
||||
}
|
||||
|
||||
#define _SE(name) name##Registry
|
||||
|
||||
#define SE_DECLARE_FUNC(funcName) \
|
||||
bool funcName##Registry(JSContext *_cx, unsigned argc, JS::Value *_vp)
|
||||
|
||||
#define SE_BIND_FUNC(funcName) \
|
||||
bool funcName##Registry(JSContext *_cx, unsigned argc, JS::Value *_vp) { \
|
||||
JsbInvokeScope(#funcName); \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
JS::RootedObject _thizObj(_cx); \
|
||||
_argv.computeThis(_cx, &_thizObj); \
|
||||
bool needDeleteValueArray{false}; \
|
||||
se::ValueArray &args = se::gValueArrayPool.get(argc, needDeleteValueArray); \
|
||||
se::CallbackDepthGuard depthGuard{args, se::gValueArrayPool._depth, needDeleteValueArray}; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, args); \
|
||||
se::PrivateObjectBase *privateObject = static_cast<se::PrivateObjectBase *>(se::internal::getPrivate(_cx, _thizObj, 0)); \
|
||||
se::Object *thisObject = reinterpret_cast<se::Object *>(se::internal::getPrivate(_cx, _thizObj, 1)); \
|
||||
se::State state(thisObject, privateObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
se::internal::setReturnValue(_cx, state.rval(), _argv); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define SE_BIND_FUNC_FAST(funcName) \
|
||||
bool funcName##Registry(JSContext *_cx, unsigned argc, JS::Value *_vp) { \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
JS::RootedObject _thizObj(_cx); \
|
||||
_argv.computeThis(_cx, &_thizObj); \
|
||||
auto *privateObject = static_cast<se::PrivateObjectBase *>(se::internal::SE_JS_GetPrivate(_thizObj, 0)); \
|
||||
funcName(privateObject->getRaw()); \
|
||||
_argv.rval().setUndefined(); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define SE_DECLARE_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(JSFreeOp *_fop, JSObject *_obj);
|
||||
|
||||
#define SE_BIND_FINALIZE_FUNC(funcName) \
|
||||
void funcName##Registry(JSFreeOp *_fop, JSObject *_obj) { \
|
||||
JsbInvokeScope(#funcName); \
|
||||
se::PrivateObjectBase *privateObject = static_cast<se::PrivateObjectBase *>(se::internal::SE_JS_GetPrivate(_obj, 0)); \
|
||||
se::Object *seObj = static_cast<se::Object *>(se::internal::SE_JS_GetPrivate(_obj, 1)); \
|
||||
bool ret = false; \
|
||||
if (privateObject == nullptr) \
|
||||
return; \
|
||||
se::State state(privateObject); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
if (seObj->isClearMappingInFinalizer() && privateObject != nullptr) { \
|
||||
void *nativeObj = privateObject->getRaw(); \
|
||||
auto iter = se::NativePtrToObjectMap::find(nativeObj); \
|
||||
if (iter != se::NativePtrToObjectMap::end()) { \
|
||||
se::NativePtrToObjectMap::erase(iter); \
|
||||
} \
|
||||
} \
|
||||
seObj->decRef(); \
|
||||
}
|
||||
|
||||
#define SE_BIND_CTOR(funcName, cls, finalizeCb) \
|
||||
bool funcName##Registry(JSContext *_cx, unsigned argc, JS::Value *_vp) { \
|
||||
JsbInvokeScope(#funcName); \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
bool needDeleteValueArray{false}; \
|
||||
se::ValueArray &args = se::gValueArrayPool.get(argc, needDeleteValueArray); \
|
||||
se::CallbackDepthGuard depthGuard{args, se::gValueArrayPool._depth, needDeleteValueArray}; \
|
||||
se::internal::jsToSeArgs(_cx, argc, _argv, args); \
|
||||
se::Object *thisObject = se::Object::_createJSObjectForConstructor(cls, _argv); \
|
||||
thisObject->_setFinalizeCallback(finalizeCb##Registry); \
|
||||
_argv.rval().setObject(*thisObject->_getJSObject()); \
|
||||
se::State state(thisObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (ret) { \
|
||||
se::Value _property; \
|
||||
bool _found = false; \
|
||||
_found = thisObject->getProperty("_ctor", &_property); \
|
||||
if (_found) _property.toObject()->call(args, thisObject); \
|
||||
} else { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define SE_BIND_PROP_GET_IMPL(funcName, postFix) \
|
||||
bool funcName##postFix##Registry(JSContext *_cx, unsigned argc, JS::Value *_vp) { \
|
||||
JsbInvokeScope(#funcName); \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(argc, _vp); \
|
||||
JS::RootedObject _thizObj(_cx); \
|
||||
_argv.computeThis(_cx, &_thizObj); \
|
||||
se::PrivateObjectBase *privateObject = static_cast<se::PrivateObjectBase *>(se::internal::getPrivate(_cx, _thizObj, 0)); \
|
||||
se::Object *thisObject = reinterpret_cast<se::Object *>(se::internal::getPrivate(_cx, _thizObj, 1)); \
|
||||
se::State state(thisObject, privateObject); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
se::internal::setReturnValue(_cx, state.rval(), _argv); \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define SE_BIND_PROP_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) \
|
||||
bool funcName##postFix##Registry(JSContext *_cx, unsigned _argc, JS::Value *_vp) { \
|
||||
JsbInvokeScope(#funcName); \
|
||||
bool ret = false; \
|
||||
JS::CallArgs _argv = JS::CallArgsFromVp(_argc, _vp); \
|
||||
JS::RootedObject _thizObj(_cx); \
|
||||
_argv.computeThis(_cx, &_thizObj); \
|
||||
se::PrivateObjectBase *privateObject = static_cast<se::PrivateObjectBase *>(se::internal::getPrivate(_cx, _thizObj, 0)); \
|
||||
se::Object *thisObject = reinterpret_cast<se::Object *>(se::internal::getPrivate(_cx, _thizObj, 1)); \
|
||||
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(_cx, _argv[0], &data); \
|
||||
se::State state(thisObject, privateObject, args); \
|
||||
ret = funcName(state); \
|
||||
if (!ret) { \
|
||||
SE_LOGE("[ERROR] Failed to invoke %s, location: %s:%d\n", #funcName, __FILE__, __LINE__); \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#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
|
||||
#define SE_QUOTEME(x) SE_QUOTEME_(x)
|
||||
|
||||
#define SE_REPORT_ERROR(fmt, ...) \
|
||||
SE_LOGD("ERROR (" __FILE__ ", " SE_QUOTEME(__LINE__) "): " fmt "\n", ##__VA_ARGS__); \
|
||||
JS_ReportErrorUTF8(se::ScriptEngine::getInstance()->_getContext(), fmt, ##__VA_ARGS__)
|
||||
|
||||
#if CC_DEBUG > 0
|
||||
|
||||
#define SE_ASSERT(cond, fmt, ...) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
SE_LOGE("ASSERT (" __FILE__ ", " SE_QUOTEME(__LINE__) "): " fmt "\n", ##__VA_ARGS__); \
|
||||
assert(false); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#else
|
||||
|
||||
#define SE_ASSERT(cond, fmt, ...)
|
||||
|
||||
#endif // #if CC_DEBUG > 0
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
780
cocos/bindings/jswrapper/sm/Object.cpp
Normal file
@@ -0,0 +1,780 @@
|
||||
/****************************************************************************
|
||||
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 "Object.h"
|
||||
|
||||
#if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
|
||||
#include "../MappingUtils.h"
|
||||
#include "Class.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
std::unordered_map<Object *, void *> __objectMap; // Currently, the value `void*` is always nullptr
|
||||
|
||||
namespace {
|
||||
JSContext *__cx = nullptr;
|
||||
} // namespace
|
||||
|
||||
Object::Object()
|
||||
: _privateObject(nullptr),
|
||||
_cls(nullptr),
|
||||
_finalizeCb(nullptr),
|
||||
_rootCount(0) {
|
||||
_currentVMId = ScriptEngine::getInstance()->getVMId();
|
||||
}
|
||||
|
||||
Object::~Object() {
|
||||
if (_cls == nullptr) {
|
||||
unroot();
|
||||
}
|
||||
|
||||
if (_rootCount > 0) {
|
||||
unprotect();
|
||||
}
|
||||
|
||||
auto iter = __objectMap.find(this);
|
||||
if (iter != __objectMap.end()) {
|
||||
__objectMap.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
bool Object::init(Class *cls, JSObject *obj) {
|
||||
_cls = cls;
|
||||
_heap = obj;
|
||||
|
||||
assert(__objectMap.find(this) == __objectMap.end());
|
||||
__objectMap.emplace(this, nullptr);
|
||||
|
||||
if (_cls == nullptr) {
|
||||
root();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Object *Object::_createJSObject(Class *cls, JSObject *obj) {
|
||||
Object *ret = ccnew Object();
|
||||
if (!ret->init(cls, obj)) {
|
||||
delete ret;
|
||||
ret = nullptr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Object *Object::_createJSObjectForConstructor(Class *cls, const JS::CallArgs &args) {
|
||||
Object *ret = ccnew Object();
|
||||
JS::RootedObject obj(__cx, JS_NewObjectForConstructor(__cx, &cls->_jsCls, args));
|
||||
if (!ret->init(cls, obj)) {
|
||||
delete ret;
|
||||
ret = nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Object *Object::createPlainObject() {
|
||||
Object *obj = Object::_createJSObject(nullptr, JS_NewPlainObject(__cx));
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object *Object::createObjectWithClass(Class *cls) {
|
||||
JS::RootedObject jsobj(__cx);
|
||||
Class::_createJSObjectWithClass(cls, &jsobj);
|
||||
Object *obj = Object::_createJSObject(cls, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object *Object::getObjectWithPtr(void *ptr) {
|
||||
Object *obj = nullptr;
|
||||
auto iter = NativePtrToObjectMap::find(ptr);
|
||||
if (iter != NativePtrToObjectMap::end()) {
|
||||
obj = iter->second;
|
||||
obj->incRef();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object *Object::createArrayObject(size_t length) {
|
||||
JS::RootedObject jsobj(__cx, JS::NewArrayObject(__cx, length));
|
||||
Object *obj = Object::_createJSObject(nullptr, jsobj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object *Object::createArrayBufferObject(const void *data, size_t byteLength) {
|
||||
Object *obj = nullptr;
|
||||
|
||||
if (byteLength > 0 && data != nullptr) {
|
||||
mozilla::UniquePtr<uint8_t[], JS::FreePolicy> jsBuf(
|
||||
js_pod_arena_malloc<uint8_t>(js::ArrayBufferContentsArena, byteLength));
|
||||
if (!jsBuf)
|
||||
return nullptr;
|
||||
|
||||
memcpy(jsBuf.get(), data, byteLength);
|
||||
JS::RootedObject jsobj(__cx, JS::NewArrayBufferWithContents(__cx, byteLength, jsBuf.get()));
|
||||
if (jsobj) {
|
||||
// If JS::NewArrayBufferWithContents returns non-null, the ownership of
|
||||
// the data is transfered to obj, so we release the ownership here.
|
||||
mozilla::Unused << jsBuf.release();
|
||||
|
||||
obj = Object::_createJSObject(nullptr, jsobj);
|
||||
}
|
||||
} else {
|
||||
JS::RootedObject jsobj(__cx, JS::NewArrayBuffer(__cx, byteLength));
|
||||
if (jsobj) {
|
||||
obj = Object::_createJSObject(nullptr, jsobj);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* static */
|
||||
Object *Object::createExternalArrayBufferObject(void *contents, size_t byteLength, BufferContentsFreeFunc freeFunc, void *freeUserData /* = nullptr*/) {
|
||||
struct BackingStoreUserData {
|
||||
BufferContentsFreeFunc freeFunc;
|
||||
void *freeUserData;
|
||||
size_t byteLength;
|
||||
};
|
||||
|
||||
auto *userData = ccnew BackingStoreUserData();
|
||||
userData->freeFunc = freeFunc;
|
||||
userData->freeUserData = freeUserData;
|
||||
userData->byteLength = byteLength;
|
||||
|
||||
Object *obj = nullptr;
|
||||
JS::RootedObject jsobj(__cx, JS::NewExternalArrayBuffer(
|
||||
__cx, byteLength, contents,
|
||||
[](void *data, void *deleterData) {
|
||||
auto *userData = reinterpret_cast<BackingStoreUserData *>(deleterData);
|
||||
userData->freeFunc(data, userData->byteLength, userData->freeUserData);
|
||||
delete userData;
|
||||
},
|
||||
userData));
|
||||
if (jsobj) {
|
||||
obj = Object::_createJSObject(nullptr, jsobj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object *Object::createTypedArray(TypedArrayType type, const void *data, size_t byteLength) {
|
||||
if (type == TypedArrayType::NONE) {
|
||||
SE_LOGE("Don't pass se::Object::TypedArrayType::NONE to createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type == TypedArrayType::UINT8_CLAMPED) {
|
||||
SE_LOGE("Doesn't support to create Uint8ClampedArray with Object::createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define CREATE_TYPEDARRAY(_type_, _data, _byteLength, count) \
|
||||
{ \
|
||||
void *tmpData = nullptr; \
|
||||
JS::RootedObject arr(__cx, JS_New##_type_##Array(__cx, (uint32_t)(count))); \
|
||||
bool isShared = false; \
|
||||
if (_data != nullptr) { \
|
||||
JS::AutoCheckCannotGC nogc; \
|
||||
tmpData = JS_Get##_type_##ArrayData(arr, &isShared, nogc); \
|
||||
memcpy(tmpData, (const void *)_data, (_byteLength)); \
|
||||
} \
|
||||
Object *obj = Object::_createJSObject(nullptr, arr); \
|
||||
return obj; \
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case TypedArrayType::INT8:
|
||||
CREATE_TYPEDARRAY(Int8, data, byteLength, byteLength);
|
||||
case TypedArrayType::INT16:
|
||||
CREATE_TYPEDARRAY(Int16, data, byteLength, byteLength / 2);
|
||||
case TypedArrayType::INT32:
|
||||
CREATE_TYPEDARRAY(Int32, data, byteLength, byteLength / 4);
|
||||
case TypedArrayType::UINT8:
|
||||
CREATE_TYPEDARRAY(Uint8, data, byteLength, byteLength);
|
||||
case TypedArrayType::UINT16:
|
||||
CREATE_TYPEDARRAY(Uint16, data, byteLength, byteLength / 2);
|
||||
case TypedArrayType::UINT32:
|
||||
CREATE_TYPEDARRAY(Uint32, data, byteLength, byteLength / 4);
|
||||
case TypedArrayType::FLOAT32:
|
||||
CREATE_TYPEDARRAY(Float32, data, byteLength, byteLength / 4);
|
||||
case TypedArrayType::FLOAT64:
|
||||
CREATE_TYPEDARRAY(Float64, data, byteLength, byteLength / 8);
|
||||
default:
|
||||
assert(false); // Should never go here.
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
#undef CREATE_TYPEDARRAY
|
||||
}
|
||||
|
||||
/* static */
|
||||
Object *Object::createTypedArrayWithBuffer(TypedArrayType type, const Object *obj) {
|
||||
return Object::createTypedArrayWithBuffer(type, obj, 0);
|
||||
}
|
||||
|
||||
/* static */
|
||||
Object *Object::createTypedArrayWithBuffer(TypedArrayType type, const Object *obj, size_t offset) {
|
||||
size_t byteLength{0};
|
||||
uint8_t *skip{nullptr};
|
||||
obj->getTypedArrayData(&skip, &byteLength);
|
||||
return Object::createTypedArrayWithBuffer(type, obj, offset, byteLength - offset);
|
||||
}
|
||||
|
||||
/* static */
|
||||
Object *Object::createTypedArrayWithBuffer(TypedArrayType type, const Object *obj, size_t offset, size_t byteLength) {
|
||||
if (type == TypedArrayType::NONE) {
|
||||
SE_LOGE("Don't pass se::Object::TypedArrayType::NONE to createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type == TypedArrayType::UINT8_CLAMPED) {
|
||||
SE_LOGE("Doesn't support to create Uint8ClampedArray with Object::createTypedArray API!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
assert(obj->isArrayBuffer());
|
||||
JS::RootedObject jsobj(__cx, obj->_getJSObject());
|
||||
|
||||
switch (type) {
|
||||
case TypedArrayType::INT8: {
|
||||
JS::RootedObject typeArray(__cx, JS_NewInt8ArrayWithBuffer(__cx, jsobj, offset, byteLength));
|
||||
return Object::_createJSObject(nullptr, typeArray);
|
||||
}
|
||||
case TypedArrayType::INT16: {
|
||||
JS::RootedObject typeArray(__cx, JS_NewInt16ArrayWithBuffer(__cx, jsobj, offset, byteLength / 2));
|
||||
return Object::_createJSObject(nullptr, typeArray);
|
||||
}
|
||||
case TypedArrayType::INT32: {
|
||||
JS::RootedObject typeArray(__cx, JS_NewInt32ArrayWithBuffer(__cx, jsobj, offset, byteLength / 4));
|
||||
return Object::_createJSObject(nullptr, typeArray);
|
||||
}
|
||||
case TypedArrayType::UINT8: {
|
||||
JS::RootedObject typeArray(__cx, JS_NewUint8ArrayWithBuffer(__cx, jsobj, offset, byteLength));
|
||||
return Object::_createJSObject(nullptr, typeArray);
|
||||
}
|
||||
case TypedArrayType::UINT16: {
|
||||
JS::RootedObject typeArray(__cx, JS_NewUint16ArrayWithBuffer(__cx, jsobj, offset, byteLength / 2));
|
||||
return Object::_createJSObject(nullptr, typeArray);
|
||||
}
|
||||
case TypedArrayType::UINT32: {
|
||||
JS::RootedObject typeArray(__cx, JS_NewUint32ArrayWithBuffer(__cx, jsobj, offset, byteLength / 4));
|
||||
return Object::_createJSObject(nullptr, typeArray);
|
||||
}
|
||||
case TypedArrayType::FLOAT32: {
|
||||
JS::RootedObject typeArray(__cx, JS_NewFloat32ArrayWithBuffer(__cx, jsobj, offset, byteLength / 4));
|
||||
return Object::_createJSObject(nullptr, typeArray);
|
||||
}
|
||||
case TypedArrayType::FLOAT64: {
|
||||
JS::RootedObject typeArray(__cx, JS_NewFloat64ArrayWithBuffer(__cx, jsobj, offset, byteLength / 8));
|
||||
return Object::_createJSObject(nullptr, typeArray);
|
||||
}
|
||||
default:
|
||||
assert(false); // Should never go here.
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Object *Object::createUint8TypedArray(uint8_t *data, size_t dataCount) {
|
||||
return createTypedArray(TypedArrayType::UINT8, data, dataCount);
|
||||
}
|
||||
|
||||
Object *Object::createJSONObject(const std::string &jsonStr) {
|
||||
Value strVal(jsonStr);
|
||||
JS::RootedValue jsStr(__cx);
|
||||
internal::seToJsValue(__cx, strVal, &jsStr);
|
||||
JS::RootedValue jsObj(__cx);
|
||||
JS::RootedString rootedStr(__cx, jsStr.toString());
|
||||
Object *obj = nullptr;
|
||||
if (JS_ParseJSON(__cx, rootedStr, &jsObj)) {
|
||||
obj = Object::_createJSObject(nullptr, jsObj.toObjectOrNull());
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Object::_setFinalizeCallback(JSFinalizeOp finalizeCb) {
|
||||
_finalizeCb = finalizeCb;
|
||||
}
|
||||
|
||||
bool Object::getProperty(const char *name, Value *data, bool cachePropertyName) {
|
||||
assert(data != nullptr);
|
||||
data->setUndefined();
|
||||
|
||||
JSObject *jsobj = _getJSObject();
|
||||
if (jsobj == nullptr)
|
||||
return false;
|
||||
|
||||
JS::RootedObject object(__cx, jsobj);
|
||||
|
||||
bool found = false;
|
||||
bool ok = JS_HasProperty(__cx, object, name, &found);
|
||||
|
||||
if (!ok || !found) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::RootedValue rcValue(__cx);
|
||||
ok = JS_GetProperty(__cx, object, name, &rcValue);
|
||||
|
||||
if (ok && data) {
|
||||
internal::jsToSeValue(__cx, rcValue, data);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Object::setProperty(const char *name, const Value &v) {
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
|
||||
JS::RootedValue value(__cx);
|
||||
internal::seToJsValue(__cx, v, &value);
|
||||
return JS_SetProperty(__cx, object, name, value);
|
||||
}
|
||||
|
||||
bool Object::defineProperty(const char *name, JSNative getter, JSNative setter) {
|
||||
JS::RootedObject jsObj(__cx, _getJSObject());
|
||||
return JS_DefineProperty(__cx, jsObj, name, getter, setter, JSPROP_ENUMERATE);
|
||||
}
|
||||
|
||||
bool Object::defineOwnProperty(const char *name, const se::Value &value, bool writable, bool enumerable, bool configurable) {
|
||||
JS::RootedObject jsObj(__cx, _getJSObject());
|
||||
JS::RootedValue jsVal(__cx);
|
||||
internal::seToJsValue(__cx, value, &jsVal);
|
||||
|
||||
unsigned attrs = 0;
|
||||
if (!writable) {
|
||||
attrs |= JSPROP_READONLY;
|
||||
}
|
||||
if (enumerable) {
|
||||
attrs |= JSPROP_ENUMERATE;
|
||||
}
|
||||
if (!configurable) {
|
||||
attrs |= JSPROP_PERMANENT;
|
||||
}
|
||||
|
||||
return JS_DefineProperty(__cx, jsObj, name, jsVal, attrs);
|
||||
}
|
||||
|
||||
bool Object::call(const ValueArray &args, Object *thisObject, Value *rval /* = nullptr*/) {
|
||||
assert(isFunction());
|
||||
|
||||
JS::RootedValueVector jsarr(__cx);
|
||||
jsarr.reserve(args.size());
|
||||
internal::seToJsArgs(__cx, args, &jsarr);
|
||||
|
||||
JS::RootedObject contextObject(__cx);
|
||||
if (thisObject != nullptr) {
|
||||
contextObject.set(thisObject->_getJSObject());
|
||||
}
|
||||
|
||||
JSObject *funcObj = _getJSObject();
|
||||
JS::RootedValue func(__cx, JS::ObjectValue(*funcObj));
|
||||
JS::RootedValue rcValue(__cx);
|
||||
|
||||
JSAutoRealm autoRealm(__cx, func.toObjectOrNull());
|
||||
bool ok = JS_CallFunctionValue(__cx, contextObject, func, jsarr, &rcValue);
|
||||
|
||||
if (ok) {
|
||||
if (rval != nullptr)
|
||||
internal::jsToSeValue(__cx, rcValue, rval);
|
||||
} else {
|
||||
se::ScriptEngine::getInstance()->clearException();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Object::defineFunction(const char *funcName, JSNative func) {
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
JSFunction *jsFunc = JS_DefineFunction(__cx, object, funcName, func, 0, JSPROP_ENUMERATE);
|
||||
return jsFunc != nullptr;
|
||||
}
|
||||
|
||||
bool Object::getArrayLength(uint32_t *length) const {
|
||||
assert(length != nullptr);
|
||||
if (!isArray())
|
||||
return false;
|
||||
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
if (JS::GetArrayLength(__cx, object, length))
|
||||
return true;
|
||||
|
||||
*length = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::getArrayElement(uint32_t index, Value *data) const {
|
||||
assert(data != nullptr);
|
||||
if (!isArray())
|
||||
return false;
|
||||
|
||||
JS::RootedObject object(__cx, _getJSObject());
|
||||
JS::RootedValue rcValue(__cx);
|
||||
if (JS_GetElement(__cx, object, index, &rcValue)) {
|
||||
internal::jsToSeValue(__cx, rcValue, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
data->setUndefined();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::setArrayElement(uint32_t index, const Value &data) {
|
||||
if (!isArray())
|
||||
return false;
|
||||
|
||||
JS::RootedValue jsval(__cx);
|
||||
internal::seToJsValue(__cx, data, &jsval);
|
||||
JS::RootedObject thisObj(__cx, _getJSObject());
|
||||
return JS_SetElement(__cx, thisObj, index, jsval);
|
||||
}
|
||||
|
||||
bool Object::isFunction() const {
|
||||
return JS_ObjectIsFunction(_getJSObject());
|
||||
}
|
||||
|
||||
bool Object::_isNativeFunction(JSNative func) const {
|
||||
JSObject *obj = _getJSObject();
|
||||
return JS_ObjectIsFunction(obj) && JS_IsNativeFunction(obj, func);
|
||||
}
|
||||
|
||||
bool Object::isTypedArray() const {
|
||||
return JS_IsTypedArrayObject(_getJSObject());
|
||||
}
|
||||
|
||||
Object::TypedArrayType Object::getTypedArrayType() const {
|
||||
TypedArrayType ret = TypedArrayType::NONE;
|
||||
JSObject *obj = _getJSObject();
|
||||
if (JS_IsInt8Array(obj))
|
||||
ret = TypedArrayType::INT8;
|
||||
else if (JS_IsInt16Array(obj))
|
||||
ret = TypedArrayType::INT16;
|
||||
else if (JS_IsInt32Array(obj))
|
||||
ret = TypedArrayType::INT32;
|
||||
else if (JS_IsUint8Array(obj))
|
||||
ret = TypedArrayType::UINT8;
|
||||
else if (JS_IsUint8ClampedArray(obj))
|
||||
ret = TypedArrayType::UINT8_CLAMPED;
|
||||
else if (JS_IsUint16Array(obj))
|
||||
ret = TypedArrayType::UINT16;
|
||||
else if (JS_IsUint32Array(obj))
|
||||
ret = TypedArrayType::UINT32;
|
||||
else if (JS_IsFloat32Array(obj))
|
||||
ret = TypedArrayType::FLOAT32;
|
||||
else if (JS_IsFloat64Array(obj))
|
||||
ret = TypedArrayType::FLOAT64;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Object::getTypedArrayData(uint8_t **ptr, size_t *length) const {
|
||||
assert(JS_IsArrayBufferViewObject(_getJSObject()));
|
||||
bool isShared = false;
|
||||
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
if (ptr != nullptr) {
|
||||
*ptr = (uint8_t *)JS_GetArrayBufferViewData(_getJSObject(), &isShared, nogc);
|
||||
}
|
||||
|
||||
if (length != nullptr) {
|
||||
*length = JS_GetArrayBufferViewByteLength(_getJSObject());
|
||||
}
|
||||
return (*ptr != nullptr);
|
||||
}
|
||||
|
||||
bool Object::isArray() const {
|
||||
JS::RootedValue value(__cx, JS::ObjectValue(*_getJSObject()));
|
||||
bool isArray = false;
|
||||
return JS::IsArrayObject(__cx, value, &isArray) && isArray;
|
||||
}
|
||||
|
||||
bool Object::isArrayBuffer() const {
|
||||
return JS::IsArrayBufferObject(_getJSObject());
|
||||
}
|
||||
|
||||
bool Object::getArrayBufferData(uint8_t **ptr, size_t *length) const {
|
||||
assert(isArrayBuffer());
|
||||
|
||||
bool isShared = false;
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
if (ptr != nullptr) {
|
||||
*ptr = (uint8_t *)JS::GetArrayBufferData(_getJSObject(), &isShared, nogc);
|
||||
}
|
||||
|
||||
if (length != nullptr) {
|
||||
*length = JS::GetArrayBufferByteLength(_getJSObject());
|
||||
}
|
||||
return (*ptr != nullptr);
|
||||
}
|
||||
|
||||
bool Object::getAllKeys(ccstd::vector<std::string> *allKeys) const {
|
||||
assert(allKeys != nullptr);
|
||||
JS::RootedObject jsobj(__cx, _getJSObject());
|
||||
JS::Rooted<JS::IdVector> props(__cx, JS::IdVector(__cx));
|
||||
if (!JS_Enumerate(__cx, jsobj, &props))
|
||||
return false;
|
||||
|
||||
ccstd::vector<std::string> keys;
|
||||
for (size_t i = 0, length = props.length(); i < length; ++i) {
|
||||
JS::RootedId id(__cx, props[i]);
|
||||
JS::RootedValue keyVal(__cx);
|
||||
JS_IdToValue(__cx, id, &keyVal);
|
||||
|
||||
if (JSID_IS_STRING(id)) {
|
||||
JS::RootedString rootedKeyVal(__cx, keyVal.toString());
|
||||
allKeys->push_back(internal::jsToStdString(__cx, rootedKeyVal));
|
||||
} else if (JSID_IS_INT(id)) {
|
||||
char buf[50] = {0};
|
||||
snprintf(buf, sizeof(buf), "%d", keyVal.toInt32());
|
||||
allKeys->push_back(buf);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Object::setPrivateObject(PrivateObjectBase *data) {
|
||||
assert(_privateObject == nullptr);
|
||||
#if CC_DEBUG
|
||||
//assert(NativePtrToObjectMap::find(data->getRaw()) == NativePtrToObjectMap::end());
|
||||
auto it = NativePtrToObjectMap::find(data->getRaw());
|
||||
if (it != NativePtrToObjectMap::end()) {
|
||||
auto *pri = it->second->getPrivateObject();
|
||||
SE_LOGE("Already exists object %s/[%s], trying to add %s/[%s]\n", pri->getName(), typeid(*pri).name(), data->getName(), typeid(*data).name());
|
||||
#if JSB_TRACK_OBJECT_CREATION
|
||||
SE_LOGE(" previous object created at %s\n", it->second->_objectCreationStackFrame.c_str());
|
||||
#endif
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
JS::RootedObject obj(__cx, _getJSObject());
|
||||
internal::setPrivate(__cx, obj, data, this, &_internalData, _finalizeCb); //TODO(cjh): how to use _internalData?
|
||||
NativePtrToObjectMap::emplace(data->getRaw(), this);
|
||||
_privateObject = data;
|
||||
}
|
||||
|
||||
PrivateObjectBase *Object::getPrivateObject() const {
|
||||
if (_privateObject == nullptr) {
|
||||
JS::RootedObject obj(__cx, _getJSObject());
|
||||
const_cast<Object *>(this)->_privateObject = static_cast<PrivateObjectBase *>(internal::getPrivate(__cx, obj, 0));
|
||||
}
|
||||
return _privateObject;
|
||||
}
|
||||
|
||||
void Object::clearPrivateData(bool clearMapping) {
|
||||
if (_privateObject != nullptr) {
|
||||
if (clearMapping) {
|
||||
NativePtrToObjectMap::erase(_privateObject->getRaw());
|
||||
}
|
||||
JS::RootedObject obj(__cx, _getJSObject());
|
||||
internal::clearPrivate(__cx, obj);
|
||||
delete _privateObject;
|
||||
_privateObject = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Object::setContext(JSContext *cx) {
|
||||
__cx = cx;
|
||||
}
|
||||
|
||||
// static
|
||||
void Object::cleanup() {
|
||||
for (const auto &e : __objectMap) {
|
||||
e.first->reset();
|
||||
}
|
||||
|
||||
ScriptEngine::getInstance()->addAfterCleanupHook([]() {
|
||||
__objectMap.clear();
|
||||
const auto &instance = NativePtrToObjectMap::instance();
|
||||
for (const auto &e : instance) {
|
||||
e.second->decRef();
|
||||
}
|
||||
NativePtrToObjectMap::clear();
|
||||
__cx = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
JSObject *Object::_getJSObject() const {
|
||||
return isRooted() ? _root.get() : _heap.get();
|
||||
}
|
||||
|
||||
void Object::root() {
|
||||
if (_rootCount == 0) {
|
||||
protect();
|
||||
}
|
||||
++_rootCount;
|
||||
}
|
||||
|
||||
void Object::unroot() {
|
||||
if (_rootCount > 0) {
|
||||
--_rootCount;
|
||||
if (_rootCount == 0) {
|
||||
unprotect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Object::protect() {
|
||||
assert(_root == nullptr);
|
||||
assert(_heap != JS::SafelyInitialized<JSObject *>::create());
|
||||
|
||||
_root.init(__cx, _heap);
|
||||
_heap.set(JS::SafelyInitialized<JSObject *>::create());
|
||||
}
|
||||
|
||||
void Object::unprotect() {
|
||||
if (!_root.initialized())
|
||||
return;
|
||||
|
||||
assert(_currentVMId == ScriptEngine::getInstance()->getVMId());
|
||||
assert(_heap == JS::SafelyInitialized<JSObject *>::create());
|
||||
_heap = _root.get();
|
||||
_root.reset();
|
||||
}
|
||||
|
||||
void Object::reset() {
|
||||
_root.reset();
|
||||
_heap = JS::SafelyInitialized<JSObject *>::create();
|
||||
}
|
||||
|
||||
void Object::onTraceCallback(JSTracer *trc, void *data) {
|
||||
auto *thiz = reinterpret_cast<Object *>(data);
|
||||
thiz->trace(trc);
|
||||
}
|
||||
|
||||
/* Tracing makes no sense in the rooted case, because JS::PersistentRooted
|
||||
* already takes care of that. */
|
||||
void Object::trace(JSTracer *tracer) {
|
||||
assert(!isRooted());
|
||||
JS::TraceEdge(tracer, &_heap, "seobj tracing");
|
||||
}
|
||||
|
||||
/* If not tracing, then you must call this method during GC in order to
|
||||
* update the object's location if it was moved, or null it out if it was
|
||||
* finalized. If the object was finalized, returns true. */
|
||||
bool Object::updateAfterGC(JSTracer *trc, void *data) {
|
||||
assert(!isRooted());
|
||||
bool isGarbageCollected = false;
|
||||
internal::PrivateData *internalData = nullptr;
|
||||
|
||||
JSObject *oldPtr = _heap.unbarrieredGet();
|
||||
if (_heap.unbarrieredGet() != nullptr)
|
||||
JS_UpdateWeakPointerAfterGC(trc, &_heap);
|
||||
|
||||
JSObject *newPtr = _heap.unbarrieredGet();
|
||||
|
||||
// IDEA: test to see ggc
|
||||
if (oldPtr != nullptr && newPtr != nullptr) {
|
||||
assert(oldPtr == newPtr);
|
||||
}
|
||||
isGarbageCollected = (newPtr == nullptr);
|
||||
if (isGarbageCollected && internalData != nullptr) {
|
||||
free(internalData);
|
||||
}
|
||||
return isGarbageCollected;
|
||||
}
|
||||
|
||||
bool Object::isRooted() const {
|
||||
return _rootCount > 0;
|
||||
}
|
||||
|
||||
bool Object::strictEquals(Object *o) const {
|
||||
JSObject *thisObj = _getJSObject();
|
||||
JSObject *oThisObj = o->_getJSObject();
|
||||
if ((thisObj == nullptr || oThisObj == nullptr) && thisObj != oThisObj)
|
||||
return false;
|
||||
|
||||
assert(thisObj);
|
||||
assert(oThisObj);
|
||||
JS::RootedValue v1(__cx, JS::ObjectValue(*_getJSObject()));
|
||||
JS::RootedValue v2(__cx, JS::ObjectValue(*o->_getJSObject()));
|
||||
bool same = false;
|
||||
bool ok = JS::SameValue(__cx, v1, v2, &same);
|
||||
return ok && same;
|
||||
}
|
||||
|
||||
bool Object::attachObject(Object *obj) {
|
||||
assert(obj);
|
||||
|
||||
Object *global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal))
|
||||
return false;
|
||||
Object *jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("registerNativeRef", &func))
|
||||
return false;
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::detachObject(Object *obj) {
|
||||
assert(obj);
|
||||
Object *global = ScriptEngine::getInstance()->getGlobalObject();
|
||||
Value jsbVal;
|
||||
if (!global->getProperty("jsb", &jsbVal))
|
||||
return false;
|
||||
Object *jsbObj = jsbVal.toObject();
|
||||
|
||||
Value func;
|
||||
|
||||
if (!jsbObj->getProperty("unregisterNativeRef", &func))
|
||||
return false;
|
||||
|
||||
ValueArray args;
|
||||
args.push_back(Value(this));
|
||||
args.push_back(Value(obj));
|
||||
func.toObject()->call(args, global);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Object::toString() const {
|
||||
std::string ret;
|
||||
if (isFunction() || isArray() || isTypedArray()) {
|
||||
JS::RootedValue val(__cx, JS::ObjectOrNullValue(_getJSObject()));
|
||||
internal::forceConvertJsValueToStdString(__cx, val, &ret);
|
||||
} else if (isArrayBuffer()) {
|
||||
ret = "[object ArrayBuffer]";
|
||||
} else {
|
||||
ret = "[object Object]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace se
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
458
cocos/bindings/jswrapper/sm/Object.h
Normal file
@@ -0,0 +1,458 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
#include "../RefCounter.h"
|
||||
#include "../Value.h"
|
||||
#include "Base.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Class;
|
||||
|
||||
namespace internal {
|
||||
struct PrivateData;
|
||||
}
|
||||
|
||||
/**
|
||||
* se::Object represents JavaScript Object.
|
||||
*/
|
||||
class Object final : public RefCounter {
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a JavaScript Object like `{} or new Object()`.
|
||||
* @return A JavaScript Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object *createPlainObject();
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Array Object like `[] or new Array()`.
|
||||
* @param[in] length The initical length of array.
|
||||
* @return A JavaScript Array Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object *createArrayObject(size_t length);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Typed Array Object with uint8 format from an existing pointer.
|
||||
* @param[in] bytes A pointer to the byte buffer to be used as the backing store of the Typed Array object.
|
||||
* @param[in] byteLength The number of bytes pointed to by the parameter bytes.
|
||||
* @return A JavaScript Typed Array Object whose backing store is the same as the one pointed data, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
* @deprecated This method is deprecated, please use `se::Object::createTypedArray` instead.
|
||||
*/
|
||||
SE_DEPRECATED_ATTRIBUTE static Object *createUint8TypedArray(uint8_t *data, size_t byteLength);
|
||||
|
||||
enum class TypedArrayType {
|
||||
NONE,
|
||||
INT8,
|
||||
INT16,
|
||||
INT32,
|
||||
UINT8,
|
||||
UINT8_CLAMPED,
|
||||
UINT16,
|
||||
UINT32,
|
||||
FLOAT32,
|
||||
FLOAT64
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Typed Array Object with specified format from an existing pointer,
|
||||
if provide a null pointer,then will create a empty JavaScript Typed Array Object.
|
||||
* @param[in] type The format of typed array.
|
||||
* @param[in] data A pointer to the byte buffer to be used as the backing store of the Typed Array object.
|
||||
* @param[in] byteLength The number of bytes pointed to by the parameter bytes.
|
||||
* @return A JavaScript Typed Array Object whose backing store is the same as the one pointed data, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object *createTypedArray(TypedArrayType type, 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 nbytes, 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 std::string &jsonStr);
|
||||
|
||||
/**
|
||||
* @brief Creates a JavaScript Native Binding Object from an existing se::Class instance.
|
||||
* @param[in] cls The se::Class instance which stores native callback informations.
|
||||
* @return A JavaScript Native Binding Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object *createObjectWithClass(Class *cls);
|
||||
|
||||
/**
|
||||
* @brief Gets a se::Object from an existing native object pointer.
|
||||
* @param[in] ptr The native object pointer associated with the se::Object
|
||||
* @return A JavaScript Native Binding Object, or nullptr if there is an error.
|
||||
* @note The return value (non-null) has to be released manually.
|
||||
*/
|
||||
static Object *getObjectWithPtr(void *ptr);
|
||||
|
||||
/**
|
||||
* @brief Gets a property from an object.
|
||||
* @param[in] name A utf-8 string containing the property's name.
|
||||
* @param[out] value The property's value if object has the property, otherwise the undefined value.
|
||||
* @return true if object has the property, otherwise false.
|
||||
*/
|
||||
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 std::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 &value);
|
||||
|
||||
inline bool setProperty(const std::string &name, const Value &value) {
|
||||
return setProperty(name.c_str(), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Defines a property with native accessor callbacks for an object.
|
||||
* @param[in] name A utf-8 string containing the property's name.
|
||||
* @param[in] getter The native callback for getter.
|
||||
* @param[in] setter The native callback for setter.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool defineProperty(const char *name, JSNative getter, JSNative setter);
|
||||
|
||||
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, JSNative func);
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object can be called as a function.
|
||||
* @return true if object can be called as a function, otherwise false.
|
||||
*/
|
||||
bool isFunction() const;
|
||||
|
||||
/**
|
||||
* @brief Calls an object as a function.
|
||||
* @param[in] args A se::Value array of arguments to pass to the function. Pass se::EmptyValueArray if argumentCount is 0.
|
||||
* @param[in] thisObject The object to use as "this," or NULL to use the global object as "this."
|
||||
* @param[out] rval The se::Value that results from calling object as a function, passing nullptr if return value is ignored.
|
||||
* @return true if object is a function and there isn't any errors, otherwise false.
|
||||
*/
|
||||
bool call(const ValueArray &args, Object *thisObject, Value *rval = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object is an array.
|
||||
* @return true if object is an array, otherwise false.
|
||||
*/
|
||||
bool isArray() const;
|
||||
|
||||
/**
|
||||
* @brief Gets array length of an array object.
|
||||
* @param[out] length The array length to be stored. It's set to 0 if there is an error.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getArrayLength(uint32_t *length) const;
|
||||
|
||||
/**
|
||||
* @brief Gets an element from an array object by numeric index.
|
||||
* @param[in] index An integer value for index.
|
||||
* @param[out] data The se::Value to be stored for the element in certain index.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getArrayElement(uint32_t index, Value *data) const;
|
||||
|
||||
/**
|
||||
* @brief Sets an element to an array object by numeric index.
|
||||
* @param[in] index An integer value for index.
|
||||
* @param[in] data The se::Value to be set to array with certain index.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool setArrayElement(uint32_t index, const Value &data);
|
||||
|
||||
/** @brief Tests whether an object is a typed array.
|
||||
* @return true if object is a typed array, otherwise false.
|
||||
*/
|
||||
bool isTypedArray() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the type of a typed array object.
|
||||
* @return The type of a typed array object.
|
||||
*/
|
||||
TypedArrayType getTypedArrayType() const;
|
||||
|
||||
/**
|
||||
* @brief Gets backing store of a typed array object.
|
||||
* @param[out] ptr A temporary pointer to the backing store of a JavaScript Typed Array object.
|
||||
* @param[out] length The byte length of a JavaScript Typed Array object.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getTypedArrayData(uint8_t **ptr, size_t *length) const;
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object is an array buffer object.
|
||||
* @return true if object is an array buffer object, otherwise false.
|
||||
*/
|
||||
bool isArrayBuffer() const;
|
||||
|
||||
/**
|
||||
* @brief Gets buffer data of an array buffer object.
|
||||
* @param[out] ptr A pointer to the data buffer that serves as the backing store for a JavaScript Typed Array object.
|
||||
* @param[out] length The number of bytes in a JavaScript data object.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getArrayBufferData(uint8_t **ptr, size_t *length) const;
|
||||
|
||||
/**
|
||||
* @brief Gets all property names of an object.
|
||||
* @param[out] allKeys A string vector to store all property names.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool getAllKeys(ccstd::vector<std::string> *allKeys) const;
|
||||
|
||||
void setPrivateObject(PrivateObjectBase *data);
|
||||
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 _privateObject ? _privateObject->getRaw() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a pointer to private data on an object.
|
||||
* @param[in] data A void* to set as the object's private data.
|
||||
* @note This method will associate private data with se::Object by std::unordered_map::emplace.
|
||||
* It's used for search a se::Object via a void* private data.
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
inline void setClearMappingInFinalizer(bool v) { _clearMappingInFinalizer = v; }
|
||||
inline bool isClearMappingInFinalizer() const { return _clearMappingInFinalizer; }
|
||||
|
||||
/**
|
||||
* @brief Roots an object from garbage collection.
|
||||
* @note Use this method when you want to store an object in a global or on the heap, where the garbage collector will not be able to discover your reference to it.
|
||||
* An object may be rooted multiple times and must be unrooted an equal number of times before becoming eligible for garbage collection.
|
||||
*/
|
||||
void root();
|
||||
|
||||
/**
|
||||
* @brief Unroots an object from garbage collection.
|
||||
* @note An object may be rooted multiple times and must be unrooted an equal number of times before becoming eligible for garbage collection.
|
||||
*/
|
||||
void unroot();
|
||||
|
||||
/**
|
||||
* @brief Tests whether an object is rooted.
|
||||
* @return true if it has been already rooted, otherwise false.
|
||||
*/
|
||||
bool isRooted() const;
|
||||
|
||||
/**
|
||||
* @brief Tests whether two objects are strict equal, as compared by the JS === operator.
|
||||
* @param[in] o The object to be tested with this object.
|
||||
* @return true if the two values are strict equal, otherwise false.
|
||||
*/
|
||||
bool strictEquals(Object *o) const;
|
||||
|
||||
/**
|
||||
* @brief Attaches an object to current object.
|
||||
* @param[in] obj The object to be attached.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note This method will set `obj` as a property of current object, therefore the lifecycle of child object will depend on current object,
|
||||
* which means `obj` may be garbage collected only after current object is garbage collected.
|
||||
* It's normally used in binding a native callback method. For example:
|
||||
|
||||
```javascript
|
||||
var self = this;
|
||||
someObject.setCallback(function(){}, self);
|
||||
```
|
||||
|
||||
```c++
|
||||
static bool SomeObject_setCallback(se::State& s)
|
||||
{
|
||||
SomeObject* cobj = (SomeObject*)s.nativeThisObject();
|
||||
const auto& args = s.args();
|
||||
size_t argc = args.size();
|
||||
if (argc == 2) {
|
||||
std::function<void()> arg0;
|
||||
do {
|
||||
if (args[0].isObject() && args[0].toObject()->isFunction())
|
||||
{
|
||||
se::Value jsThis(args[1]);
|
||||
se::Value jsFunc(args[0]);
|
||||
|
||||
jsThis.toObject()->attachObject(jsFunc.toObject());
|
||||
|
||||
auto lambda = [=]() -> void {
|
||||
...
|
||||
// Call jsFunc stuff...
|
||||
...
|
||||
};
|
||||
arg0 = lambda;
|
||||
}
|
||||
else
|
||||
{
|
||||
arg0 = nullptr;
|
||||
}
|
||||
} while(false);
|
||||
SE_PRECONDITION2(ok, false, "Error processing arguments");
|
||||
cobj->setCallback(arg0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
```
|
||||
*/
|
||||
bool attachObject(Object *obj);
|
||||
|
||||
/**
|
||||
* @brief Detaches an object from current object.
|
||||
* @param[in] obj The object to be detached.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note The attached object will not be released if current object is not garbage collected.
|
||||
*/
|
||||
bool detachObject(Object *obj);
|
||||
|
||||
/**
|
||||
* @brief Returns the string for describing current object.
|
||||
* @return The string for describing current object.
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
// Private API used in wrapper
|
||||
static Object *_createJSObject(Class *cls, JSObject *obj);
|
||||
static Object *_createJSObjectForConstructor(Class *cls, const JS::CallArgs &args);
|
||||
void _setFinalizeCallback(JSFinalizeOp finalizeCb);
|
||||
bool _isNativeFunction(JSNative func) const;
|
||||
JSObject *_getJSObject() const;
|
||||
Class *_getClass() const { return _cls; }
|
||||
//
|
||||
|
||||
private:
|
||||
Object();
|
||||
bool init(Class *cls, JSObject *obj);
|
||||
virtual ~Object();
|
||||
|
||||
static void setContext(JSContext *cx);
|
||||
static void cleanup();
|
||||
static void onTraceCallback(JSTracer *trc, void *data);
|
||||
|
||||
void trace(JSTracer *tracer);
|
||||
bool updateAfterGC(JSTracer *trc, void *data);
|
||||
|
||||
void protect();
|
||||
void unprotect();
|
||||
void reset();
|
||||
|
||||
JS::Heap<JSObject *> _heap; /* should be untouched if in rooted mode */
|
||||
JS::PersistentRootedObject _root; /* should be null if not in rooted mode */
|
||||
|
||||
PrivateObjectBase *_privateObject{nullptr};
|
||||
internal::PrivateData *_internalData{nullptr};
|
||||
|
||||
Class *_cls{nullptr};
|
||||
JSFinalizeOp _finalizeCb{nullptr};
|
||||
|
||||
uint32_t _rootCount{0};
|
||||
uint32_t _currentVMId{0};
|
||||
|
||||
bool _clearMappingInFinalizer{true};
|
||||
|
||||
friend class ScriptEngine;
|
||||
friend class Class;
|
||||
};
|
||||
|
||||
extern std::unordered_map<Object *, void *> __objectMap; // Currently, the value `void*` is always nullptr
|
||||
|
||||
} // namespace se
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
1150
cocos/bindings/jswrapper/sm/ScriptEngine.cpp
Normal file
347
cocos/bindings/jswrapper/sm/ScriptEngine.h
Normal file
@@ -0,0 +1,347 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
/**
|
||||
* ScriptEngine is a sington which represents a context of JavaScript VM.
|
||||
*/
|
||||
class ScriptEngine final {
|
||||
public:
|
||||
/**
|
||||
* @brief Gets or creates the instance of script engine.
|
||||
* @return The script engine instance.
|
||||
*/
|
||||
static ScriptEngine *getInstance();
|
||||
|
||||
/**
|
||||
* @brief Destroys the instance of script engine.
|
||||
*/
|
||||
static void destroyInstance();
|
||||
|
||||
/**
|
||||
* @brief Gets the global object of JavaScript VM.
|
||||
* @return The se::Object stores the global JavaScript object.
|
||||
*/
|
||||
Object *getGlobalObject();
|
||||
|
||||
typedef bool (*RegisterCallback)(Object *);
|
||||
|
||||
/**
|
||||
* @brief Adds a callback for registering a native binding module.
|
||||
* @param[in] cb A callback for registering a native binding module.
|
||||
* @note This method just add a callback to a vector, callbacks is invoked in `start` method.
|
||||
*/
|
||||
void addRegisterCallback(RegisterCallback cb);
|
||||
|
||||
/**
|
||||
* @brief 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 Initializes script engine.
|
||||
* @return true if succeed, otherwise false.
|
||||
* @note This method will create JavaScript context and global object.
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function before initializing script engine.
|
||||
* @param[in] hook A hook function to be invoked before initializing script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addBeforeInitHook(const std::function<void()> &hook);
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function after initializing script engine.
|
||||
* @param[in] hook A hook function to be invoked before initializing script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addAfterInitHook(const std::function<void()> &hook);
|
||||
|
||||
/**
|
||||
* @brief Cleanups script engine.
|
||||
* @note This method will removes all objects in JavaScript VM even whose are rooted, then shutdown JavaScript VMf.
|
||||
*/
|
||||
void cleanup();
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function before cleanuping script engine.
|
||||
* @param[in] hook A hook function to be invoked before cleanuping script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addBeforeCleanupHook(const std::function<void()> &hook);
|
||||
|
||||
/**
|
||||
* @brief Adds a hook function after cleanuping script engine.
|
||||
* @param[in] hook A hook function to be invoked after cleanuping script engine.
|
||||
* @note Multiple hook functions could be added, they will be invoked by the order of adding.
|
||||
*/
|
||||
void addAfterCleanupHook(const std::function<void()> &hook);
|
||||
|
||||
/**
|
||||
* @brief Executes a utf-8 string buffer which contains JavaScript code.
|
||||
* @param[in] scriptStr A utf-8 string buffer, if it isn't null-terminated, parameter `length` should be assigned and > 0.
|
||||
* @param[in] length The length of parameter `scriptStr`, it will be set to string length internally if passing < 0 and parameter `scriptStr` is null-terminated.
|
||||
* @param[in] rval The se::Value that results from evaluating script. Passing nullptr if you don't care about the result.
|
||||
* @param[in] fileName A string containing a URL for the script's source file. This is used by debuggers and when reporting exceptions. Pass NULL if you do not care to include source file information.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool evalString(const char *scriptStr, ssize_t length = -1, Value *rval = nullptr, const char *fileName = nullptr);
|
||||
|
||||
/**
|
||||
* @brief 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 std::string &path, const std::string &pathBc) {
|
||||
assert(false);
|
||||
return false;
|
||||
} //cjh
|
||||
|
||||
/**
|
||||
* @brief Grab a snapshot of the current JavaScript execution stack.
|
||||
* @return current stack trace string
|
||||
*/
|
||||
std::string getCurrentStackTrace() { return ""; } //cjh
|
||||
|
||||
/**
|
||||
* Delegate class for file operation
|
||||
*/
|
||||
class FileOperationDelegate {
|
||||
public:
|
||||
FileOperationDelegate()
|
||||
: onGetDataFromFile(nullptr),
|
||||
onGetStringFromFile(nullptr),
|
||||
onCheckFileExist(nullptr),
|
||||
onGetFullPath(nullptr) {}
|
||||
|
||||
bool isValid() const {
|
||||
return onGetDataFromFile != nullptr && onGetStringFromFile != nullptr && onCheckFileExist != nullptr && onGetFullPath != nullptr;
|
||||
}
|
||||
|
||||
// path, buffer, buffer size
|
||||
std::function<void(const std::string &, const std::function<void(const uint8_t *, size_t)> &)> onGetDataFromFile;
|
||||
// path, return file string content.
|
||||
std::function<std::string(const std::string &)> onGetStringFromFile;
|
||||
// path
|
||||
std::function<bool(const std::string &)> onCheckFileExist;
|
||||
// path, return full path
|
||||
std::function<std::string(const std::string &)> onGetFullPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Sets the delegate for file operation.
|
||||
* @param delegate[in] The delegate instance for file operation.
|
||||
*/
|
||||
void setFileOperationDelegate(const FileOperationDelegate &delegate);
|
||||
|
||||
/**
|
||||
* @brief Gets the delegate for file operation.
|
||||
* @return The delegate for file operation
|
||||
*/
|
||||
const FileOperationDelegate &getFileOperationDelegate() const;
|
||||
|
||||
/**
|
||||
* @brief Executes a file which contains JavaScript code.
|
||||
* @param[in] path Script file path.
|
||||
* @param[in] rval The se::Value that results from evaluating script. Passing nullptr if you don't care about the result.
|
||||
* @return true if succeed, otherwise false.
|
||||
*/
|
||||
bool runScript(const std::string &path, Value *rval = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Tests whether script engine is doing garbage collection.
|
||||
* @return true if it's in garbage collection, otherwise false.
|
||||
*/
|
||||
bool isGarbageCollecting();
|
||||
|
||||
/**
|
||||
* @brief Performs a JavaScript garbage collection.
|
||||
*/
|
||||
void garbageCollect() { JS_GC(_cx); }
|
||||
|
||||
/**
|
||||
* @brief Tests whether script engine is being cleaned up.
|
||||
* @return true if it's in cleaning up, otherwise false.
|
||||
*/
|
||||
bool isInCleanup() { return _isInCleanup; }
|
||||
|
||||
/**
|
||||
* @brief Tests whether script engine is valid.
|
||||
* @return true if it's valid, otherwise false.
|
||||
*/
|
||||
bool isValid() { return _isValid; }
|
||||
|
||||
/**
|
||||
* @brief Throw JS exception
|
||||
*/
|
||||
void throwException(const std::string &errorMessage) { assert(false); } //cjh
|
||||
|
||||
/**
|
||||
* @brief Clears all exceptions.
|
||||
*/
|
||||
void clearException();
|
||||
|
||||
using ExceptionCallback = std::function<void(const char *, const char *, const char *)>; // location, message, stack
|
||||
|
||||
/**
|
||||
* @brief Sets the callback function while an exception is fired.
|
||||
* @param[in] cb The callback function to notify that an exception is fired.
|
||||
*/
|
||||
void setExceptionCallback(const ExceptionCallback &cb);
|
||||
|
||||
/**
|
||||
* @brief Sets the callback function while an exception is fired in JS.
|
||||
* @param[in] cb The callback function to notify that an exception is fired.
|
||||
*/
|
||||
void setJSExceptionCallback(const ExceptionCallback &cb);
|
||||
|
||||
/**
|
||||
* @brief Gets the start time of script engine.
|
||||
* @return The start time of script engine.
|
||||
*/
|
||||
const std::chrono::steady_clock::time_point &getStartTime() const { return _startTime; }
|
||||
|
||||
/**
|
||||
* @brief Enables JavaScript debugger
|
||||
* @param[in] serverAddr The address of debugger server.
|
||||
* @param[in] port The port of debugger server will use.
|
||||
* @param[in] isWait Whether wait debugger attach when loading.
|
||||
*/
|
||||
void enableDebugger(const std::string &serverAddr, uint32_t port, bool isWait = false);
|
||||
|
||||
/**
|
||||
* @brief Tests whether JavaScript debugger is enabled
|
||||
* @return true if JavaScript debugger is enabled, otherwise false.
|
||||
*/
|
||||
bool isDebuggerEnabled() const;
|
||||
|
||||
/**
|
||||
* @brief Main loop update trigger, it's need to invoked in main thread every frame.
|
||||
*/
|
||||
void mainLoopUpdate();
|
||||
|
||||
/**
|
||||
* @brief Gets script virtual machine instance ID. Default value is 1, increase by 1 if `init` is invoked.
|
||||
*/
|
||||
uint32_t getVMId() const { return _vmId; }
|
||||
|
||||
/**
|
||||
* @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
|
||||
JSContext *_getContext() { return _cx; }
|
||||
void _setGarbageCollecting(bool isGarbageCollecting);
|
||||
void _debugProcessInput(const std::string &str);
|
||||
//
|
||||
private:
|
||||
ScriptEngine();
|
||||
~ScriptEngine();
|
||||
|
||||
static void onWeakPointerCompartmentCallback(JSTracer *trc, JS::Compartment *comp, void *data);
|
||||
static void onWeakPointerZoneGroupCallback(JSTracer *trc, void *data);
|
||||
|
||||
bool getScript(const std::string &path, JS::MutableHandleScript script);
|
||||
bool compileScript(const std::string &path, JS::MutableHandleScript script);
|
||||
|
||||
JSContext *_cx;
|
||||
JS::Realm *_oldCompartment;
|
||||
|
||||
Object *_globalObj;
|
||||
Object *_debugGlobalObj;
|
||||
|
||||
FileOperationDelegate _fileOperationDelegate;
|
||||
|
||||
ccstd::vector<RegisterCallback> _registerCallbackArray;
|
||||
ccstd::vector<RegisterCallback> _permRegisterCallbackArray;
|
||||
std::chrono::steady_clock::time_point _startTime;
|
||||
|
||||
ccstd::vector<std::function<void()>> _beforeInitHookArray;
|
||||
ccstd::vector<std::function<void()>> _afterInitHookArray;
|
||||
|
||||
ccstd::vector<std::function<void()>> _beforeCleanupHookArray;
|
||||
ccstd::vector<std::function<void()>> _afterCleanupHookArray;
|
||||
|
||||
ExceptionCallback _exceptionCallback;
|
||||
// name ~> JSScript map
|
||||
std::unordered_map<std::string, JS::PersistentRootedScript *> _filenameScriptMap;
|
||||
|
||||
std::string _debuggerServerAddr;
|
||||
uint32_t _debuggerServerPort;
|
||||
|
||||
uint32_t _vmId;
|
||||
|
||||
bool _isGarbageCollecting;
|
||||
bool _isValid;
|
||||
bool _isInCleanup;
|
||||
bool _isErrorHandleWorking;
|
||||
};
|
||||
|
||||
} // namespace se
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
34
cocos/bindings/jswrapper/sm/SeApi.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/****************************************************************************
|
||||
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 "../State.h"
|
||||
#include "../Value.h"
|
||||
#include "Class.h"
|
||||
#include "HelperMacros.h"
|
||||
#include "Object.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "Utils.h"
|
||||
207
cocos/bindings/jswrapper/sm/Utils.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
#include "Class.h"
|
||||
#include "Object.h"
|
||||
#include "ScriptEngine.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
namespace internal {
|
||||
|
||||
void *SE_JS_GetPrivate(JSObject *obj, uint32_t slot) {
|
||||
assert(slot >= 0 && slot < 2);
|
||||
const auto &v = JS::GetReservedSlot(obj, slot);
|
||||
return v.isNullOrUndefined() ? nullptr : v.toPrivate();
|
||||
}
|
||||
|
||||
void SE_JS_SetPrivate(JSObject *obj, uint32_t slot, void *data) {
|
||||
assert(slot >= 0 && slot < 2);
|
||||
JS::SetReservedSlot(obj, slot, JS::PrivateValue(data));
|
||||
}
|
||||
|
||||
bool isJSBClass(JSObject *obj) {
|
||||
const JSClass *cls = JS::GetClass(obj);
|
||||
return (cls->flags & (JSCLASS_HAS_RESERVED_SLOTS(2)) && (cls->flags & JSCLASS_USERBIT1));
|
||||
}
|
||||
|
||||
void forceConvertJsValueToStdString(JSContext *cx, JS::HandleValue jsval, std::string *ret) {
|
||||
assert(ret != nullptr);
|
||||
JS::RootedString jsStr(cx, JS::ToString(cx, jsval));
|
||||
*ret = jsToStdString(cx, jsStr);
|
||||
}
|
||||
|
||||
std::string jsToStdString(JSContext *cx, JS::HandleString jsStr) {
|
||||
JS::UniqueChars str = JS_EncodeStringToUTF8(cx, jsStr);
|
||||
std::string ret(str.get());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jsToSeArgs(JSContext *cx, int argc, const JS::CallArgs &argv, ValueArray &outArr) {
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
jsToSeValue(cx, argv[i], &outArr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsArgs(JSContext *cx, const ValueArray &args, JS::RootedValueVector *outArr) {
|
||||
for (const auto &arg : args) {
|
||||
JS::RootedValue v(cx);
|
||||
seToJsValue(cx, arg, &v);
|
||||
outArr->append(v);
|
||||
}
|
||||
}
|
||||
|
||||
void seToJsValue(JSContext *cx, const Value &arg, JS::MutableHandleValue outVal) {
|
||||
switch (arg.getType()) {
|
||||
case Value::Type::Number: {
|
||||
JS::RootedValue value(cx);
|
||||
value.setDouble(arg.toNumber());
|
||||
outVal.set(value);
|
||||
} break;
|
||||
|
||||
case Value::Type::String: {
|
||||
JS::UTF8Chars utf8Str(arg.toString().c_str(), arg.toString().length());
|
||||
JSString *string = JS_NewStringCopyUTF8N(cx, utf8Str);
|
||||
JS::RootedValue value(cx);
|
||||
value.setString(string);
|
||||
outVal.set(value);
|
||||
} break;
|
||||
|
||||
case Value::Type::Boolean: {
|
||||
JS::RootedValue value(cx);
|
||||
value.setBoolean(arg.toBoolean());
|
||||
outVal.set(value);
|
||||
} break;
|
||||
|
||||
case Value::Type::Object: {
|
||||
JS::RootedValue value(cx, JS::ObjectValue(*arg.toObject()->_getJSObject()));
|
||||
outVal.set(value);
|
||||
} break;
|
||||
|
||||
case Value::Type::Null: {
|
||||
JS::RootedValue value(cx);
|
||||
value.setNull();
|
||||
outVal.set(value);
|
||||
} break;
|
||||
|
||||
case Value::Type::Undefined: {
|
||||
JS::RootedValue value(cx);
|
||||
value.setUndefined();
|
||||
outVal.set(value);
|
||||
} break;
|
||||
case Value::Type::BigInt: {
|
||||
JS::RootedValue value(cx);
|
||||
JS::BigInt *bi = JS::NumberToBigInt(cx, arg.toUint64());
|
||||
outVal.setBigInt(bi);
|
||||
} break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void jsToSeValue(JSContext *cx, JS::HandleValue jsval, Value *v) {
|
||||
if (jsval.isNumber()) {
|
||||
v->setNumber(jsval.toNumber());
|
||||
} else if (jsval.isString()) {
|
||||
JS::RootedString jsstr(cx, jsval.toString());
|
||||
v->setString(jsToStdString(cx, jsstr));
|
||||
} else if (jsval.isBoolean()) {
|
||||
v->setBoolean(jsval.toBoolean());
|
||||
} else if (jsval.isObject()) {
|
||||
Object *object = nullptr;
|
||||
|
||||
JS::RootedObject jsobj(cx, jsval.toObjectOrNull());
|
||||
PrivateObjectBase *privateObject = static_cast<PrivateObjectBase *>(internal::getPrivate(cx, jsobj, 0));
|
||||
void *nativeObj = privateObject ? privateObject->getRaw() : nullptr;
|
||||
bool needRoot = false;
|
||||
if (nativeObj != nullptr) {
|
||||
object = Object::getObjectWithPtr(nativeObj);
|
||||
}
|
||||
|
||||
if (object == nullptr) {
|
||||
object = Object::_createJSObject(nullptr, jsobj);
|
||||
needRoot = true;
|
||||
}
|
||||
v->setObject(object, needRoot);
|
||||
object->decRef();
|
||||
} else if (jsval.isNull()) {
|
||||
v->setNull();
|
||||
} else if (jsval.isUndefined()) {
|
||||
v->setUndefined();
|
||||
} else if (jsval.isBigInt()) {
|
||||
v->setUint64(JS::ToBigUint64(jsval.toBigInt()));
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void setReturnValue(JSContext *cx, const Value &data, const JS::CallArgs &argv) {
|
||||
JS::RootedValue rval(cx);
|
||||
seToJsValue(cx, data, &rval);
|
||||
argv.rval().set(rval);
|
||||
}
|
||||
|
||||
bool hasPrivate(JSContext *cx, JS::HandleObject obj) {
|
||||
return isJSBClass(obj);
|
||||
}
|
||||
|
||||
void *getPrivate(JSContext *cx, JS::HandleObject obj, uint32_t slot) {
|
||||
bool found = isJSBClass(obj);
|
||||
if (found) {
|
||||
return SE_JS_GetPrivate(obj, slot);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setPrivate(JSContext *cx, JS::HandleObject obj, PrivateObjectBase *data, Object *seObj, PrivateData **outInternalData, JSFinalizeOp finalizeCb) {
|
||||
bool found = isJSBClass(obj);
|
||||
assert(found);
|
||||
if (found) {
|
||||
SE_JS_SetPrivate(obj, 0, data);
|
||||
SE_JS_SetPrivate(obj, 1, seObj);
|
||||
if (outInternalData != nullptr) {
|
||||
*outInternalData = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clearPrivate(JSContext *cx, JS::HandleObject obj) {
|
||||
bool found = isJSBClass(obj);
|
||||
if (found) {
|
||||
SE_JS_SetPrivate(obj, 0, nullptr);
|
||||
SE_JS_SetPrivate(obj, 1, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace se
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
70
cocos/bindings/jswrapper/sm/Utils.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/****************************************************************************
|
||||
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_SM
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
#include "../Value.h"
|
||||
|
||||
namespace se {
|
||||
|
||||
class Class;
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct PrivateData {
|
||||
PrivateObjectBase *data{nullptr};
|
||||
Object *seObj{nullptr};
|
||||
JSFinalizeOp finalizeCb{nullptr};
|
||||
};
|
||||
|
||||
void forceConvertJsValueToStdString(JSContext *cx, JS::HandleValue jsval, std::string *ret);
|
||||
std::string jsToStdString(JSContext *cx, JS::HandleString jsStr);
|
||||
|
||||
void jsToSeArgs(JSContext *cx, int argc, const JS::CallArgs &argv, ValueArray &outArr);
|
||||
void jsToSeValue(JSContext *cx, JS::HandleValue jsval, Value *v);
|
||||
void seToJsArgs(JSContext *cx, const ValueArray &args, JS::RootedValueVector *outArr);
|
||||
void seToJsValue(JSContext *cx, const Value &v, JS::MutableHandleValue outVal);
|
||||
|
||||
void setReturnValue(JSContext *cx, const Value &data, const JS::CallArgs &argv);
|
||||
|
||||
bool hasPrivate(JSContext *cx, JS::HandleObject obj);
|
||||
void *getPrivate(JSContext *cx, JS::HandleObject obj, uint32_t slot);
|
||||
void setPrivate(JSContext *cx, JS::HandleObject obj, PrivateObjectBase *data, Object *seObj, PrivateData **outInternalData, JSFinalizeOp finalizeCb);
|
||||
void clearPrivate(JSContext *cx, JS::HandleObject obj);
|
||||
|
||||
void *SE_JS_GetPrivate(JSObject *obj, uint32_t slot);
|
||||
void SE_JS_SetPrivate(JSObject *obj, uint32_t slot, void *data);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace se
|
||||
|
||||
#endif // #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM
|
||||
49
cocos/bindings/jswrapper/v8/Base.h
Normal 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
|
||||
328
cocos/bindings/jswrapper/v8/Class.cpp
Normal 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
|
||||
173
cocos/bindings/jswrapper/v8/Class.h
Normal 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
|
||||
181
cocos/bindings/jswrapper/v8/HelperMacros.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
211
cocos/bindings/jswrapper/v8/HelperMacros.h
Normal 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
|
||||
40
cocos/bindings/jswrapper/v8/MissingSymbols.cpp
Normal 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
|
||||
31
cocos/bindings/jswrapper/v8/MissingSymbols.h
Normal 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"
|
||||
1128
cocos/bindings/jswrapper/v8/Object.cpp
Normal file
706
cocos/bindings/jswrapper/v8/Object.h
Normal 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
|
||||
151
cocos/bindings/jswrapper/v8/ObjectWrap.cpp
Normal 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
|
||||
88
cocos/bindings/jswrapper/v8/ObjectWrap.h
Normal 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
|
||||
2
cocos/bindings/jswrapper/v8/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
V8 inspector from node commit (4e8bc7181c1f2491e187477798d433a4488f43d4)
|
||||
|
||||
1254
cocos/bindings/jswrapper/v8/ScriptEngine.cpp
Normal file
441
cocos/bindings/jswrapper/v8/ScriptEngine.h
Normal 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
|
||||
32
cocos/bindings/jswrapper/v8/SeApi.h
Normal 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"
|
||||
249
cocos/bindings/jswrapper/v8/Utils.cpp
Normal 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
|
||||
56
cocos/bindings/jswrapper/v8/Utils.h
Normal 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
|
||||
597
cocos/bindings/jswrapper/v8/debugger/SHA1.cpp
Normal 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
|
||||
69
cocos/bindings/jswrapper/v8/debugger/SHA1.h
Normal 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
|
||||
188
cocos/bindings/jswrapper/v8/debugger/base64.h
Normal 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_
|
||||
117
cocos/bindings/jswrapper/v8/debugger/env.cpp
Normal 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
|
||||
618
cocos/bindings/jswrapper/v8/debugger/env.h
Normal 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
|
||||
2409
cocos/bindings/jswrapper/v8/debugger/http_parser.cpp
Normal file
353
cocos/bindings/jswrapper/v8/debugger/http_parser.h
Normal 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
|
||||
822
cocos/bindings/jswrapper/v8/debugger/inspector_agent.cpp
Normal 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
|
||||
116
cocos/bindings/jswrapper/v8/debugger/inspector_agent.h
Normal 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_
|
||||
533
cocos/bindings/jswrapper/v8/debugger/inspector_io.cpp
Normal 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
|
||||
181
cocos/bindings/jswrapper/v8/debugger/inspector_io.h
Normal 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_
|
||||
643
cocos/bindings/jswrapper/v8/debugger/inspector_socket.cpp
Normal 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
|
||||
109
cocos/bindings/jswrapper/v8/debugger/inspector_socket.h
Normal 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_
|
||||
700
cocos/bindings/jswrapper/v8/debugger/inspector_socket_server.cpp
Normal 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
|
||||
105
cocos/bindings/jswrapper/v8/debugger/inspector_socket_server.h
Normal 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_
|
||||
1118
cocos/bindings/jswrapper/v8/debugger/node.cpp
Normal file
160
cocos/bindings/jswrapper/v8/debugger/node.h
Normal 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
|
||||
141
cocos/bindings/jswrapper/v8/debugger/node_debug_options.cpp
Normal 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
|
||||
45
cocos/bindings/jswrapper/v8/debugger/node_debug_options.h
Normal 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_
|
||||