768 lines
26 KiB
C++
768 lines
26 KiB
C++
/*
|
|
* Tencent is pleased to support the open source community by making Puerts available.
|
|
* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
|
* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may
|
|
* be subject to their corresponding license terms. This file is subject to the terms and conditions defined in file 'LICENSE',
|
|
* which is part of this source code package.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "JsEnv.h"
|
|
#include "DynamicDelegateProxy.h"
|
|
#include "StructWrapper.h"
|
|
#include "CppObjectMapper.h"
|
|
#include "V8Utils.h"
|
|
#include "ObjectMapper.h"
|
|
#include "JSLogger.h"
|
|
#include "ObjectRetainer.h"
|
|
#if !defined(ENGINE_INDEPENDENT_JSENV)
|
|
#include "TypeScriptGeneratedClass.h"
|
|
#endif
|
|
#include "UECompatible.h"
|
|
#include "ContainerMeta.h"
|
|
#include "ObjectCacheNode.h"
|
|
#include <unordered_map>
|
|
|
|
#if ENGINE_MINOR_VERSION >= 25 || ENGINE_MAJOR_VERSION > 4
|
|
#include "UObject/WeakFieldPtr.h"
|
|
#endif
|
|
|
|
PRAGMA_DISABLE_UNDEFINED_IDENTIFIER_WARNINGS
|
|
#pragma warning(push, 0)
|
|
#include "libplatform/libplatform.h"
|
|
#include "v8.h"
|
|
#pragma warning(pop)
|
|
PRAGMA_ENABLE_UNDEFINED_IDENTIFIER_WARNINGS
|
|
|
|
#include "NamespaceDef.h"
|
|
|
|
#include "V8InspectorImpl.h"
|
|
|
|
#if defined(WITH_NODEJS)
|
|
PRAGMA_DISABLE_UNDEFINED_IDENTIFIER_WARNINGS
|
|
#pragma warning(push, 0)
|
|
#include "node.h"
|
|
#include "uv.h"
|
|
#pragma warning(pop)
|
|
PRAGMA_ENABLE_UNDEFINED_IDENTIFIER_WARNINGS
|
|
#endif
|
|
|
|
#if USE_WASM3
|
|
#include "WasmRuntime.h"
|
|
#include "PuertsWasm/WasmJsFunctionParams.h"
|
|
#endif
|
|
|
|
#if V8_MAJOR_VERSION < 8 || defined(WITH_QUICKJS) || defined(WITH_NODEJS) || (WITH_EDITOR && !defined(FORCE_USE_STATIC_V8_LIB))
|
|
#define WITH_BACKING_STORE_AUTO_FREE 0
|
|
#else
|
|
#define WITH_BACKING_STORE_AUTO_FREE 1
|
|
#endif
|
|
|
|
namespace PUERTS_NAMESPACE
|
|
{
|
|
class JSError
|
|
{
|
|
public:
|
|
FString Message;
|
|
|
|
JSError()
|
|
{
|
|
}
|
|
|
|
explicit JSError(const FString& m) : Message(m)
|
|
{
|
|
}
|
|
};
|
|
|
|
class FJsEnvImpl : public IJsEnv, IObjectMapper, public FUObjectArray::FUObjectDeleteListener
|
|
{
|
|
public:
|
|
explicit FJsEnvImpl(const FString& ScriptRoot);
|
|
|
|
FJsEnvImpl(std::shared_ptr<IJSModuleLoader> InModuleLoader, std::shared_ptr<ILogger> InLogger, int InPort,
|
|
std::function<void(const FString&)> InOnSourceLoadedCallback, const FString InFlags, void* InExternalRuntime,
|
|
void* InExternalContext);
|
|
|
|
virtual ~FJsEnvImpl() override;
|
|
|
|
virtual void Start(const FString& ModuleNameOrScript, const TArray<TPair<FString, UObject*>>& Arguments) override;
|
|
|
|
virtual bool IdleNotificationDeadline(double DeadlineInSeconds) override;
|
|
|
|
virtual void LowMemoryNotification() override;
|
|
|
|
virtual void RequestMinorGarbageCollectionForTesting() override;
|
|
|
|
virtual void RequestFullGarbageCollectionForTesting() override;
|
|
|
|
virtual void WaitDebugger(double timeout) override
|
|
{
|
|
#ifdef THREAD_SAFE
|
|
v8::Locker Locker(MainIsolate);
|
|
#endif
|
|
const auto startTime = FDateTime::Now();
|
|
while (Inspector && !Inspector->Tick())
|
|
{
|
|
if (timeout > 0)
|
|
{
|
|
auto now = FDateTime::Now();
|
|
if ((now - startTime).GetTotalSeconds() >= timeout)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !defined(ENGINE_INDEPENDENT_JSENV)
|
|
virtual void TryBindJs(const class UObjectBase* InObject) override;
|
|
|
|
virtual void RebindJs() override;
|
|
#endif
|
|
|
|
virtual FString CurrentStackTrace() override;
|
|
|
|
virtual void InitExtensionMethodsMap() override;
|
|
|
|
void JsHotReload(FName ModuleName, const FString& JsSource);
|
|
|
|
virtual void ReloadModule(FName ModuleName, const FString& JsSource) override;
|
|
|
|
virtual void ReloadSource(const FString& Path, const std::string& JsSource) override;
|
|
|
|
std::function<void(const FString&)> OnSourceLoadedCallback;
|
|
|
|
virtual void OnSourceLoaded(std::function<void(const FString&)> Callback) override;
|
|
|
|
public:
|
|
bool IsTypeScriptGeneratedClass(UClass* Class);
|
|
|
|
void SetJsTakeRef(UObject* UEObject, FClassWrapper* ClassWrapper);
|
|
|
|
virtual void Bind(FClassWrapper* ClassWrapper, UObject* UEObject, v8::Local<v8::Object> JSObject) override;
|
|
|
|
virtual void UnBind(UClass* Class, UObject* UEObject) override;
|
|
|
|
virtual void UnBind(UClass* Class, UObject* UEObject, bool ResetPointer);
|
|
|
|
v8::Local<v8::Value> FindOrAdd(
|
|
v8::Isolate* InIsolate, v8::Local<v8::Context>& Context, UClass* Class, UObject* UEObject, bool SkipTypeScriptInitial);
|
|
|
|
virtual v8::Local<v8::Value> FindOrAdd(
|
|
v8::Isolate* InIsolate, v8::Local<v8::Context>& Context, UClass* Class, UObject* UEObject) override;
|
|
|
|
virtual void BindStruct(
|
|
FScriptStructWrapper* ScriptStructWrapper, void* Ptr, v8::Local<v8::Object> JSObject, bool PassByPointer) override;
|
|
|
|
virtual void UnBindStruct(FScriptStructWrapper* ScriptStructWrapper, void* Ptr) override;
|
|
|
|
virtual void UnBindCppObject(v8::Isolate* Isolate, JSClassDefinition* ClassDefinition, void* Ptr) override;
|
|
|
|
virtual v8::Local<v8::Value> FindOrAddStruct(
|
|
v8::Isolate* Isolate, v8::Local<v8::Context>& Context, UScriptStruct* ScriptStruct, void* Ptr, bool PassByPointer) override;
|
|
|
|
virtual void BindCppObject(v8::Isolate* InIsolate, JSClassDefinition* ClassDefinition, void* Ptr,
|
|
v8::Local<v8::Object> JSObject, bool PassByPointer) override;
|
|
|
|
virtual void* GetPrivateData(v8::Local<v8::Context> Context, v8::Local<v8::Object> JSObject) override;
|
|
|
|
virtual void SetPrivateData(v8::Local<v8::Context> Context, v8::Local<v8::Object> JSObject, void* Ptr) override;
|
|
|
|
virtual v8::MaybeLocal<v8::Function> LoadTypeById(v8::Local<v8::Context> Context, const void* TypeId) override;
|
|
|
|
virtual v8::Local<v8::Value> FindOrAddCppObject(
|
|
v8::Isolate* Isolate, v8::Local<v8::Context>& Context, const void* TypeId, void* Ptr, bool PassByPointer) override;
|
|
|
|
virtual void Merge(
|
|
v8::Isolate* Isolate, v8::Local<v8::Context> Context, v8::Local<v8::Object> Src, UStruct* DesType, void* Des) override;
|
|
|
|
enum ContainerType
|
|
{
|
|
EArray,
|
|
EMap,
|
|
ESet
|
|
};
|
|
|
|
void BindContainer(void* Ptr, v8::Local<v8::Object> JSObject, void (*Callback)(const v8::WeakCallbackInfo<void>& data),
|
|
bool PassByPointer, ContainerType Type);
|
|
|
|
virtual void UnBindContainer(void* Ptr) override;
|
|
|
|
virtual v8::Local<v8::Value> FindOrAddContainer(v8::Isolate* Isolate, v8::Local<v8::Context>& Context, PropertyMacro* Property,
|
|
FScriptArray* Ptr, bool PassByPointer) override;
|
|
|
|
virtual v8::Local<v8::Value> FindOrAddContainer(v8::Isolate* Isolate, v8::Local<v8::Context>& Context, PropertyMacro* Property,
|
|
FScriptSet* Ptr, bool PassByPointer) override;
|
|
|
|
virtual v8::Local<v8::Value> FindOrAddContainer(v8::Isolate* Isolate, v8::Local<v8::Context>& Context,
|
|
PropertyMacro* KeyProperty, PropertyMacro* ValueProperty, FScriptMap* Ptr, bool PassByPointer) override;
|
|
|
|
virtual v8::Local<v8::Value> FindOrAddDelegate(v8::Isolate* Isolate, v8::Local<v8::Context>& Context, UObject* Owner,
|
|
PropertyMacro* Property, void* DelegatePtr, bool PassByPointer) override;
|
|
|
|
virtual bool AddToDelegate(
|
|
v8::Isolate* Isolate, v8::Local<v8::Context>& Context, void* DelegatePtr, v8::Local<v8::Function> JsFunction) override;
|
|
|
|
virtual PropertyMacro* FindDelegateProperty(void* DelegatePtr) override;
|
|
|
|
virtual FScriptDelegate NewDelegate(v8::Isolate* Isolate, v8::Local<v8::Context>& Context, UObject* Owner,
|
|
v8::Local<v8::Function> JsFunction, UFunction* SignatureFunction) override;
|
|
|
|
void ReleaseManualReleaseDelegate(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
virtual bool RemoveFromDelegate(
|
|
v8::Isolate* Isolate, v8::Local<v8::Context>& Context, void* DelegatePtr, v8::Local<v8::Function> JsFunction) override;
|
|
|
|
virtual bool ClearDelegate(v8::Isolate* Isolate, v8::Local<v8::Context>& Context, void* DelegatePtr) override;
|
|
|
|
virtual void ExecuteDelegate(v8::Isolate* Isolate, v8::Local<v8::Context>& Context,
|
|
const v8::FunctionCallbackInfo<v8::Value>& Info, void* DelegatePtr) override;
|
|
|
|
virtual bool IsInstanceOf(UStruct* Struct, v8::Local<v8::Object> JsObject) override;
|
|
|
|
virtual bool IsInstanceOfCppObject(v8::Isolate* Isolate, const void* TypeId, v8::Local<v8::Object> JsObject) override;
|
|
|
|
virtual std::weak_ptr<int> GetJsEnvLifeCycleTracker() override;
|
|
|
|
virtual v8::Local<v8::Value> AddSoftObjectPtr(v8::Isolate* Isolate, v8::Local<v8::Context> Context,
|
|
FSoftObjectPtr* SoftObjectPtr, UClass* Class, bool IsSoftClass) override;
|
|
|
|
bool CheckDelegateProxies(float Tick);
|
|
|
|
virtual v8::Local<v8::Value> CreateArray(
|
|
v8::Isolate* Isolate, v8::Local<v8::Context>& Context, FPropertyTranslator* Property, void* ArrayPtr) override;
|
|
|
|
void InvokeDelegateCallback(UDynamicDelegateProxy* Proxy, void* Params);
|
|
|
|
#if !defined(ENGINE_INDEPENDENT_JSENV)
|
|
void JsConstruct(UClass* Class, UObject* Object, const v8::UniquePersistent<v8::Function>& Constructor,
|
|
const v8::UniquePersistent<v8::Object>& Prototype);
|
|
|
|
void TsConstruct(UTypeScriptGeneratedClass* Class, UObject* Object);
|
|
|
|
void InvokeJsMethod(UObject* ContextObject, UJSGeneratedFunction* Function, FFrame& Stack, void* RESULT_PARAM);
|
|
|
|
void InvokeMixinMethod(UObject* ContextObject, UJSGeneratedFunction* Function, FFrame& Stack, void* RESULT_PARAM);
|
|
|
|
void TypeScriptInitial(UClass* Class, UObject* Object, const bool TypeScriptClassFound = false);
|
|
|
|
void InvokeTsMethod(UObject* ContextObject, UFunction* Function, FFrame& Stack, void* RESULT_PARAM);
|
|
|
|
void NotifyReBind(UTypeScriptGeneratedClass* Class);
|
|
#endif
|
|
|
|
v8::UniquePersistent<v8::Function> JsPromiseRejectCallback;
|
|
|
|
V8_INLINE static FJsEnvImpl* Get(v8::Isolate* Isolate)
|
|
{
|
|
return static_cast<FJsEnvImpl*>(FV8Utils::IsolateData<IObjectMapper>(Isolate));
|
|
}
|
|
|
|
public:
|
|
#if ENGINE_MINOR_VERSION > 22 || ENGINE_MAJOR_VERSION > 4
|
|
virtual void OnUObjectArrayShutdown() override
|
|
{
|
|
GUObjectArray.RemoveUObjectDeleteListener(static_cast<FUObjectArray::FUObjectDeleteListener*>(this));
|
|
}
|
|
#endif
|
|
virtual void NotifyUObjectDeleted(const class UObjectBase* Object, int32 Index) override;
|
|
|
|
void TryReleaseType(UStruct* Struct);
|
|
|
|
private:
|
|
bool LoadFile(const FString& RequiringDir, const FString& ModuleName, FString& OutPath, FString& OutDebugPath,
|
|
TArray<uint8>& Data, FString& ErrInfo);
|
|
|
|
void ExecuteModule(const FString& ModuleName);
|
|
|
|
void EvalScript(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void Log(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void SearchModule(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void LoadModule(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
v8::Local<v8::Value> UETypeToJsClass(v8::Isolate* Isolate, v8::Local<v8::Context> Context, UField* Type);
|
|
|
|
void LoadUEType(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void LoadCppType(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void UEClassToJSClass(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void SetJsTakeRefInTs(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
bool GetContainerTypeProperty(v8::Local<v8::Context> Context, v8::Local<v8::Value> Value, PropertyMacro** PropertyPtr);
|
|
|
|
void NewContainer(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
std::shared_ptr<FStructWrapper> GetStructWrapper(UStruct* InStruct, bool& IsReuseTemplate);
|
|
|
|
struct FTemplateInfo
|
|
{
|
|
v8::UniquePersistent<v8::FunctionTemplate> Template;
|
|
std::shared_ptr<FStructWrapper> StructWrapper;
|
|
};
|
|
|
|
FTemplateInfo* GetTemplateInfoOfType(UStruct* Class, bool& Existed);
|
|
|
|
v8::Local<v8::Function> GetJsClass(UStruct* Class, v8::Local<v8::Context> Context);
|
|
|
|
FPropertyTranslator* GetContainerPropertyTranslator(PropertyMacro* Property);
|
|
|
|
void SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void SetFTickerDelegate(const v8::FunctionCallbackInfo<v8::Value>& Info, bool Continue);
|
|
|
|
bool TimerCallback(int DelegateHandleId, bool Continue);
|
|
|
|
void RemoveFTickerDelegateHandle(int HandleId);
|
|
|
|
void SetInterval(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void ClearInterval(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void MergeObject(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void NewObjectByClass(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void NewStructByScriptStruct(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
#if !defined(ENGINE_INDEPENDENT_JSENV)
|
|
void MakeUClass(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
TArray<TWeakObjectPtr<UClass>> MixinClasses;
|
|
void Mixin(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
#endif
|
|
|
|
void FindModule(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void DumpStatisticsLog(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void SetInspectorCallback(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
void DispatchProtocolMessage(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
#ifndef WITH_QUICKJS
|
|
v8::MaybeLocal<v8::Module> FetchESModuleTree(v8::Local<v8::Context> Context, const FString& FileName);
|
|
|
|
v8::MaybeLocal<v8::Module> FetchCJSModuleAsESModule(v8::Local<v8::Context> Context, const FString& ModuleName);
|
|
|
|
struct FModuleInfo
|
|
{
|
|
v8::Global<v8::Module> Module;
|
|
TMap<FString, v8::Global<v8::Module>> ResolveCache;
|
|
v8::Global<v8::Value> CJSValue;
|
|
};
|
|
|
|
std::unordered_multimap<int, FModuleInfo*>::iterator FindModuleInfo(v8::Local<v8::Module> Module);
|
|
|
|
static v8::MaybeLocal<v8::Module> ResolveModuleCallback(v8::Local<v8::Context> Context, v8::Local<v8::String> Specifier,
|
|
#if V8_MAJOR_VERSION >= 9
|
|
v8::Local<v8::FixedArray> ImportAttributes, // not implement yet
|
|
#endif
|
|
v8::Local<v8::Module> Referrer);
|
|
#endif
|
|
|
|
struct ObjectMerger;
|
|
|
|
std::unique_ptr<ObjectMerger>& GetObjectMerger(UStruct* Struct);
|
|
|
|
struct ObjectMerger
|
|
{
|
|
std::map<std::string, std::unique_ptr<FPropertyTranslator>> Fields;
|
|
UStruct* Struct;
|
|
FJsEnvImpl* Parent;
|
|
|
|
ObjectMerger(FJsEnvImpl* InParent, UStruct* InStruct)
|
|
{
|
|
Parent = InParent;
|
|
Struct = InStruct;
|
|
for (TFieldIterator<PropertyMacro> It(Struct); It; ++It)
|
|
{
|
|
PropertyMacro* Property = *It;
|
|
TStringConversion<TStringConvert<TCHAR, ANSICHAR>> Name(*Property->GetName());
|
|
Fields[Name.Get()] = FPropertyTranslator::Create(Property);
|
|
}
|
|
}
|
|
|
|
void Merge(v8::Isolate* Isolate, v8::Local<v8::Context> Context, v8::Local<v8::Object> JsObject, void* Ptr)
|
|
{
|
|
if (auto Class = Cast<UClass>(Struct))
|
|
{
|
|
UObject* Object = reinterpret_cast<UObject*>(Ptr);
|
|
if (!Object->IsValidLowLevel() || UEObjectIsPendingKill(Object) || Object->GetClass() != Class ||
|
|
FV8Utils::GetPointer(JsObject))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
auto Keys = JsObject->GetOwnPropertyNames(Context).ToLocalChecked();
|
|
for (decltype(Keys->Length()) i = 0; i < Keys->Length(); ++i)
|
|
{
|
|
auto Key = Keys->Get(Context, i).ToLocalChecked();
|
|
auto Iter = Fields.find(*v8::String::Utf8Value(Isolate, Key));
|
|
if (Iter != Fields.end())
|
|
{
|
|
auto MaybeValue = JsObject->Get(Context, Key);
|
|
if (!MaybeValue.IsEmpty())
|
|
{
|
|
auto Value = MaybeValue.ToLocalChecked();
|
|
if (Value->IsObject())
|
|
{
|
|
auto JsObjectField = Value->ToObject(Context).ToLocalChecked();
|
|
if (!FV8Utils::GetPointerFast<void>(JsObjectField))
|
|
{
|
|
UStruct* FieldStruct = nullptr;
|
|
if (auto ObjectPropertyBase = CastFieldMacro<ObjectPropertyBaseMacro>(Iter->second->Property))
|
|
{
|
|
FieldStruct = ObjectPropertyBase->PropertyClass;
|
|
}
|
|
else if (auto StructProperty = CastFieldMacro<StructPropertyMacro>(Iter->second->Property))
|
|
{
|
|
FieldStruct = StructProperty->Struct;
|
|
}
|
|
if (FieldStruct)
|
|
{
|
|
Parent->GetObjectMerger(FieldStruct)
|
|
->Merge(Isolate, Context, JsObjectField,
|
|
Iter->second->Property->ContainerPtrToValuePtr<void>(Ptr));
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
if (!Value->IsUndefined())
|
|
Iter->second->JsToUEInContainer(Isolate, Context, Value, Ptr, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
friend ObjectMerger;
|
|
|
|
#if USE_WASM3
|
|
std::shared_ptr<WasmEnv> PuertsWasmEnv;
|
|
//在执行module.instance的时候,如果有指定memory,那么这个module对应会创建一个runtime
|
|
TArray<std::shared_ptr<WasmRuntime>> PuertsWasmRuntimeList;
|
|
TArray<WasmNormalLinkInfo*> PuertsWasmCachedLinkFunctionList;
|
|
|
|
protected:
|
|
void Wasm_NewMemory(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
void Wasm_MemoryGrowth(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
void Wasm_MemoryBuffer(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
void Wasm_TableGrowth(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
void Wasm_TableSet(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
void Wasm_TableLen(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
void Wasm_Instance(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
void Wasm_OverrideWebAssembly(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
#endif
|
|
|
|
public:
|
|
#if !defined(ENGINE_INDEPENDENT_JSENV)
|
|
class TsDynamicInvokerImpl : public ITsDynamicInvoker
|
|
{
|
|
public:
|
|
TsDynamicInvokerImpl(FJsEnvImpl* InParent) : Parent(InParent)
|
|
{
|
|
}
|
|
|
|
virtual void TsConstruct(UTypeScriptGeneratedClass* Class, UObject* Object) override
|
|
{
|
|
if (Parent)
|
|
Parent->TsConstruct(Class, Object);
|
|
}
|
|
|
|
virtual void InvokeTsMethod(UObject* ContextObject, UFunction* Function, FFrame& Stack, void* RESULT_PARAM) override
|
|
{
|
|
if (Parent)
|
|
Parent->InvokeTsMethod(ContextObject, Function, Stack, RESULT_PARAM);
|
|
}
|
|
|
|
virtual void NotifyReBind(UTypeScriptGeneratedClass* Class) override
|
|
{
|
|
if (Parent)
|
|
Parent->NotifyReBind(Class);
|
|
}
|
|
|
|
FJsEnvImpl* Parent;
|
|
};
|
|
|
|
TSharedPtr<ITsDynamicInvoker, ESPMode::ThreadSafe> TsDynamicInvoker;
|
|
|
|
TSharedPtr<IDynamicInvoker, ESPMode::ThreadSafe> MixinInvoker;
|
|
#endif
|
|
private:
|
|
FObjectRetainer UserObjectRetainer;
|
|
|
|
FObjectRetainer SysObjectRetainer;
|
|
|
|
std::shared_ptr<IJSModuleLoader> ModuleLoader;
|
|
|
|
std::shared_ptr<ILogger> Logger;
|
|
|
|
bool Started;
|
|
|
|
private:
|
|
v8::Isolate::CreateParams CreateParams;
|
|
|
|
#if defined(WITH_NODEJS)
|
|
uv_loop_t NodeUVLoop;
|
|
|
|
std::unique_ptr<node::ArrayBufferAllocator> NodeArrayBufferAllocator;
|
|
|
|
node::IsolateData* NodeIsolateData;
|
|
|
|
node::Environment* NodeEnv;
|
|
|
|
uv_thread_t PollingThread;
|
|
|
|
uv_sem_t PollingSem;
|
|
|
|
uv_async_t DummyUVHandle;
|
|
|
|
bool PollingClosed = false;
|
|
|
|
FGraphEventRef LastJob;
|
|
|
|
#if PLATFORM_LINUX
|
|
int Epoll;
|
|
#endif
|
|
|
|
void StartPolling();
|
|
|
|
void UvRunOnce();
|
|
|
|
void PollEvents();
|
|
|
|
static void OnWatcherQueueChanged(uv_loop_t* loop);
|
|
|
|
void WakeupPollingThread();
|
|
|
|
void StopPolling();
|
|
#endif
|
|
|
|
v8::Isolate* MainIsolate;
|
|
|
|
v8::Global<v8::Context> DefaultContext;
|
|
|
|
v8::Global<v8::Function> Require;
|
|
|
|
v8::Global<v8::Function> GetESMMain;
|
|
|
|
v8::Global<v8::Function> ReloadJs;
|
|
|
|
#if !PUERTS_FORCE_CPP_UFUNCTION
|
|
v8::Global<v8::Function> MergePrototype;
|
|
#endif
|
|
|
|
v8::Global<v8::Function> RemoveListItem;
|
|
|
|
v8::Global<v8::Function> GenListApply;
|
|
|
|
#if defined(WITH_V8_BYTECODE)
|
|
v8::Global<v8::Function> GenEmptyCode;
|
|
#endif
|
|
|
|
TMap<UStruct*, FTemplateInfo> TypeToTemplateInfoMap;
|
|
|
|
TMap<FString, std::shared_ptr<FStructWrapper>> TypeReflectionMap;
|
|
|
|
TMap<UObject*, v8::UniquePersistent<v8::Value>> ObjectMap;
|
|
|
|
TMap<void*, FObjectCacheNode> StructCache;
|
|
|
|
struct ContainerCacheItem
|
|
{
|
|
v8::UniquePersistent<v8::Value> Container;
|
|
bool NeedRelease;
|
|
ContainerType Type;
|
|
};
|
|
|
|
TMap<void*, ContainerCacheItem> ContainerCache;
|
|
|
|
FCppObjectMapper CppObjectMapper;
|
|
|
|
v8::UniquePersistent<v8::FunctionTemplate> ArrayTemplate;
|
|
|
|
v8::UniquePersistent<v8::FunctionTemplate> SetTemplate;
|
|
|
|
v8::UniquePersistent<v8::FunctionTemplate> MapTemplate;
|
|
|
|
v8::UniquePersistent<v8::FunctionTemplate> FixSizeArrayTemplate;
|
|
|
|
struct ContainerPropertyInfo
|
|
{
|
|
#if ENGINE_MINOR_VERSION < 25 && ENGINE_MAJOR_VERSION < 5
|
|
TWeakObjectPtr<PropertyMacro> PropertyWeakPtr;
|
|
#else
|
|
TWeakFieldPtr<PropertyMacro> PropertyWeakPtr;
|
|
#endif
|
|
std::unique_ptr<FPropertyTranslator> PropertyTranslator;
|
|
};
|
|
|
|
std::map<PropertyMacro*, ContainerPropertyInfo> ContainerPropertyMap;
|
|
|
|
std::map<UFunction*, std::unique_ptr<FFunctionTranslator>> JsCallbackPrototypeMap;
|
|
|
|
std::map<UStruct*, std::unique_ptr<ObjectMerger>> ObjectMergers;
|
|
|
|
struct DelegateObjectInfo
|
|
{
|
|
v8::UniquePersistent<v8::Object> JSObject; // function to proxy save here
|
|
TWeakObjectPtr<UObject> Owner; //可用于自动清理
|
|
DelegatePropertyMacro* DelegateProperty;
|
|
MulticastDelegatePropertyMacro* MulticastDelegateProperty;
|
|
UFunction* SignatureFunction;
|
|
bool PassByPointer;
|
|
TWeakObjectPtr<UDynamicDelegateProxy> Proxy;
|
|
v8::UniquePersistent<v8::Array> JsCallbacks;
|
|
};
|
|
|
|
struct TsFunctionInfo
|
|
{
|
|
v8::UniquePersistent<v8::Function> JsFunction;
|
|
|
|
std::unique_ptr<FFunctionTranslator> FunctionTranslator;
|
|
};
|
|
|
|
class DynamicInvokerImpl : public IDynamicInvoker
|
|
{
|
|
public:
|
|
DynamicInvokerImpl(FJsEnvImpl* InParent) : Parent(InParent)
|
|
{
|
|
}
|
|
|
|
virtual void InvokeDelegateCallback(UDynamicDelegateProxy* Proxy, void* Params) override
|
|
{
|
|
if (Parent)
|
|
Parent->InvokeDelegateCallback(Proxy, Params);
|
|
}
|
|
#if !defined(ENGINE_INDEPENDENT_JSENV)
|
|
virtual void JsConstruct(UClass* Class, UObject* Object, const v8::UniquePersistent<v8::Function>& Constructor,
|
|
const v8::UniquePersistent<v8::Object>& Prototype) override
|
|
{
|
|
if (Parent)
|
|
Parent->JsConstruct(Class, Object, Constructor, Prototype);
|
|
}
|
|
|
|
virtual void InvokeJsMethod(
|
|
UObject* ContextObject, UJSGeneratedFunction* Function, FFrame& Stack, void* RESULT_PARAM) override
|
|
{
|
|
if (Parent)
|
|
Parent->InvokeJsMethod(ContextObject, Function, Stack, RESULT_PARAM);
|
|
}
|
|
|
|
virtual void InvokeMixinMethod(
|
|
UObject* ContextObject, UJSGeneratedFunction* Function, FFrame& Stack, void* RESULT_PARAM) override
|
|
{
|
|
if (Parent)
|
|
Parent->InvokeMixinMethod(ContextObject, Function, Stack, RESULT_PARAM);
|
|
}
|
|
#endif
|
|
FJsEnvImpl* Parent;
|
|
};
|
|
#if !defined(ENGINE_INDEPENDENT_JSENV)
|
|
struct FBindInfo
|
|
{
|
|
FName Name;
|
|
v8::UniquePersistent<v8::Function> Constructor;
|
|
v8::UniquePersistent<v8::Object> Prototype;
|
|
bool InjectNotFinished;
|
|
};
|
|
|
|
TMap<UTypeScriptGeneratedClass*, FBindInfo> BindInfoMap;
|
|
|
|
void FinishInjection(UClass* InClass);
|
|
|
|
void MakeSureInject(UTypeScriptGeneratedClass* Class, bool ForceReinject, bool RebindObject);
|
|
#endif
|
|
TSharedPtr<DynamicInvokerImpl, ESPMode::ThreadSafe> DynamicInvoker;
|
|
|
|
TSet<UClass*> GeneratedClasses;
|
|
|
|
v8::UniquePersistent<v8::FunctionTemplate> DelegateTemplate;
|
|
|
|
v8::UniquePersistent<v8::FunctionTemplate> MulticastDelegateTemplate;
|
|
|
|
v8::UniquePersistent<v8::FunctionTemplate> SoftObjectPtrTemplate;
|
|
|
|
std::map<void*, DelegateObjectInfo> DelegateMap;
|
|
|
|
TMap<UFunction*, TsFunctionInfo> TsFunctionMap;
|
|
|
|
TMap<UFunction*, v8::UniquePersistent<v8::Function>> MixinFunctionMap;
|
|
|
|
std::map<UStruct*, std::vector<UFunction*>> ExtensionMethodsMap;
|
|
|
|
bool ExtensionMethodsMapInited = false;
|
|
|
|
struct FTimerInfo
|
|
{
|
|
v8::Global<v8::Function> Callback;
|
|
FUETickDelegateHandle TickerHandle;
|
|
};
|
|
uint32_t TimerID = 0;
|
|
TMap<uint32_t, FTimerInfo> TimerInfos;
|
|
|
|
FUETickDelegateHandle DelegateProxiesCheckerHandler;
|
|
|
|
V8Inspector* Inspector;
|
|
|
|
V8InspectorChannel* InspectorChannel;
|
|
|
|
v8::Global<v8::Function> InspectorMessageHandler;
|
|
|
|
FContainerMeta ContainerMeta;
|
|
|
|
v8::Global<v8::Map> ManualReleaseCallbackMap;
|
|
|
|
std::vector<TWeakObjectPtr<UDynamicDelegateProxy>> ManualReleaseCallbackList;
|
|
|
|
TMap<UObject*, TArray<TWeakObjectPtr<UDynamicDelegateProxy>>> AutoReleaseCallbacksMap;
|
|
|
|
#ifndef WITH_QUICKJS
|
|
TMap<FString, v8::Global<v8::Module>> PathToModule;
|
|
|
|
std::unordered_multimap<int, FModuleInfo*> HashToModuleInfo;
|
|
#endif
|
|
|
|
#ifdef SINGLE_THREAD_VERIFY
|
|
uint32 BoundThreadId;
|
|
#endif
|
|
|
|
typedef void (FJsEnvImpl::*V8MethodCallback)(const v8::FunctionCallbackInfo<v8::Value>& Info);
|
|
|
|
template <V8MethodCallback callback>
|
|
struct MethodBindingHelper
|
|
{
|
|
static void Bind(v8::Isolate* Isolate, v8::Local<v8::Context> Context, v8::Local<v8::Object> Obj, const char* Key,
|
|
v8::Local<v8::External> This)
|
|
{
|
|
Obj->Set(Context, FV8Utils::ToV8String(Isolate, Key),
|
|
v8::FunctionTemplate::New(
|
|
Isolate,
|
|
[](const v8::FunctionCallbackInfo<v8::Value>& Info)
|
|
{
|
|
auto Self = static_cast<FJsEnvImpl*>((v8::Local<v8::External>::Cast(Info.Data()))->Value());
|
|
(Self->*callback)(Info);
|
|
},
|
|
This)
|
|
->GetFunction(Context)
|
|
.ToLocalChecked())
|
|
.Check();
|
|
}
|
|
};
|
|
#if defined(WITH_V8_BYTECODE)
|
|
uint32_t Expect_FlagHash = 0;
|
|
#if V8_MAJOR_VERSION >= 11
|
|
uint32_t Expect_ReadOnlySnapshotChecksum = 0;
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
} // namespace PUERTS_NAMESPACE
|