From 324e8f79c593819af7072440cbf868b0584d86f8 Mon Sep 17 00:00:00 2001 From: wengchangcheng Date: Tue, 7 Feb 2023 18:36:08 +0800 Subject: [PATCH] Descriptor: Support async/await in taskpool Details: Trigger concurrent callback before return Issue: https://gitee.com/open_harmony/dashboard?issue_id=I6DC6E Signed-off-by: wengchangcheng Change-Id: I6377d90157f0452446e932f8c4c1d14ff22c3109 --- ecmascript/ecma_vm.cpp | 22 ++++++++++++++++++++++ ecmascript/ecma_vm.h | 16 +++++++++++++++- ecmascript/interpreter/interpreter-inl.h | 2 ++ ecmascript/js_function_kind.h | 7 ++----- ecmascript/js_serializer.cpp | 8 ++++---- ecmascript/js_serializer.h | 2 +- ecmascript/napi/include/jsnapi.h | 5 +++++ ecmascript/napi/jsnapi.cpp | 7 +++++++ ecmascript/object_factory.cpp | 2 ++ ecmascript/stubs/runtime_stubs-inl.h | 2 +- ecmascript/tests/js_serializer_test.cpp | 10 +++++----- 11 files changed, 66 insertions(+), 17 deletions(-) diff --git a/ecmascript/ecma_vm.cpp b/ecmascript/ecma_vm.cpp index 369fa45f5d..a85933771d 100644 --- a/ecmascript/ecma_vm.cpp +++ b/ecmascript/ecma_vm.cpp @@ -955,4 +955,26 @@ JSTaggedValue EcmaVM::GetMethodByIndex(MethodIndex idx) ASSERT(index < internalNativeMethods_.size()); return internalNativeMethods_[index]; } + +void EcmaVM::TriggerConcurrentCallback(JSTaggedValue result, JSTaggedValue hint) +{ + if (concurrentCallback_ == nullptr) { + LOG_ECMA(INFO) << "Only trigger concurrent callback in taskpool thread"; + return; + } + + if (result.IsJSPromise()) { + // Async concurrent will return Promise + auto promise = JSPromise::Cast(result.GetTaggedObject()); + if (promise->GetPromiseState() == PromiseState::PENDING) { + LOG_ECMA(ERROR) << "Promise is in pending state, don't return"; + return; + } + result = promise->GetPromiseResult(); + } + + auto ret = JSNApiHelper::ToLocal(JSHandle(thread_, result)); + auto data = JSNApiHelper::ToLocal(JSHandle(thread_, hint)); + concurrentCallback_(ret, data, concurrentData_); +} } // namespace panda::ecmascript diff --git a/ecmascript/ecma_vm.h b/ecmascript/ecma_vm.h index 6a3eeb174a..dcab4cf160 100644 --- a/ecmascript/ecma_vm.h +++ b/ecmascript/ecma_vm.h @@ -16,6 +16,8 @@ #ifndef ECMASCRIPT_ECMA_VM_H #define ECMASCRIPT_ECMA_VM_H +#include + #include "ecmascript/base/config.h" #include "ecmascript/builtins/builtins_method_index.h" #include "ecmascript/js_handle.h" @@ -23,9 +25,9 @@ #include "ecmascript/js_thread.h" #include "ecmascript/mem/c_containers.h" #include "ecmascript/mem/c_string.h" +#include "ecmascript/napi/include/jsnapi.h" #include "ecmascript/taskpool/taskpool.h" #include "ecmascript/waiter_list.h" -#include namespace panda { class JSNApi; @@ -371,6 +373,14 @@ public: return resolveBufferCallback_; } + void SetConcurrentCallback(ConcurrentCallback callback, void *data) + { + concurrentCallback_ = callback; + concurrentData_ = data; + } + + void TriggerConcurrentCallback(JSTaggedValue result, JSTaggedValue hint); + void AddConstpool(const JSPandaFile *jsPandaFile, JSTaggedValue constpool, int32_t index = 0); bool HasCachedConstpool(const JSPandaFile *jsPandaFile) const; @@ -655,6 +665,10 @@ private: ResolvePathCallback resolvePathCallback_ {nullptr}; ResolveBufferCallback resolveBufferCallback_ {nullptr}; + // Concurrent taskpool callback and data + ConcurrentCallback concurrentCallback_ {nullptr}; + void *concurrentData_ {nullptr}; + // vm parameter configurations EcmaParamConfiguration ecmaParamConfiguration_; #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER) diff --git a/ecmascript/interpreter/interpreter-inl.h b/ecmascript/interpreter/interpreter-inl.h index f3f50e59a5..9dda1c2005 100644 --- a/ecmascript/interpreter/interpreter-inl.h +++ b/ecmascript/interpreter/interpreter-inl.h @@ -1354,6 +1354,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t methodHandle.Update(JSFunction::Cast(state->function.GetTaggedObject())->GetMethod()); [[maybe_unused]] auto fistPC = methodHandle->GetBytecodeArray(); UPDATE_HOTNESS_COUNTER(-(pc - fistPC)); + JSTaggedType *currentSp = sp; sp = state->base.prev; ASSERT(sp != nullptr); @@ -1401,6 +1402,7 @@ NO_UB_SANITIZE void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t methodHandle.Update(JSFunction::Cast(state->function.GetTaggedObject())->GetMethod()); [[maybe_unused]] auto fistPC = methodHandle->GetBytecodeArray(); UPDATE_HOTNESS_COUNTER_NON_ACC(-(pc - fistPC)); + JSTaggedType *currentSp = sp; sp = state->base.prev; ASSERT(sp != nullptr); diff --git a/ecmascript/js_function_kind.h b/ecmascript/js_function_kind.h index b6b62121dc..440af8657c 100644 --- a/ecmascript/js_function_kind.h +++ b/ecmascript/js_function_kind.h @@ -21,8 +21,6 @@ namespace panda::ecmascript { enum class FunctionKind : uint8_t { NORMAL_FUNCTION = 0, - // Concurrent function is normal function with concurrent property - CONCURRENT_FUNCTION, // BEGIN accessors GETTER_FUNCTION, SETTER_FUNCTION, @@ -32,19 +30,18 @@ enum class FunctionKind : uint8_t { // BEGIN async functions ASYNC_ARROW_FUNCTION, // END arrow functions + // Concurrent function is async function with concurrent property + CONCURRENT_FUNCTION, ASYNC_FUNCTION, // END async functions // BEGIN base constructors BASE_CONSTRUCTOR, - // BEGIN default constructors - DEFAULT_BASE_CONSTRUCTOR, // BEGIN class constructors CLASS_CONSTRUCTOR, // END base constructors // BEGIN constructable functions BUILTIN_PROXY_CONSTRUCTOR, BUILTIN_CONSTRUCTOR, - // END default constructors DERIVED_CONSTRUCTOR, // END class constructors GENERATOR_FUNCTION, diff --git a/ecmascript/js_serializer.cpp b/ecmascript/js_serializer.cpp index 0282570713..46227f6280 100644 --- a/ecmascript/js_serializer.cpp +++ b/ecmascript/js_serializer.cpp @@ -310,7 +310,7 @@ bool JSSerializer::WriteTaggedObject(const JSHandle &value) return WritePlainObject(value); case JSType::JS_NATIVE_POINTER: return WriteNativePointer(value); - case JSType::JS_FUNCTION: + case JSType::JS_ASYNC_FUNCTION: // means CONCURRENT_FUNCTION return WriteJSFunction(value); case JSType::METHOD: return WriteMethod(value); @@ -483,7 +483,7 @@ bool JSSerializer::WriteMethod(const JSHandle &value) bool JSSerializer::WriteJSFunction(const JSHandle &value) { size_t oldSize = bufferSize_; - if (!WriteType(SerializationUID::JS_FUNCTION)) { + if (!WriteType(SerializationUID::CONCURRENT_FUNCTION)) { return false; } JSHandle func = JSHandle::Cast(value); @@ -1229,7 +1229,7 @@ JSHandle JSDeserializer::DeserializeJSTaggedValue() return ReadJSArrayBuffer(); case SerializationUID::TAGGED_OBJECT_REFERNCE: return ReadReference(); - case SerializationUID::JS_FUNCTION: + case SerializationUID::CONCURRENT_FUNCTION: return ReadJSFunction(); case SerializationUID::TAGGED_ARRAY: return ReadTaggedArray(); @@ -1382,7 +1382,7 @@ JSHandle JSDeserializer::ReadNativeMethod() JSHandle JSDeserializer::ReadJSFunction() { JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); - JSHandle func = factory_->NewJSFunction(env); + JSHandle func = factory_->NewJSFunction(env, nullptr, FunctionKind::CONCURRENT_FUNCTION); JSHandle funcTag(func); referenceMap_.emplace(objectId_++, funcTag); JSHandle methodVal = DeserializeJSTaggedValue(); diff --git a/ecmascript/js_serializer.h b/ecmascript/js_serializer.h index 69c676cd94..54ba3938ad 100644 --- a/ecmascript/js_serializer.h +++ b/ecmascript/js_serializer.h @@ -94,7 +94,7 @@ enum class SerializationUID : uint8_t { ERROR_MESSAGE_BEGIN, ERROR_MESSAGE_END, // Function begin - JS_FUNCTION, + CONCURRENT_FUNCTION, JS_METHOD, NATIVE_METHOD, CONSTANT_POOL, diff --git a/ecmascript/napi/include/jsnapi.h b/ecmascript/napi/include/jsnapi.h index a551bcade4..c24aa05a0c 100644 --- a/ecmascript/napi/include/jsnapi.h +++ b/ecmascript/napi/include/jsnapi.h @@ -38,6 +38,9 @@ class CopyableGlobal; template class Global; class JSNApi; +template +class Local; +class JSValueRef; class PrimitiveRef; class ArrayRef; class StringRef; @@ -65,6 +68,7 @@ using QuickFixQueryCallBack = bool (*)(std::string, std::string &, void **, size using EcmaVM = ecmascript::EcmaVM; using JSThread = ecmascript::JSThread; using JSTaggedType = uint64_t; +using ConcurrentCallback = void (*)(Local val, Local hint, void *data); static constexpr size_t DEFAULT_GC_THREAD_NUM = 7; static constexpr size_t DEFAULT_LONG_PAUSE_TIME = 40; @@ -1297,6 +1301,7 @@ public: static void SetAssetPath(EcmaVM *vm, const std::string &assetPath); static void SetLoop(EcmaVM *vm, void *loop); static std::string GetAssetPath(EcmaVM *vm); + static bool InitForConcurrentThread(EcmaVM *vm, ConcurrentCallback cb, void *data); static bool InitForConcurrentFunction(EcmaVM *vm, Local func); static void SetBundleName(EcmaVM *vm, std::string bundleName); static std::string GetBundleName(EcmaVM *vm); diff --git a/ecmascript/napi/jsnapi.cpp b/ecmascript/napi/jsnapi.cpp index 83125ae395..103e40ebf0 100644 --- a/ecmascript/napi/jsnapi.cpp +++ b/ecmascript/napi/jsnapi.cpp @@ -2699,6 +2699,13 @@ std::string JSNApi::GetAssetPath(EcmaVM *vm) return vm->GetAssetPath().c_str(); } +bool JSNApi::InitForConcurrentThread(EcmaVM *vm, ConcurrentCallback cb, void *data) +{ + vm->SetConcurrentCallback(cb, data); + + return true; +} + bool JSNApi::InitForConcurrentFunction(EcmaVM *vm, Local function) { [[maybe_unused]] LocalScope scope(vm); diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index d2f3fd1018..245f6180ce 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -1387,6 +1387,8 @@ JSHandle ObjectFactory::NewJSFunction(const JSHandle &env hclass = JSHandle::Cast(env->GetFunctionClassWithProto()); } else if (JSFunction::IsConstructorKind(kind)) { hclass = JSHandle::Cast(env->GetConstructorFunctionClass()); + } else if (kind == FunctionKind::CONCURRENT_FUNCTION) { + hclass = JSHandle::Cast(env->GetAsyncFunctionClass()); } else { hclass = JSHandle::Cast(env->GetNormalFunctionClass()); } diff --git a/ecmascript/stubs/runtime_stubs-inl.h b/ecmascript/stubs/runtime_stubs-inl.h index 80b2421993..c245be9709 100644 --- a/ecmascript/stubs/runtime_stubs-inl.h +++ b/ecmascript/stubs/runtime_stubs-inl.h @@ -1795,7 +1795,6 @@ JSTaggedValue RuntimeStubs::RuntimeDefinefunc(JSThread *thread, const JSHandleGetFunctionKind(); switch (kind) { case FunctionKind::NORMAL_FUNCTION: - case FunctionKind::CONCURRENT_FUNCTION: case FunctionKind::BASE_CONSTRUCTOR: { auto hclass = JSHandle::Cast(env->GetFunctionClassWithProto()); jsFunc = factory->NewJSFunctionByHClass(methodHandle, hclass); @@ -1811,6 +1810,7 @@ JSTaggedValue RuntimeStubs::RuntimeDefinefunc(JSThread *thread, const JSHandleNewJSFunctionByHClass(methodHandle, generatorClass); break; } + case FunctionKind::CONCURRENT_FUNCTION: case FunctionKind::ASYNC_FUNCTION: { auto asyncClass = JSHandle::Cast(env->GetAsyncFunctionClass()); jsFunc = factory->NewJSFunctionByHClass(methodHandle, asyncClass); diff --git a/ecmascript/tests/js_serializer_test.cpp b/ecmascript/tests/js_serializer_test.cpp index 9eb59fe30f..dd7fe0bc58 100644 --- a/ecmascript/tests/js_serializer_test.cpp +++ b/ecmascript/tests/js_serializer_test.cpp @@ -1553,7 +1553,7 @@ HWTEST_F_L0(JSSerializerTest, SerializeFunction) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - JSHandle jsFunction = factory->NewJSFunction(env); + JSHandle jsFunction = factory->NewJSFunction(env, nullptr, FunctionKind::CONCURRENT_FUNCTION); EXPECT_TRUE(jsFunction->IsJSFunction()); JSSerializer *serializer = new JSSerializer(thread); @@ -1571,9 +1571,9 @@ HWTEST_F_L0(JSSerializerTest, SerializeObjectWithFunction) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); - JSHandle function1 = env->GetFunctionFunction(); + JSHandle function1 = factory->NewJSFunction(env, nullptr, FunctionKind::CONCURRENT_FUNCTION); EXPECT_TRUE(function1->IsJSFunction()); - JSHandle function2 = env->GetFunctionFunction(); + JSHandle function2 = factory->NewJSFunction(env, nullptr, FunctionKind::CONCURRENT_FUNCTION); EXPECT_TRUE(function2->IsJSFunction()); JSHandle key1(factory->NewFromASCII("1")); JSHandle key2(factory->NewFromASCII("2")); @@ -1585,9 +1585,9 @@ HWTEST_F_L0(JSSerializerTest, SerializeObjectWithFunction) JSHandle value3(factory->NewFromASCII("value")); JSHandle obj = factory->NewEmptyJSObject(); JSObject::SetProperty(thread, JSHandle(obj), key1, value1); - JSObject::SetProperty(thread, JSHandle(obj), key2, function1); + JSObject::SetProperty(thread, JSHandle(obj), key2, JSHandle(function1)); JSObject::SetProperty(thread, JSHandle(obj), key3, value2); - JSObject::SetProperty(thread, JSHandle(obj), key4, function2); + JSObject::SetProperty(thread, JSHandle(obj), key4, JSHandle(function2)); JSObject::SetProperty(thread, JSHandle(obj), key5, value3); JSSerializer *serializer = new JSSerializer(thread);