/* * Tencent is pleased to support the open source community by making ScriptX available. * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include "QjsHelper.hpp" namespace script { namespace qjs_backend { struct QjsEngine::BookKeepFetcher { template static ::script::internal::GlobalWeakBookkeeping* get(const T* ref) { if (!ref) return nullptr; auto& val = ref->val_; if (!val.engine_) return nullptr; return &val.engine_->globalWeakBookkeeping_; } template static ::script::internal::GlobalWeakBookkeeping::HandleType& handle(const T* ref) { auto& val = const_cast(ref)->val_; return val.handle_; } }; struct QjsBookKeepFetcher : QjsEngine::BookKeepFetcher {}; using BookKeep = ::script::internal::GlobalWeakBookkeeping::Helper; inline GlobalRefState& GlobalRefState::operator=(const GlobalRefState& assign) { assert(this != &assign); bool wasEmpty = isEmpty(); if (!wasEmpty) { qjs_backend::freeValue(ref_, engine_->context_); } ref_ = assign.ref_; engine_ = assign.engine_; if (engine_) qjs_backend::dupValue(ref_, engine_->context_); return *this; } inline GlobalRefState& GlobalRefState::operator=(GlobalRefState&& move) noexcept { assert(this != &move); bool wasEmpty = isEmpty(); if (!wasEmpty) { qjs_backend::freeValue(ref_, engine_->context_); } ref_ = move.ref_; engine_ = move.engine_; move.ref_ = JS_UNDEFINED; move.engine_ = nullptr; return *this; } inline bool GlobalRefState::isEmpty() const { return engine_ == nullptr; } template void GlobalRefState::reset(GlobalOrWeak* thiz) { if (!isEmpty()) { qjs_backend::freeValue(ref_, engine_->context_); qjs_backend::BookKeep::remove(thiz); ref_ = JS_UNDEFINED; engine_ = nullptr; } } inline void GlobalRefState::swap(GlobalRefState& other) { std::swap(ref_, other.ref_); std::swap(engine_, other.engine_); } template void GlobalRefState::dtor(GlobalOrWeak* thiz) { if (!isEmpty()) { EngineScope scope(engine_); reset(thiz); } } } // namespace qjs_backend template Global::Global() noexcept : val_() {} template Global::Global(const script::Local& localReference) : val_() { val_.ref_ = localReference.val_; val_.engine_ = &qjs_backend::currentEngine(); qjs_backend::dupValue(val_.ref_, val_.engine_->context_); qjs_backend::BookKeep::keep(this); } template Global::Global(const script::Weak& weak) : Global(::script::converter::Converter>::toCpp(weak.getValue())) {} template Global::Global(const script::Global& copy) : val_() { *this = copy; } template Global::Global(script::Global&& move) noexcept : val_() { *this = std::move(move); } template Global::~Global() { val_.dtor(this); } template Global& Global::operator=(const script::Global& assign) { if (this != &assign) { bool wasEmpty = isEmpty(); val_ = assign.val_; qjs_backend::BookKeep::afterCopy(wasEmpty, this, &assign); } return *this; } template Global& Global::operator=(script::Global&& move) noexcept { if (this != &move) { bool wasEmpty = isEmpty(); val_ = std::move(move.val_); qjs_backend::BookKeep::afterMove(wasEmpty, this, &move); } return *this; } template void Global::swap(Global& rhs) noexcept { val_.swap(rhs.val_); qjs_backend::BookKeep::afterSwap(this, &rhs); } template Global& Global::operator=(const script::Local& assign) { *this = Global(assign); return *this; } template Local Global::get() const { if (isEmpty()) throw Exception("get on empty Global"); return qjs_interop::makeLocal(qjs_backend::dupValue(val_.ref_, val_.engine_->context_)); } template Local Global::getValue() const { if (isEmpty()) return {}; return qjs_interop::makeLocal(qjs_backend::dupValue(val_.ref_, val_.engine_->context_)); } template bool Global::isEmpty() const { return val_.isEmpty(); } template void Global::reset() { val_.reset(this); } // == Weak == template Weak::Weak() noexcept : val_() {} template Weak::~Weak() { val_.dtor(this); } template Weak::Weak(const script::Local& localReference) : val_() { auto ref = qjs_interop::peekLocal(localReference); val_.engine_ = &qjs_backend::currentEngine(); // if has our patch, use the real WeakRef impl. #ifdef QUICK_JS_HAS_SCRIPTX_PATCH if (JS_IsObject(ref)) { val_.ref_ = JS_NewWeakRef(val_.engine_->context_, ref); qjs_backend::checkException(val_.ref_); qjs_backend::BookKeep::keep(this); return; } #endif val_.ref_ = qjs_backend::dupValue(ref, val_.engine_->context_); qjs_backend::BookKeep::keep(this); } template Weak::Weak(const script::Global& globalReference) : Weak(::script::converter::Converter>::toCpp(globalReference.getValue())) {} template Weak::Weak(const script::Weak& copy) : val_() { *this = copy; } template Weak::Weak(script::Weak&& move) noexcept : val_() { *this = std::move(move); } template Weak& Weak::operator=(const script::Weak& assign) { if (this != &assign) { bool wasEmpty = isEmpty(); val_ = assign.val_; qjs_backend::BookKeep::afterCopy(wasEmpty, this, &assign); } return *this; } template Weak& Weak::operator=(script::Weak&& move) noexcept { if (this != &move) { bool wasEmpty = isEmpty(); val_ = std::move(move.val_); qjs_backend::BookKeep::afterMove(wasEmpty, this, &move); } return *this; } template void Weak::swap(Weak& rhs) noexcept { val_.swap(rhs.val_); qjs_backend::BookKeep::afterSwap(this, &rhs); } template Weak& Weak::operator=(const script::Local& assign) { *this = Weak(assign); return *this; } template Local Weak::get() const { auto value = getValue(); if (value.isNull()) throw Exception("get on empty Weak"); return converter::Converter>::toCpp(value); } template Local Weak::getValue() const { if (isEmpty()) return {}; #ifdef QUICK_JS_HAS_SCRIPTX_PATCH if (JS_IsObject(val_.ref_)) { auto ref = JS_GetWeakRef(val_.engine_->context_, val_.ref_); qjs_backend::checkException(ref); return qjs_interop::makeLocal(ref); } #endif return qjs_interop::makeLocal(qjs_backend::dupValue(val_.ref_, val_.engine_->context_)); } template bool Weak::isEmpty() const { return val_.isEmpty(); } template void Weak::reset() noexcept { val_.reset(this); } } // namespace script