/* * 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. */ #if USE_WASM3 #include "WasmJsFunctionParams.h" #include "ContainerMeta.h" #include "Misc/FileHelper.h" #include "Serialization/JsonReader.h" #include "WasmRuntime.h" #include "V8Utils.h" #include "JSLogger.h" #include "../ObjectMapper.h" #include "UECompatible.h" #include "WasmModuleInstance.h" #include "GenericPlatform/GenericPlatformMemory.h" namespace PUERTS_NAMESPACE { static void NormalInstanceCall(const v8::FunctionCallbackInfo& Info) { IM3Function _Function = static_cast((v8::Local::Cast(Info.Data()))->Value()); int argCount = _Function->funcType->numArgs; check(Info.Length() >= argCount); auto Isolate = Info.GetIsolate(); auto Context = Isolate->GetCurrentContext(); int64* args = (int64*) FMemory_Alloca(sizeof(int64) * argCount); void** argsPointer = (void**) FMemory_Alloca(sizeof(void*) * argCount); for (int Index = 0; Index < argCount; Index++) { const int type = _Function->funcType->types[_Function->funcType->numRets + Index]; switch (type) { case c_m3Type_i32: check(Info[Index]->IsNumber()); *((int*) (args + Index)) = Info[Index]->Int32Value(Context).ToChecked(); argsPointer[Index] = args + Index; break; case c_m3Type_i64: check(Info[Index]->IsBigInt()); *(args + Index) = Info[Index]->ToBigInt(Context).ToLocalChecked()->Int64Value(); argsPointer[Index] = args + Index; break; case c_m3Type_f32: check(Info[Index]->IsNumber()); *((float*) (args + Index)) = (float) Info[Index]->NumberValue(Context).ToChecked(); argsPointer[Index] = args + Index; break; case c_m3Type_f64: check(Info[Index]->IsNumber()); *((double*) (args + Index)) = Info[Index]->NumberValue(Context).ToChecked(); argsPointer[Index] = args + Index; break; default: check(0); } } if (Export_m3_Call(_Function, argCount, (const void**) argsPointer)) { check(_Function->funcType->numRets <= 1); if (_Function->funcType->numRets == 1) { int64 RetValue; void* ret[1] = {&RetValue}; if (Export_m3_GetResults(_Function, 1, (const void**) ret)) { const int type = _Function->funcType->types[0]; switch (type) { case c_m3Type_i32: Info.GetReturnValue().Set(*((int*) (&RetValue))); break; case c_m3Type_i64: Info.GetReturnValue().Set(v8::BigInt::New(Isolate, RetValue)); break; case c_m3Type_f32: Info.GetReturnValue().Set(*((float*) (&RetValue))); break; case c_m3Type_f64: Info.GetReturnValue().Set(*((double*) (&RetValue))); default: check(0); } } else { FV8Utils::ThrowException(Isolate, "call wasm get results failed"); } } } else { FV8Utils::ThrowException(Isolate, "call wasm failed"); } } static const void* NormalInstanceLink(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t* _sp, void* _mem) { IM3Function _Function = _ctx->function; WasmNormalLinkInfo* _Info = (WasmNormalLinkInfo*) _ctx->userdata; check(_Function->funcType->numRets <= 1); uint64_t* raw_sp = _sp; if (_Function->funcType->numRets > 0) { _sp++; } TArray, TInlineAllocator<20>> Params; check(_Function->funcType->numArgs < 20); for (int i = 0; i < _Function->funcType->numArgs; i++) { const int type = _Function->funcType->types[_Function->funcType->numRets + i]; switch (type) { case c_m3Type_i32: Params.Add(v8::Int32::New(_Info->Isolate, *((int32*) (_sp + i)))); break; case c_m3Type_i64: Params.Add(v8::BigInt::New(_Info->Isolate, *(_sp + i))); break; case c_m3Type_f32: Params.Add(v8::Number::New(_Info->Isolate, *((float*) (_sp + i)))); break; case c_m3Type_f64: Params.Add(v8::Number::New(_Info->Isolate, *((double*) (_sp + i)))); break; default: check(0); } } v8::TryCatch TryCatch(_Info->Isolate); auto Ret = _Info->CachedFunction.Get(_Info->Isolate) ->Call(_Info->Isolate->GetCurrentContext(), v8::Undefined(_Info->Isolate), Params.Num(), Params.GetData()); if (TryCatch.HasCaught()) { UE_LOG(Puerts, Error, TEXT("%s"), *FV8Utils::TryCatchToString(_Info->Isolate, &TryCatch)); return "some error"; } if (_Function->funcType->numRets == 1) { v8::Local RetValue = Ret.ToLocalChecked(); const int type = _Function->funcType->types[0]; switch (type) { case c_m3Type_i32: check(RetValue->IsNumber()); *((int*) (raw_sp)) = RetValue->Int32Value(_Info->Isolate->GetCurrentContext()).ToChecked(); break; case c_m3Type_i64: check(RetValue->IsBigInt()); *raw_sp = RetValue->ToBigInt(_Info->Isolate->GetCurrentContext()).ToLocalChecked()->Int64Value(); break; case c_m3Type_f32: check(RetValue->IsNumber()); *((float*) (raw_sp)) = RetValue->NumberValue(_Info->Isolate->GetCurrentContext()).ToChecked(); break; case c_m3Type_f64: check(RetValue->IsNumber()); *((double*) (raw_sp)) = RetValue->NumberValue(_Info->Isolate->GetCurrentContext()).ToChecked(); break; default: check(0); } } return nullptr; } WasmRuntime* NormalInstanceModule(v8::Isolate* Isolate, v8::Local& Context, TArray& InData, v8::Local& ExportsObject, v8::Local ImportsValue, const TArray>& RuntimeList, TArray& CachedLinkFunctionList) { WasmRuntime* UsedRuntime = RuntimeList[0].get(); v8::Local MemoryObject; v8::Local ImportsObject; //需要同时支持使用js mem以及 env memory if (ImportsValue->IsObject()) { v8::Local EnvObject; ImportsObject = ImportsValue.As(); { auto MaybeEnvValue = ImportsObject->Get(Context, FV8Utils::ToV8String(Isolate, "env")); if (!MaybeEnvValue.IsEmpty()) { auto EnvValue = MaybeEnvValue.ToLocalChecked(); if (EnvValue->IsObject()) { EnvObject = EnvValue.As(); auto MaybeMemory = EnvObject->Get(Context, FV8Utils::ToV8String(Isolate, "memory")); if (!MaybeMemory.IsEmpty()) { auto MemoryValue = MaybeMemory.ToLocalChecked(); if (MemoryValue->IsObject()) { MemoryObject = MemoryValue.As(); } } } } } if (EnvObject.IsEmpty()) { auto MaybeEnvValue = ImportsObject->Get(Context, FV8Utils::ToV8String(Isolate, "js")); if (!MaybeEnvValue.IsEmpty()) { auto EnvValue = MaybeEnvValue.ToLocalChecked(); if (EnvValue->IsObject()) { EnvObject = EnvValue.As(); auto MaybeMemory = EnvObject->Get(Context, FV8Utils::ToV8String(Isolate, "mem")); if (!MaybeMemory.IsEmpty()) { auto MemoryValue = MaybeMemory.ToLocalChecked(); if (MemoryValue->IsObject()) { MemoryObject = MemoryValue.As(); } } } } } } //默认情况下使用第一个runtime,其他情况使用memory里面指定的 if (!MemoryObject.IsEmpty()) { int Seq = MemoryObject->Get(Context, FV8Utils::ToV8String(Isolate, "_Seq")).ToLocalChecked()->Int32Value(Context).ToChecked(); for (auto r : RuntimeList) { if (r->GetRuntimeSeq() == Seq) { UsedRuntime = r.get(); break; } } } auto CustomLinkFunc = [&](IM3Module _Module) -> bool { if (!ImportsObject.IsEmpty()) { auto ModuleNames = ImportsObject->GetOwnPropertyNames(Context).ToLocalChecked(); for (decltype(ModuleNames->Length()) j = 0; j < ModuleNames->Length(); ++j) { v8::Local ModuleName; v8::Local ModuleValue; if (ModuleNames->Get(Context, j).ToLocal(&ModuleName) && ImportsObject->Get(Context, ModuleName).ToLocal(&ModuleValue)) { auto Module = ModuleValue.As(); v8::String::Utf8Value UtfModuleName(Isolate, ModuleName); auto FunctionNames = Module->GetOwnPropertyNames(Context).ToLocalChecked(); for (decltype(FunctionNames->Length()) i = 0; i < FunctionNames->Length(); ++i) { v8::Local FunctionName; v8::Local FunctionValue; if (FunctionNames->Get(Context, i).ToLocal(&FunctionName) && Module->Get(Context, FunctionName).ToLocal(&FunctionValue)) { if (FunctionValue->IsFunction()) { WasmNormalLinkInfo* NewInfo = new WasmNormalLinkInfo(); NewInfo->CachedFunction.Reset(Isolate, FunctionValue.As()); NewInfo->Isolate = Isolate; CachedLinkFunctionList.Add(NewInfo); if (!Export_m3_LinkRawFunctionEx(_Module, *UtfModuleName, (*(v8::String::Utf8Value(Isolate, FunctionName))), nullptr, &NormalInstanceLink, NewInfo)) { return false; } } } } } } } return true; }; WasmModuleInstance* NewInstance = new WasmModuleInstance(InData); if (NewInstance->ParseModule(UsedRuntime->GetEnv())) { //如果没有指明需要import memory,那么使用默认的runtime即可,即便外面传入了memory也不生效 if (!NewInstance->GetModule()->memoryImported) { UsedRuntime = RuntimeList[0].get(); } if (!NewInstance->LoadModule(UsedRuntime, 0, CustomLinkFunc)) { delete NewInstance; NewInstance = nullptr; } } else { delete NewInstance; NewInstance = nullptr; } if (NewInstance && NewInstance->GetAllExportFunctions().Num()) { IM3Module _Module = NewInstance->GetModule(); for (uint32 i = 0; i < _Module->numFunctions; ++i) { IM3Function f = &_Module->functions[i]; if (f->compiled && f->export_name && *(f->export_name)) { auto Data = v8::External::New(Isolate, f); auto Func = v8::Function::New(Context, NormalInstanceCall, Data).ToLocalChecked(); Func->Set(Context, FV8Utils::ToV8String(Isolate, M3_FUNCTION_KEY), Data); (void) ExportsObject->Set(Context, FV8Utils::ToV8String(Isolate, f->export_name), Func); } } } if (NewInstance->GetModule()->memoryExportName) { (void) ExportsObject->Set(Context, FV8Utils::ToV8String(Isolate, "__memoryExport"), FV8Utils::ToV8String(Isolate, NewInstance->GetModule()->memoryExportName)); } if (NewInstance->GetModule()->tableExportName) { (void) ExportsObject->Set(Context, FV8Utils::ToV8String(Isolate, "__tableExport"), FV8Utils::ToV8String(Isolate, NewInstance->GetModule()->tableExportName)); (void) ExportsObject->Set( Context, FV8Utils::ToV8String(Isolate, "__moduleIndex"), v8::Integer::New(Isolate, NewInstance->Index)); } return UsedRuntime; } } // namespace PUERTS_NAMESPACE #endif