/* * 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 "../../src/Engine.h" #include "../../src/Native.h" #include "../../src/Reference.h" #include "../../src/Scope.h" #include "../../src/Value.h" #include "../../src/utils/GlobalWeakBookkeeping.hpp" #include "V8Helper.h" #include "V8Platform.h" namespace script::v8_backend { class InspectorClient; class V8Engine : public ::script::ScriptEngine { struct ManagedObject { V8Engine* engine; void* data; std::function cleanupFunc; }; struct ThreadGlobalScope { EngineScope scope_; explicit ThreadGlobalScope(V8Engine* engine) : scope_(EngineScope::InternalEnterEngine{}, engine, false) {} }; private: bool isOwnIsolate_ = true; // used only for node addon std::unique_ptr threadGlobalScope_ = nullptr; std::unordered_map> nativeRegistry_; std::shared_ptr v8Platform_; std::unique_ptr allocator_; std::shared_ptr<::script::utils::MessageQueue> messageQueue_; // V8 don't do gc on Isolate::Dispose, // so we must got a way to manage native object. // key: native pointer // value: v8 weak std::unordered_map> managedObject_; std::unordered_map> keptObject_; size_t keptObjectId_ = 0; bool isDestroying_ = false; internal::GlobalWeakBookkeeping globalWeakBookkeeping_; // create a slave engine explicit V8Engine(V8Engine* masterEngine); protected: v8::Isolate* isolate_; v8::Global context_; v8::Global internalStoreSymbol_; v8::Global constructorMarkSymbol_; explicit V8Engine(std::shared_ptr messageQueue, const std::function& isolateFactory); ~V8Engine() override; public: explicit V8Engine(std::shared_ptr messageQueue = {}); /** * Create a ScriptEngine instance wrapping existing v8 instance, especially for NODE JS addons. * After creating such instance, there will be a global EngineScope for CURRENT THREAD, until you * destroy the engine (controlled by the param addGlobalEngineScope, which is true by default). * Note: when destroying such ScriptEngine instance, the v8 isolate/context * are not destroyed, only ScriptEngine related resources. * * Note: V8 has it's on event-loop, especially for node.js, so you may ignore messageQueue. * Note: BUT, if you post message to ScriptEngine::messageQueue(), you still need to schedule * MessageQueue::loop from time to time. */ explicit V8Engine(std::shared_ptr messageQueue, v8::Isolate* isolate, v8::Local context, bool addGlobalEngineScope = true); void destroy() noexcept override; bool isDestroying() const override; Local get(const Local& key) override; Local getGlobal(); void set(const Local& key, const Local& value) override; using ScriptEngine::set; Local eval(const Local& script, const Local& sourceFile) override; Local eval(const Local& script) override; using ScriptEngine::eval; /** * Create a new V8 Engine that share the same isolate, but with different context. * Caller own the returned pointer, and the returned instance * should be deleted BEFORE this instance. * * and the message queue from a slave engine is the same as its master. */ UniqueEnginePtr newSlaveEngine(); std::shared_ptr<::script::utils::MessageQueue> messageQueue() override; void gc() override; size_t getHeapSize() override; void adjustAssociatedMemory(int64_t count) override; ScriptLanguage getLanguageType() override; std::string getEngineVersion() override; private: void initContext(); Local eval(const Local& script, const Local& sourceFile); template void registerNativeClassImpl(const ClassDefine* classDefine); template Local newNativeClassImpl(const ClassDefine* classDefine, size_t argc, const Local* args); template bool isInstanceOfImpl(const Local& value, const ClassDefine* classDefine); template T* getNativeInstanceImpl(const Local& value, const ClassDefine* classDefine); template v8::Local newConstructor(const ClassDefine* classDefine); void registerNativeClassStatic(v8::Local funcT, const internal::StaticDefine* staticDefine); template void registerNativeClassInstance(v8::Local funcT, const ClassDefine* classDefine); // the following function are public only for you to interact with raw v8 APIs. template static T make(Args&&... args) { return T(std::forward(args)...); } template static inline typename Local::InternalLocalRef toV8(v8::Isolate* /*isolate*/, const Local& ref) { return ref.val_; } static typename Local::InternalLocalRef toV8(v8::Isolate* isolate, const Local& ref) { if (ref.val_.IsEmpty()) { return v8::Undefined(isolate).As>(); } return ref.val_; } static inline Arguments extractV8Arguments(V8Engine* engine, const v8::FunctionCallbackInfo& args) { return Arguments(std::make_pair(engine, args)); } private: void addManagedObject(void* nativeObj, v8::Local obj, std::function&& proc); size_t keepReference(const Local& ref); /** * can be called withoud EngineScope */ void removeKeptReference(size_t id); private: // WHO is your friend!!! friend class V8EngineScope; friend class V8HandleScope; friend class V8ExitEngineScope; template friend class ::script::Local; template friend class ::script::Global; template friend class ::script::Weak; friend class ::script::Object; friend class ::script::ScriptEngine; // in V8Helper.h friend class v8_backend::V8HandleScope; friend v8::Isolate* currentEngineIsolateChecked(); friend v8::Local currentEngineContextChecked(); friend std::tuple> currentEngineIsolateAndContextChecked(); friend void checkException(v8::TryCatch& tryCatch); friend void rethrowException(const Exception& exception); // in V8Helper.hpp template friend Ret toV8ValueArray(v8::Isolate* isolate, size_t argc, const Local* args, Closure c); friend class ::script::Exception; friend class ::script::Function; friend class ::script::ByteBuffer; friend class ::script::Arguments; friend class ::script::ScriptClass; friend class ::script::StringHolder; friend class ::script::ScriptClass; friend class ExceptionFields; friend class InspectorClient; template friend class GlobalRefState; friend struct V8BookKeepFetcher; friend struct ::script::v8_interop; template static auto& refVal(Ref* ref) { return ref->val_; } }; } // namespace script::v8_backend