/* * 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. */ #include "PropertyTranslator.h" #include "V8Utils.h" #include "ObjectMapper.h" #include "StructWrapper.h" #if !defined(ENGINE_INDEPENDENT_JSENV) #include "Engine/UserDefinedStruct.h" #endif #include "ArrayBuffer.h" #include "ContainerWrapper.h" #include "JsObject.h" #ifdef PUERTS_FTEXT_AS_OBJECT #include "TypeInfo.hpp" #endif namespace PUERTS_NAMESPACE { void FPropertyTranslator::Getter(const v8::FunctionCallbackInfo& Info) { v8::Isolate* Isolate = Info.GetIsolate(); v8::Local Context = Isolate->GetCurrentContext(); FPropertyTranslator* This = static_cast((v8::Local::Cast(Info.Data()))->Value()); This->Getter(Isolate, Context, Info); } void FPropertyTranslator::Getter( v8::Isolate* Isolate, v8::Local& Context, const v8::FunctionCallbackInfo& Info) { if (!IsPropertyValid()) { FV8Utils::ThrowException(Isolate, "Property is invalid!"); return; } v8::Local Ret; if (OwnerIsClass) { UObject* Object = FV8Utils::GetUObject(Info.Holder()); if (!Object) { FV8Utils::ThrowException(Isolate, "access a null object"); return; } if (FV8Utils::IsReleasedPtr(Object)) { FV8Utils::ThrowException(Isolate, "access a invalid object"); return; } Ret = UEToJsInContainer(Isolate, Context, Object, true); } else { auto Ptr = FV8Utils::GetPointer(Info.Holder()); if (!Ptr) { FV8Utils::ThrowException(Isolate, "access a null struct"); return; } Ret = UEToJsInContainer(Isolate, Context, Ptr, true); } if (NeedLinkOuter) { LinkOuterImpl(Context, Info.Holder(), Ret); } Info.GetReturnValue().Set(Ret); } void FPropertyTranslator::Setter(const v8::FunctionCallbackInfo& Info) { v8::Isolate* Isolate = Info.GetIsolate(); v8::Local Context = Isolate->GetCurrentContext(); FPropertyTranslator* This = static_cast((v8::Local::Cast(Info.Data()))->Value()); This->Setter(Isolate, Context, Info[0], Info); } void FPropertyTranslator::Setter(v8::Isolate* Isolate, v8::Local& Context, v8::Local Value, const v8::FunctionCallbackInfo& Info) { if (!IsPropertyValid()) { FV8Utils::ThrowException(Isolate, "Property is invalid!"); return; } if (OwnerIsClass) { UObject* Object = FV8Utils::GetUObject(Info.Holder()); if (!Object) { FV8Utils::ThrowException(Isolate, "access a null object"); return; } if (FV8Utils::IsReleasedPtr(Object)) { FV8Utils::ThrowException(Isolate, "access a invalid object"); return; } JsToUEInContainer(Isolate, Context, Value, Object, true); } else { auto Ptr = FV8Utils::GetPointer(Info.Holder()); if (!Ptr) { FV8Utils::ThrowException(Isolate, "access a null struct"); return; } JsToUEInContainer(Isolate, Context, Value, Ptr, true); } } void FPropertyTranslator::DelegateGetter(const v8::FunctionCallbackInfo& Info) { v8::Isolate* Isolate = Info.GetIsolate(); v8::Isolate::Scope IsolateScope(Isolate); v8::HandleScope HandleScope(Isolate); v8::Local Context = Isolate->GetCurrentContext(); v8::Context::Scope ContextScope(Context); FPropertyTranslator* PropertyTranslator = static_cast((v8::Local::Cast(Info.Data()))->Value()); if (!PropertyTranslator->IsPropertyValid()) { FV8Utils::ThrowException(Isolate, "Property is invalid!"); return; } auto Object = FV8Utils::GetUObject(Info.Holder()); if (!Object) { FV8Utils::ThrowException(Isolate, "access a null object"); return; } if (FV8Utils::IsReleasedPtr(Object)) { FV8Utils::ThrowException(Isolate, "access a invalid object"); return; } auto DelegatePtr = PropertyTranslator->Property->ContainerPtrToValuePtr(Object); Info.GetReturnValue().Set(FV8Utils::IsolateData(Isolate)->FindOrAddDelegate( Isolate, Context, Object, PropertyTranslator->Property, DelegatePtr, true)); } void FPropertyTranslator::SetAccessor(v8::Isolate* Isolate, v8::Local Template) { if (Property->IsA() || Property->IsA() #if ENGINE_MINOR_VERSION >= 23 || ENGINE_MAJOR_VERSION > 4 || Property->IsA() || Property->IsA() #endif ) { if (Property->GetOwnerStruct()->IsA()) // only uobject support { auto DelegateGetterTemplate = v8::FunctionTemplate::New(Isolate, DelegateGetter, v8::External::New(Isolate, this)); Template->PrototypeTemplate()->SetAccessorProperty(FV8Utils::InternalString(Isolate, Property->GetName()), DelegateGetterTemplate, v8::Local(), (v8::PropertyAttribute)(v8::DontDelete | v8::ReadOnly)); } } else { auto OwnerStruct = Property->GetOwnerStruct(); auto Self = v8::External::New(Isolate, this); auto GetterTemplate = v8::FunctionTemplate::New(Isolate, Getter, Self); auto SetterTemplate = v8::FunctionTemplate::New(Isolate, Setter, Self); #if !defined(ENGINE_INDEPENDENT_JSENV) FString PropertyName = OwnerStruct && OwnerStruct->IsA() ? #if ENGINE_MINOR_VERSION >= 23 || ENGINE_MAJOR_VERSION > 4 Property->GetAuthoredName() #else Property->GetDisplayNameText().ToString() #endif : Property->GetName(); #ifdef PUERTS_WITH_EDITOR_SUFFIX if (Property->IsEditorOnlyProperty()) { PropertyName += EditorOnlyPropertySuffix; } #endif Template->PrototypeTemplate()->SetAccessorProperty( FV8Utils::InternalString(Isolate, PropertyName), GetterTemplate, SetterTemplate, v8::DontDelete); #else Template->PrototypeTemplate()->SetAccessorProperty( FV8Utils::InternalString(Isolate, Property->GetName()), GetterTemplate, SetterTemplate, v8::DontDelete); #endif } } class FInt32PropertyTranslator : public FPropertyTranslator { public: explicit FInt32PropertyTranslator(PropertyMacro* InProperty) : FPropertyTranslator(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return v8::Integer::New(Isolate, static_cast(NumericProperty->GetSignedIntPropertyValue(ValuePtr))); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { NumericProperty->SetIntPropertyValue(ValuePtr, static_cast(Value->Int32Value(Context).ToChecked())); return true; } }; class FUInt32PropertyTranslator : public FPropertyTranslator { public: explicit FUInt32PropertyTranslator(PropertyMacro* InProperty) : FPropertyTranslator(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return v8::Integer::NewFromUnsigned(Isolate, static_cast(NumericProperty->GetUnsignedIntPropertyValue(ValuePtr))); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { NumericProperty->SetIntPropertyValue(ValuePtr, static_cast(Value->Uint32Value(Context).ToChecked())); return true; } }; class FInt64PropertyTranslator : public FPropertyTranslator { public: explicit FInt64PropertyTranslator(PropertyMacro* InProperty) : FPropertyTranslator(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return v8::BigInt::New(Isolate, NumericProperty->GetSignedIntPropertyValue(ValuePtr)); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { if (Value->IsBigInt()) { NumericProperty->SetIntPropertyValue( ValuePtr, static_cast(Value->ToBigInt(Context).ToLocalChecked()->Int64Value())); } return true; } }; class FUInt64PropertyTranslator : public FPropertyTranslator { public: explicit FUInt64PropertyTranslator(PropertyMacro* InProperty) : FPropertyTranslator(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return v8::BigInt::NewFromUnsigned(Isolate, NumericProperty->GetUnsignedIntPropertyValue(ValuePtr)); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { if (Value->IsBigInt()) { NumericProperty->SetIntPropertyValue( ValuePtr, static_cast(Value->ToBigInt(Context).ToLocalChecked()->Uint64Value())); } return true; } }; class FNumberPropertyTranslator : public FPropertyTranslator { public: explicit FNumberPropertyTranslator(PropertyMacro* InProperty) : FPropertyTranslator(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return v8::Number::New(Isolate, NumericProperty->GetFloatingPointPropertyValue(ValuePtr)); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { NumericProperty->SetFloatingPointPropertyValue(ValuePtr, Value->NumberValue(Context).ToChecked()); return true; } }; class FBooleanPropertyTranslator : public FPropertyTranslator { public: explicit FBooleanPropertyTranslator(PropertyMacro* InProperty) : FPropertyTranslator(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return v8::Boolean::New(Isolate, BoolProperty->GetPropertyValue(ValuePtr)); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { BoolProperty->SetPropertyValue(ValuePtr, Value->BooleanValue(Isolate)); return true; } }; class FEnumPropertyTranslator : public FPropertyTranslator { public: explicit FEnumPropertyTranslator(PropertyMacro* InProperty) : FPropertyTranslator(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return v8::Integer::New( Isolate, static_cast(EnumProperty->GetUnderlyingProperty()->GetSignedIntPropertyValue(ValuePtr))); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue( ValuePtr, static_cast(Value->Int32Value(Context).ToChecked())); return true; } }; class FPropertyWithDestructorReflection : public FPropertyTranslator { public: explicit FPropertyWithDestructorReflection(PropertyMacro* InProperty) : FPropertyTranslator(InProperty) { } void Cleanup(void* ContainerPtr) const override { Property->DestroyValue_InContainer(ContainerPtr); } }; //----------------------------string----------------------------- class FStringPropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FStringPropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return FV8Utils::ToV8String(Isolate, StringProperty->GetPropertyValue(ValuePtr)); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { auto Str = FV8Utils::ToFString(Isolate, Value); // TCHAR* Str = (TCHAR*)(*(v8::String::Value(Isolate, Value))); StringProperty->SetPropertyValue(ValuePtr, Str); return true; } }; class FNamePropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FNamePropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return FV8Utils::ToV8String(Isolate, NameProperty->GetPropertyValue(ValuePtr)); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { if (Value->IsArrayBuffer()) { auto Ab = v8::Local::Cast(Value); size_t ByteLength; auto Data = DataTransfer::GetArrayBufferData(Ab, ByteLength); if (ByteLength == sizeof(FName)) { NameProperty->SetPropertyValue(ValuePtr, *static_cast(Data)); return true; } } NameProperty->SetPropertyValue(ValuePtr, FV8Utils::ToFName(Isolate, Value)); return true; } }; class FTextPropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FTextPropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { #ifndef PUERTS_FTEXT_AS_OBJECT return FV8Utils::ToV8String(Isolate, TextProperty->GetPropertyValue(ValuePtr)); #else return DataTransfer::FindOrAddCData(Context->GetIsolate(), Context, StaticTypeId::get(), PassByPointer ? ValuePtr : new FText(TextProperty->GetPropertyValue(ValuePtr)), PassByPointer); #endif } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { #ifndef PUERTS_FTEXT_AS_OBJECT TextProperty->SetPropertyValue(ValuePtr, FText::FromString(FV8Utils::ToFString(Isolate, Value))); #else auto TextPtr = DataTransfer::GetPointerFast(Value.As()); TextProperty->SetPropertyValue(ValuePtr, TextPtr ? *TextPtr : FText()); #endif return true; } }; // object, class, struct class FObjectPropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FObjectPropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { UObject* UEObject = ObjectBaseProperty->GetObjectPropertyValue(ValuePtr); if (!UEObject || !UEObject->IsValidLowLevelFast() || UEObjectIsPendingKill(UEObject)) { return v8::Undefined(Isolate); } return FV8Utils::IsolateData(Isolate)->FindOrAdd(Isolate, Context, UEObject->GetClass(), UEObject); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { auto Object = FV8Utils::GetUObject(Context, Value); if (FV8Utils::IsReleasedPtr(Object)) { FV8Utils::ThrowException(Isolate, "passing a invalid object"); return false; } ObjectBaseProperty->SetObjectPropertyValue(ValuePtr, Object); return true; } private: }; class FSoftObjectPropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FSoftObjectPropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { const FSoftObjectPtr* Ptr = SoftObjectProperty->GetPropertyValuePtr(ValuePtr); return FV8Utils::IsolateData(Isolate)->AddSoftObjectPtr( Isolate, Context, new FSoftObjectPtr(*Ptr), SoftObjectProperty->PropertyClass, false); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { FSoftObjectPtr* Ptr = static_cast(FV8Utils::GetPointer(Context, Value)); if (!Ptr) { FV8Utils::ThrowException(Isolate, "passing a invalid object for FSoftObjectPtr"); return false; } SoftObjectProperty->SetPropertyValue(ValuePtr, *Ptr); return true; } }; class FSoftClassPropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FSoftClassPropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { const FSoftObjectPtr* Ptr = SoftObjectProperty->GetPropertyValuePtr(ValuePtr); return FV8Utils::IsolateData(Isolate)->AddSoftObjectPtr( Isolate, Context, new FSoftObjectPtr(*Ptr), SoftClassProperty->MetaClass, true); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { FSoftObjectPtr* Ptr = static_cast(FV8Utils::GetPointer(Context, Value)); if (!Ptr) { FV8Utils::ThrowException(Isolate, "passing a invalid object for FSoftObjectPtr"); return false; } SoftObjectProperty->SetPropertyValue(ValuePtr, *Ptr); return true; } }; #if ENGINE_MINOR_VERSION >= 25 || ENGINE_MAJOR_VERSION > 4 class FFieldPathPropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FFieldPathPropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return FV8Utils::ToV8String(Isolate, FieldPathProperty->GetPropertyValuePtr(ValuePtr)->ToString()); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { auto Path = FV8Utils::ToFString(Isolate, Value); FFieldPath FieldPath; FieldPath.Generate(*Path); if (!FieldPath.GetTyped(FieldPathProperty->PropertyClass)) { FV8Utils::ThrowException(Isolate, FString::Printf(TEXT("invalid FieldPath: %s"), *Path)); return false; } FieldPathProperty->SetPropertyValue(ValuePtr, FieldPath); return true; } }; #endif #ifdef GetObject #undef GetObject #endif class FInterfacePropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FInterfacePropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { const FScriptInterface& Interface = InterfaceProperty->GetPropertyValue(ValuePtr); UObject* Object = Interface.GetObject(); if (!Object || !Object->IsValidLowLevelFast() || UEObjectIsPendingKill(Object)) { return v8::Undefined(Isolate); } return FV8Utils::IsolateData(Isolate)->FindOrAdd(Isolate, Context, Object->GetClass(), Object); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { UObject* Object = FV8Utils::GetUObject(Context, Value); if (FV8Utils::IsReleasedPtr(Object)) { FV8Utils::ThrowException(Isolate, "passing a invalid object"); return false; } FScriptInterface* Interface = reinterpret_cast(ValuePtr); Interface->SetObject(Object); Interface->SetInterface(Object ? Object->GetInterfaceAddress(InterfaceProperty->InterfaceClass) : nullptr); return true; } }; class FFastPropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FFastPropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } virtual bool JsToUEFast(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* TempBuff, void** OutValuePtr) const override { void* Ptr = FV8Utils::GetPointer(Context, Value); if (Ptr) { *OutValuePtr = Ptr; return true; } *OutValuePtr = TempBuff; return JsToUE(Isolate, Context, Value, TempBuff, false); } }; class FScriptStructPropertyTranslator : public FFastPropertyTranslator { public: explicit FScriptStructPropertyTranslator(PropertyMacro* InProperty) : FFastPropertyTranslator(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override //还是得有个指针模式,否则不能通过obj.xx.xx直接修改struct值,倒是和性能无关,应该强制js测不许保存指针型对象的引用(从native侧进入,最后一层退出时清空?) { void* Ptr = const_cast(ValuePtr); if (!PassByPointer) { // FScriptStructWrapper::Alloc using new, so delete in static wrapper is safe Ptr = FScriptStructWrapper::Alloc(StructProperty->Struct); StructProperty->CopySingleValue(Ptr, ValuePtr); } return FV8Utils::IsolateData(Isolate)->FindOrAddStruct( Isolate, Context, StructProperty->Struct, Ptr, PassByPointer); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { void* Ptr = FV8Utils::GetPointer(Context, Value); if (Ptr) { if (DeepCopy || !ParamShallowCopySize) { StructProperty->CopySingleValue(ValuePtr, Ptr); } else { FMemory::Memcpy(ValuePtr, Ptr, ParamShallowCopySize); } } else if (Value->IsObject()) { FV8Utils::IsolateData(Isolate)->Merge( Isolate, Context, Value->ToObject(Context).ToLocalChecked(), StructProperty->Struct, ValuePtr); } return true; } }; class FArrayBufferPropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FArrayBufferPropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { void* Ptr = const_cast(ValuePtr); FArrayBuffer* ArrayBuffer = static_cast(Ptr); if (ArrayBuffer->bCopy) { v8::Local Ab = v8::ArrayBuffer::New(Isolate, ArrayBuffer->Length); void* Buff = static_cast(DataTransfer::GetArrayBufferData(Ab)); ::memcpy(Buff, ArrayBuffer->Data, ArrayBuffer->Length); return Ab; } else { return DataTransfer::NewArrayBuffer(Context, ArrayBuffer->Data, ArrayBuffer->Length); } } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { FArrayBuffer ArrayBuffer; if (Value->IsArrayBufferView()) { v8::Local BuffView = Value.As(); auto Ab = BuffView->Buffer(); ArrayBuffer.Data = static_cast(DataTransfer::GetArrayBufferData(Ab)) + BuffView->ByteOffset(); ArrayBuffer.Length = BuffView->ByteLength(); } else if (Value->IsArrayBuffer()) { auto Ab = v8::Local::Cast(Value); size_t ByteLength; ArrayBuffer.Data = DataTransfer::GetArrayBufferData(Ab, ByteLength); ArrayBuffer.Length = ByteLength; } StructProperty->CopySingleValue(ValuePtr, &ArrayBuffer); return true; } }; class FArrayBufferValuePropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FArrayBufferValuePropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { void* Ptr = const_cast(ValuePtr); FArrayBufferValue* ArrayBuffer = static_cast(Ptr); v8::Local Ab = v8::ArrayBuffer::New(Isolate, ArrayBuffer->Data.Num()); void* Buff = static_cast(DataTransfer::GetArrayBufferData(Ab)); ::memcpy(Buff, ArrayBuffer->Data.GetData(), ArrayBuffer->Data.Num()); return Ab; } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { FArrayBufferValue* ArrayBuffer = static_cast(ValuePtr); size_t Len = 0; char* Data = nullptr; if (Value->IsArrayBufferView()) { v8::Local BuffView = Value.As(); auto Ab = BuffView->Buffer(); Data = static_cast(DataTransfer::GetArrayBufferData(Ab)) + BuffView->ByteOffset(); Len = BuffView->ByteLength(); } else if (Value->IsArrayBuffer()) { auto Ab = v8::Local::Cast(Value); Data = static_cast(DataTransfer::GetArrayBufferData(Ab, Len)); } if (Len > 0 && Data) { ArrayBuffer->Data.AddUninitialized(Len); ::memcpy(ArrayBuffer->Data.GetData(), Data, Len); } else { ArrayBuffer->Data.Reset(); } return true; } }; class FJsObjectPropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FJsObjectPropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { if (!ValuePtr) return v8::Undefined(Isolate); const FJsObject* JsObject = static_cast(ValuePtr); return JsObject->GetJsObject(); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { if (Value->IsObject()) { auto Object = Value.As(); FJsObject JsObject(Context, Object); *static_cast(ValuePtr) = JsObject; } return true; } }; class FClassPropertyTranslator : public FObjectPropertyTranslator { public: explicit FClassPropertyTranslator(PropertyMacro* InProperty) : FObjectPropertyTranslator(InProperty) { } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { auto Object = FV8Utils::GetUObject(Context, Value); if (FV8Utils::IsReleasedPtr(Object)) { FV8Utils::ThrowException(Isolate, "passing a invalid object"); return false; } UClass* Class = Cast(Object); ObjectBaseProperty->SetObjectPropertyValue( ValuePtr, (Class && Class->IsChildOf(ClassProperty->MetaClass)) ? Class : nullptr); return true; } }; // containers class FScriptArrayPropertyTranslator : public FFastPropertyTranslator { public: explicit FScriptArrayPropertyTranslator(PropertyMacro* InProperty) : FFastPropertyTranslator(InProperty) { if (Property->HasAnyPropertyFlags(CPF_OutParm) && !Property->HasAnyPropertyFlags(CPF_ConstParm)) { ParamShallowCopySize = sizeof(FScriptArray); } } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool ByPointer) const override { FScriptArray* ScriptArray; if (ByPointer) { ScriptArray = const_cast(reinterpret_cast(&ArrayProperty->GetPropertyValue(ValuePtr))); } else { ScriptArray = reinterpret_cast(new FScriptArrayEx(ArrayProperty->Inner)); ArrayProperty->CopyCompleteValue(ScriptArray, ValuePtr); } return FV8Utils::IsolateData(Isolate)->FindOrAddContainer( Isolate, Context, ArrayProperty->Inner, ScriptArray, ByPointer); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { auto Ptr = FV8Utils::GetPointer(Context, Value); if (Ptr) { if (DeepCopy || !ParamShallowCopySize) { ArrayProperty->CopyCompleteValue(ValuePtr, Ptr); } else { FMemory::Memcpy(ValuePtr, Ptr, sizeof(FScriptArray)); } } return true; } private: }; class FScriptSetPropertyTranslator : public FFastPropertyTranslator { public: explicit FScriptSetPropertyTranslator(PropertyMacro* InProperty) : FFastPropertyTranslator(InProperty) { if (Property->HasAnyPropertyFlags(CPF_OutParm) && !Property->HasAnyPropertyFlags(CPF_ConstParm)) { ParamShallowCopySize = sizeof(FScriptSet); } } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool ByPointer) const override { FScriptSet* ScriptSet; if (ByPointer) { ScriptSet = const_cast(reinterpret_cast(&SetProperty->GetPropertyValue(ValuePtr))); } else { ScriptSet = reinterpret_cast(new FScriptSetEx(SetProperty->ElementProp)); SetProperty->CopyCompleteValue(ScriptSet, ValuePtr); } return FV8Utils::IsolateData(Isolate)->FindOrAddContainer( Isolate, Context, SetProperty->ElementProp, ScriptSet, ByPointer); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { auto Ptr = FV8Utils::GetPointer(Context, Value); if (Ptr) { if (DeepCopy || !ParamShallowCopySize) { SetProperty->CopyCompleteValue(ValuePtr, Ptr); } else { FMemory::Memcpy(ValuePtr, Ptr, sizeof(FScriptSet)); } } return true; } private: }; class FScriptMapPropertyTranslator : public FFastPropertyTranslator { public: explicit FScriptMapPropertyTranslator(PropertyMacro* InProperty) : FFastPropertyTranslator(InProperty) { if (Property->HasAnyPropertyFlags(CPF_OutParm) && !Property->HasAnyPropertyFlags(CPF_ConstParm)) { ParamShallowCopySize = sizeof(FScriptMap); } } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool ByPointer) const override { FScriptMap* ScriptMap; if (ByPointer) { ScriptMap = const_cast(reinterpret_cast(&MapProperty->GetPropertyValue(ValuePtr))); } else { ScriptMap = reinterpret_cast(new FScriptMapEx(MapProperty->KeyProp, MapProperty->ValueProp)); MapProperty->CopyCompleteValue(ScriptMap, ValuePtr); } return FV8Utils::IsolateData(Isolate)->FindOrAddContainer( Isolate, Context, MapProperty->KeyProp, MapProperty->ValueProp, ScriptMap, ByPointer); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { auto Ptr = FV8Utils::GetPointer(Context, Value); if (Ptr) { if (DeepCopy || !ParamShallowCopySize) { MapProperty->CopyCompleteValue(ValuePtr, Ptr); } else { FMemory::Memcpy(ValuePtr, Ptr, sizeof(FScriptMap)); } } return true; } private: }; // delegate //另外特殊处理 class DoNothingPropertyTranslator : public FPropertyTranslator { public: explicit DoNothingPropertyTranslator(PropertyMacro* InProperty) : FPropertyTranslator(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return v8::Undefined(Isolate); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { return true; } }; // fix array class FFixArrayReflection : public FPropertyTranslator { public: explicit FFixArrayReflection(std::unique_ptr InInner) : FPropertyTranslator(InInner->Property) { Inner = std::move(InInner); } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { return FV8Utils::IsolateData(Isolate)->CreateArray( Isolate, Context, Inner.get(), const_cast(ValuePtr)); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { // Fix Size Array do not assignable return true; } virtual void Cleanup(void* ContainerPtr) const { Inner->Cleanup(ContainerPtr); } }; // delegate class FDelegatePropertyTranslator : public FPropertyWithDestructorReflection { public: explicit FDelegatePropertyTranslator(PropertyMacro* InProperty) : FPropertyWithDestructorReflection(InProperty) { } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { auto DelegatePtr = static_cast(const_cast(ValuePtr)); if (DelegatePtr) { UObject* UEObject = DelegatePtr->GetUObject(); if (UEObject && UEObject->IsValidLowLevelFast() && !UEObjectIsPendingKill(UEObject)) { return FV8Utils::IsolateData(Isolate)->FindOrAddDelegate( Isolate, Context, UEObject, DelegateProperty, DelegatePtr, PassByPointer); } } return v8::Undefined(Isolate); } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { FScriptDelegate* Des = DelegateProperty->GetPropertyValuePtr(ValuePtr); if (Value->IsFunction()) { *Des = FV8Utils::IsolateData(Isolate)->NewDelegate( Isolate, Context, nullptr, Value.As(), DelegateProperty->SignatureFunction); } else { auto Src = static_cast(FV8Utils::GetPointer(Context, Value, 0)); if (Des && Src) { *Des = *Src; } else if (Des && Value->IsArray()) { auto Array = Value->ToObject(Context).ToLocalChecked(); auto Obj = FV8Utils::GetUObject(Context, Array->Get(Context, 0).ToLocalChecked()); if (Obj) { if (FV8Utils::IsReleasedPtr(Obj)) { FV8Utils::ThrowException(Isolate, "passing a invalid object"); return false; } auto Func = Array->Get(Context, 1).ToLocalChecked(); if (Func->IsString()) { FScriptDelegate Delegate; Delegate.BindUFunction(Obj, *FV8Utils::ToFString(Isolate, Func)); *Des = Delegate; } else if (Func->IsFunction()) { *Des = FV8Utils::IsolateData(Isolate)->NewDelegate( Isolate, Context, Obj, Func.As(), DelegateProperty->SignatureFunction); } } } } return true; } private: }; class FOutReflection : public FPropertyTranslator { public: explicit FOutReflection(std::unique_ptr InInner) : FPropertyTranslator(InInner->Property) { Inner = std::move(InInner); ParamShallowCopySize = Inner->ParamShallowCopySize; } // { // value : realvalue // } v8::Local UEToJs( v8::Isolate* Isolate, v8::Local& Context, const void* ValuePtr, bool PassByPointer) const override { auto Result = v8::Object::New(Isolate); auto ReturnVal = Result->Set(Context, 0, Inner->UEToJs(Isolate, Context, ValuePtr, PassByPointer)); return Result; } bool JsToUE(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { if (Value->IsObject()) { auto Outer = Value->ToObject(Context).ToLocalChecked(); auto Realvalue = Outer->Get(Context, 0).ToLocalChecked(); return Inner->JsToUE(Isolate, Context, Realvalue, ValuePtr, DeepCopy); } return true; } virtual bool JsToUEFast(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* TempBuff, void** OutValuePtr) const override { if (Value->IsObject()) { auto Outer = Value->ToObject(Context).ToLocalChecked(); auto Realvalue = Outer->Get(Context, 0).ToLocalChecked(); return Inner->JsToUEFast(Isolate, Context, Realvalue, TempBuff, OutValuePtr); } *OutValuePtr = TempBuff; return true; } void UEOutToJs(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, const void* ValuePtr, bool PassByPointer) const override { if (Value->IsObject()) { auto Outer = Value->ToObject(Context).ToLocalChecked(); if (Inner->ParamShallowCopySize) { auto Realvalue = Outer->Get(Context, 0).ToLocalChecked(); auto Ptr = FV8Utils::GetPointer(Context, Realvalue); if (Ptr && Ptr != ValuePtr) { FMemory::Memcpy(Ptr, ValuePtr, ParamShallowCopySize); return; } } auto ReturnVal = Outer->Set(Context, 0, Inner->UEToJs(Isolate, Context, ValuePtr, PassByPointer)); if (Inner->ParamShallowCopySize) // $ref(undefined) for shallow copy type { Property->DestroyValue(const_cast(ValuePtr)); } } } bool JsToUEOut(v8::Isolate* Isolate, v8::Local& Context, const v8::Local& Value, void* ValuePtr, bool DeepCopy) const override { return JsToUE(Isolate, Context, Value, ValuePtr, DeepCopy); } bool IsOut() const override { return true; } void Cleanup(void* ContainerPtr) const override { Inner->Cleanup(ContainerPtr); } }; template