/* * 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 #include "CoreMinimal.h" #include "CoreUObject.h" #include "NamespaceDef.h" #include "DataTransfer.h" #include "UECompatible.h" namespace PUERTS_NAMESPACE { enum ArgType { EArgInt32, EArgNumber, EArgString, EArgExternal, EArgFunction, EArgObject }; class JSENV_API FV8Utils { public: FORCEINLINE static void ThrowException(v8::Isolate* Isolate, const FString& Message) { ThrowException(Isolate, TCHAR_TO_ANSI(*Message)); } FORCEINLINE static void ThrowException(v8::Isolate* Isolate, const char* Message) { auto ExceptionStr = v8::String::NewFromUtf8(Isolate, Message, v8::NewStringType::kNormal).ToLocalChecked(); Isolate->ThrowException(v8::Exception::Error(ExceptionStr)); } FORCEINLINE static void* GetPointer(v8::Local& Context, v8::Local Value, int Index = 0) { if (Value.IsEmpty() || !Value->IsObject() || Value->IsUndefined() || Value->IsNull()) { return nullptr; } auto Object = Value->ToObject(Context).ToLocalChecked(); return GetPointerFast(Object, Index); } FORCEINLINE static void* GetPointer(v8::Local Object, int Index = 0) { if (Object.IsEmpty() || Object->IsUndefined() || Object->IsNull()) { return nullptr; } return GetPointerFast(Object, Index); } template FORCEINLINE static T* GetPointerFast(v8::Local Object, int Index = 0) { return DataTransfer::GetPointerFast(Object, Index); } FORCEINLINE static UObject* GetUObject(v8::Local& Context, v8::Local Value, int Index = 0) { auto UEObject = reinterpret_cast(GetPointer(Context, Value, Index)); return (!UEObject || (UEObject != RELEASED_UOBJECT && UEObject->IsValidLowLevelFast() && !UEObjectIsPendingKill(UEObject))) ? UEObject : RELEASED_UOBJECT; } FORCEINLINE static UObject* GetUObject(v8::Local Object, int Index = 0) { auto UEObject = reinterpret_cast(GetPointer(Object, Index)); return (!UEObject || (UEObject != RELEASED_UOBJECT && UEObject->IsValidLowLevelFast() && !UEObjectIsPendingKill(UEObject))) ? UEObject : RELEASED_UOBJECT; } FORCEINLINE static bool IsReleasedPtr(void* Ptr) { return RELEASED_UOBJECT_MEMBER == Ptr; } FORCEINLINE static v8::Local InternalString(v8::Isolate* Isolate, const FString& String) { return ToV8String(Isolate, String); } FORCEINLINE static v8::Local InternalString(v8::Isolate* Isolate, const char* String) { return v8::String::NewFromUtf8(Isolate, String, v8::NewStringType::kNormal).ToLocalChecked(); } static FString ToFString(v8::Isolate* Isolate, v8::Local Value); FORCEINLINE static FName ToFName(v8::Isolate* Isolate, v8::Local Value) { return UTF8_TO_TCHAR(*(v8::String::Utf8Value(Isolate, Value))); } FORCEINLINE static v8::Local ToV8String(v8::Isolate* Isolate, const FString& String) { // return ToV8String(Isolate, TCHAR_TO_UTF8(*String)); return ToV8String(Isolate, *String); } FORCEINLINE static v8::Local ToV8String(v8::Isolate* Isolate, const FName& String) { const FNameEntry* Entry = String.GetComparisonNameEntry(); FString Out; if (String.GetNumber() == NAME_NO_NUMBER_INTERNAL) { Out.Empty(Entry->GetNameLength()); Entry->AppendNameToString(Out); } else { Out.Empty(Entry->GetNameLength() + 6); Entry->AppendNameToString(Out); Out += TEXT('_'); Out.AppendInt(NAME_INTERNAL_TO_EXTERNAL(String.GetNumber())); } return ToV8String(Isolate, Out); } FORCEINLINE static v8::Local ToV8String(v8::Isolate* Isolate, const FText& String) { return ToV8String(Isolate, String.ToString()); } static v8::Local ToV8String(v8::Isolate* Isolate, const TCHAR* String); FORCEINLINE static v8::Local ToV8String(v8::Isolate* Isolate, const char* String) { return v8::String::NewFromUtf8(Isolate, String, v8::NewStringType::kNormal).ToLocalChecked(); } static v8::Local ToV8StringFromFileContent(v8::Isolate* Isolate, const TArray& FileContent) { const uint8* Buffer = FileContent.GetData(); auto Size = FileContent.Num(); if (Size >= 2 && !(Size & 1) && ((Buffer[0] == 0xff && Buffer[1] == 0xfe) || (Buffer[0] == 0xfe && Buffer[1] == 0xff))) { FString Content; FFileHelper::BufferToString(Content, Buffer, Size); return ToV8String(Isolate, Content); } else { if (Size >= 3 && Buffer[0] == 0xef && Buffer[1] == 0xbb && Buffer[2] == 0xbf) { // Skip over UTF-8 BOM if there is one Buffer += 3; Size -= 3; } return v8::String::NewFromUtf8(Isolate, (const char*) Buffer, v8::NewStringType::kNormal, Size).ToLocalChecked(); } } template FORCEINLINE static T* IsolateData(v8::Isolate* Isolate) { return DataTransfer::IsolateData(Isolate); } FORCEINLINE static bool CheckArgumentLength(const v8::FunctionCallbackInfo& Info, int32 Length) { if (Info.Length() < Length) { ThrowException(Info.GetIsolate(), FString::Printf(TEXT("Bad parameters, the function expect %d, but %d provided."), Length, Info.Length())); return false; } return true; } FORCEINLINE static FString TryCatchToString(v8::Isolate* Isolate, v8::TryCatch* TryCatch) { v8::Isolate::Scope IsolateScope(Isolate); v8::HandleScope HandleScope(Isolate); v8::String::Utf8Value Exception(Isolate, TryCatch->Exception()); FString ExceptionStr(UTF8_TO_TCHAR(*Exception)); v8::Local Message = TryCatch->Message(); if (Message.IsEmpty()) { // 如果没有提供更详细的信息,直接输出Exception return ExceptionStr; } else { v8::Local Context(Isolate->GetCurrentContext()); // 输出 (filename):(line number): (message). v8::String::Utf8Value FileName(Isolate, Message->GetScriptResourceName()); int LineNum = Message->GetLineNumber(Context).FromJust(); FString FileNameStr(*FileName); FString LineNumStr = FString::FromInt(LineNum); FString FileInfoStr; FileInfoStr.Append(FileNameStr).Append(":").Append(LineNumStr).Append(": ").Append(ExceptionStr); FString FinalReport; FinalReport.Append(FileInfoStr).Append("\n"); // 输出调用栈信息 v8::Local StackTrace; if (TryCatch->StackTrace(Context).ToLocal(&StackTrace)) { v8::String::Utf8Value StackTraceVal(Isolate, StackTrace); FString StackTraceStr(*StackTraceVal); FinalReport.Append("\n").Append(StackTraceStr); } return FinalReport; } } FORCEINLINE static bool CheckArgument(const v8::FunctionCallbackInfo& Info, const std::vector& TypesExpect) { if (Info.Length() < TypesExpect.size()) { ThrowException(Info.GetIsolate(), FString::Printf(TEXT("Bad parameters, the function expect %d, but %d provided."), TypesExpect.size(), Info.Length())); return false; } for (int i = 0; i < TypesExpect.size(); ++i) { switch (TypesExpect[i]) { case EArgInt32: if (!Info[i]->IsInt32()) { ThrowException(Info.GetIsolate(), FString::Printf(TEXT("Bad parameters #%d, expect a int32."), i)); return false; } else { break; } case EArgNumber: if (!Info[i]->IsNumber()) { ThrowException(Info.GetIsolate(), FString::Printf(TEXT("Bad parameters #%d, expect a int32."), i)); return false; } else { break; } case EArgString: if (!Info[i]->IsString()) { ThrowException(Info.GetIsolate(), FString::Printf(TEXT("Bad parameters #%d, expect a string."), i)); return false; } else { break; } case EArgExternal: if (!Info[i]->IsExternal()) { ThrowException(Info.GetIsolate(), FString::Printf(TEXT("Bad parameters #%d, expect an external."), i)); return false; } else { break; } case EArgFunction: if (!Info[i]->IsFunction()) { ThrowException(Info.GetIsolate(), FString::Printf(TEXT("Bad parameters #%d, expect a function."), i)); return false; } else { break; } case EArgObject: if (!Info[i]->IsObject()) { ThrowException(Info.GetIsolate(), FString::Printf(TEXT("Bad parameters #%d, expect a object."), i)); return false; } else { break; } default: break; } } return true; } }; } // namespace PUERTS_NAMESPACE #define CHECK_V8_ARGS_LEN(Length) \ if (!FV8Utils::CheckArgumentLength(Info, Length)) \ { \ return; \ } #define CHECK_V8_ARGS(...) \ static std::vector ArgExpect = {__VA_ARGS__}; \ if (!FV8Utils::CheckArgument(Info, ArgExpect)) \ { \ return; \ }