133eb0b6dSopenharmony_ci/*
233eb0b6dSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
333eb0b6dSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
433eb0b6dSopenharmony_ci * you may not use this file except in compliance with the License.
533eb0b6dSopenharmony_ci * You may obtain a copy of the License at
633eb0b6dSopenharmony_ci *
733eb0b6dSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
833eb0b6dSopenharmony_ci *
933eb0b6dSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1033eb0b6dSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1133eb0b6dSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1233eb0b6dSopenharmony_ci * See the License for the specific language governing permissions and
1333eb0b6dSopenharmony_ci * limitations under the License.
1433eb0b6dSopenharmony_ci */
1533eb0b6dSopenharmony_ci
1633eb0b6dSopenharmony_ci#ifndef FOUNDATION_ACE_NAPI_NATIVE_ENGINE_NATIVE_ASYNC_HOOK_CONTEXT_H
1733eb0b6dSopenharmony_ci#define FOUNDATION_ACE_NAPI_NATIVE_ENGINE_NATIVE_ASYNC_HOOK_CONTEXT_H
1833eb0b6dSopenharmony_ci
1933eb0b6dSopenharmony_ci#include "callback_scope_manager/native_callback_scope_manager.h"
2033eb0b6dSopenharmony_ci#include "ecmascript/napi/include/jsnapi.h"
2133eb0b6dSopenharmony_ci#include "native_value.h"
2233eb0b6dSopenharmony_ci#include "native_engine.h"
2333eb0b6dSopenharmony_ci#include "utils/log.h"
2433eb0b6dSopenharmony_ci
2533eb0b6dSopenharmony_ciclass NativeAsyncHookContext;
2633eb0b6dSopenharmony_cistatic panda::JSValueRef* InternalMakeCallback(NativeEngine* engine, panda::FunctionRef* funRef,
2733eb0b6dSopenharmony_ci                                               panda::JSValueRef* obj, panda::JSValueRef *const argv[],
2833eb0b6dSopenharmony_ci                                               size_t argc, NativeAsyncHookContext* asyncContext);
2933eb0b6dSopenharmony_ci
3033eb0b6dSopenharmony_cienum CallbackScopeCode {
3133eb0b6dSopenharmony_ci    CALLBACK_SCOPE_OK = 0,
3233eb0b6dSopenharmony_ci    CALLBACK_SCOPE_INVALID_ARG,
3333eb0b6dSopenharmony_ci    CALLBACK_SCOPE_MISMATCH,
3433eb0b6dSopenharmony_ci};
3533eb0b6dSopenharmony_ci
3633eb0b6dSopenharmony_ciclass NativeAsyncHookContext {
3733eb0b6dSopenharmony_cipublic:
3833eb0b6dSopenharmony_ci    NativeAsyncHookContext(NativeEngine* env,
3933eb0b6dSopenharmony_ci                           panda::Local<panda::ObjectRef> resourceObject,
4033eb0b6dSopenharmony_ci                           const panda::Local<panda::StringRef> resourceName,
4133eb0b6dSopenharmony_ci                           bool isExternalResource) : env_(env), resource_(env->GetEcmaVm(), resourceObject)
4233eb0b6dSopenharmony_ci    {
4333eb0b6dSopenharmony_ci        asyncId_ = env->NewAsyncId();
4433eb0b6dSopenharmony_ci        triggerAsyncId_ = env->GetDefaultTriggerAsyncId();
4533eb0b6dSopenharmony_ci        lostReference_ = false;
4633eb0b6dSopenharmony_ci        if (isExternalResource) {
4733eb0b6dSopenharmony_ci            resource_.SetWeak();
4833eb0b6dSopenharmony_ci            resource_.SetWeakCallback(reinterpret_cast<void*>(this), FreeGlobalCallBack, NativeFinalizeCallBack);
4933eb0b6dSopenharmony_ci        }
5033eb0b6dSopenharmony_ci
5133eb0b6dSopenharmony_ci        NativeAsyncWrap::EmitAsyncInit(env,
5233eb0b6dSopenharmony_ci                                       resourceObject,
5333eb0b6dSopenharmony_ci                                       resourceName,
5433eb0b6dSopenharmony_ci                                       asyncId_,
5533eb0b6dSopenharmony_ci                                       triggerAsyncId_);
5633eb0b6dSopenharmony_ci    }
5733eb0b6dSopenharmony_ci
5833eb0b6dSopenharmony_ci    ~NativeAsyncHookContext()
5933eb0b6dSopenharmony_ci    {
6033eb0b6dSopenharmony_ci        resource_.FreeGlobalHandleAddr();
6133eb0b6dSopenharmony_ci        lostReference_ = true;
6233eb0b6dSopenharmony_ci        NativeAsyncWrap::EmitDestroy(env_, asyncId_);
6333eb0b6dSopenharmony_ci    }
6433eb0b6dSopenharmony_ci
6533eb0b6dSopenharmony_ci    static void FreeGlobalCallBack(void* ref)
6633eb0b6dSopenharmony_ci    {
6733eb0b6dSopenharmony_ci        auto that = reinterpret_cast<NativeAsyncHookContext*>(ref);
6833eb0b6dSopenharmony_ci        that->resource_.FreeGlobalHandleAddr();
6933eb0b6dSopenharmony_ci        that->lostReference_ = true;
7033eb0b6dSopenharmony_ci    }
7133eb0b6dSopenharmony_ci
7233eb0b6dSopenharmony_ci    static void NativeFinalizeCallBack(void* ref) {}
7333eb0b6dSopenharmony_ci
7433eb0b6dSopenharmony_ci    inline AsyncIdInfo GetAsyncIdInfo()
7533eb0b6dSopenharmony_ci    {
7633eb0b6dSopenharmony_ci        return {asyncId_, triggerAsyncId_};
7733eb0b6dSopenharmony_ci    }
7833eb0b6dSopenharmony_ci
7933eb0b6dSopenharmony_ci    inline NativeCallbackScope* OpenCallbackScope()
8033eb0b6dSopenharmony_ci    {
8133eb0b6dSopenharmony_ci        EnsureReference();
8233eb0b6dSopenharmony_ci        auto callbackScopeManager = env_->GetCallbackScopeManager();
8333eb0b6dSopenharmony_ci        if (callbackScopeManager == nullptr) {
8433eb0b6dSopenharmony_ci            return nullptr;
8533eb0b6dSopenharmony_ci        }
8633eb0b6dSopenharmony_ci
8733eb0b6dSopenharmony_ci        auto callbackScope = callbackScopeManager->Open(env_, resource_.ToLocal(), GetAsyncIdInfo());
8833eb0b6dSopenharmony_ci        callbackScopeManager->IncrementOpenCallbackScopes();
8933eb0b6dSopenharmony_ci
9033eb0b6dSopenharmony_ci        return callbackScope;
9133eb0b6dSopenharmony_ci    }
9233eb0b6dSopenharmony_ci
9333eb0b6dSopenharmony_ci    inline void EnsureReference()
9433eb0b6dSopenharmony_ci    {
9533eb0b6dSopenharmony_ci        if (lostReference_) {
9633eb0b6dSopenharmony_ci            auto ecmaVm = env_->GetEcmaVm();
9733eb0b6dSopenharmony_ci            panda::Global<panda::ObjectRef> resource(ecmaVm, panda::ObjectRef::New(ecmaVm));
9833eb0b6dSopenharmony_ci            resource_ = resource;
9933eb0b6dSopenharmony_ci            lostReference_ = false;
10033eb0b6dSopenharmony_ci        }
10133eb0b6dSopenharmony_ci    }
10233eb0b6dSopenharmony_ci
10333eb0b6dSopenharmony_ci    static CallbackScopeCode CloseCallbackScope(NativeEngine* env, NativeCallbackScope* scope)
10433eb0b6dSopenharmony_ci    {
10533eb0b6dSopenharmony_ci        auto callbackScopeManager = env->GetCallbackScopeManager();
10633eb0b6dSopenharmony_ci        if (callbackScopeManager == nullptr) {
10733eb0b6dSopenharmony_ci            return CALLBACK_SCOPE_INVALID_ARG;
10833eb0b6dSopenharmony_ci        }
10933eb0b6dSopenharmony_ci        if (callbackScopeManager->GetOpenCallbackScopes() > 0) {
11033eb0b6dSopenharmony_ci            callbackScopeManager->DecrementOpenCallbackScopes();
11133eb0b6dSopenharmony_ci            callbackScopeManager->Close(scope);
11233eb0b6dSopenharmony_ci            return CALLBACK_SCOPE_OK;
11333eb0b6dSopenharmony_ci        } else {
11433eb0b6dSopenharmony_ci            return CALLBACK_SCOPE_MISMATCH;
11533eb0b6dSopenharmony_ci        }
11633eb0b6dSopenharmony_ci    }
11733eb0b6dSopenharmony_ci
11833eb0b6dSopenharmony_ci    panda::JSValueRef* MakeCallback(panda::FunctionRef* funRef, panda::JSValueRef* obj,
11933eb0b6dSopenharmony_ci                                    panda::JSValueRef *const argv[], size_t argc)
12033eb0b6dSopenharmony_ci    {
12133eb0b6dSopenharmony_ci        EnsureReference();
12233eb0b6dSopenharmony_ci        panda::JSValueRef* rst = InternalMakeCallback(env_, funRef, obj, argv, argc, this);
12333eb0b6dSopenharmony_ci        return rst;
12433eb0b6dSopenharmony_ci    }
12533eb0b6dSopenharmony_ciprivate:
12633eb0b6dSopenharmony_ci    NativeEngine* env_ {nullptr};
12733eb0b6dSopenharmony_ci    double asyncId_ = 0;
12833eb0b6dSopenharmony_ci    double triggerAsyncId_ = 0;
12933eb0b6dSopenharmony_ci    panda::Global<panda::ObjectRef> resource_;
13033eb0b6dSopenharmony_ci    bool lostReference_ = false;
13133eb0b6dSopenharmony_ci};
13233eb0b6dSopenharmony_ci
13333eb0b6dSopenharmony_cistatic panda::FunctionRef* AsyncHooksCallbackTrampoline()
13433eb0b6dSopenharmony_ci{
13533eb0b6dSopenharmony_ci    return nullptr; // not support async_hook yet, and the actions need to be improved in the future
13633eb0b6dSopenharmony_ci}
13733eb0b6dSopenharmony_ci
13833eb0b6dSopenharmony_cistatic void CloseScope(NativeAsyncHookContext* nativeAsyncContext,
13933eb0b6dSopenharmony_ci                       NativeCallbackScopeManager* callbackScopeMgr,
14033eb0b6dSopenharmony_ci                       NativeCallbackScope* callbackScope,
14133eb0b6dSopenharmony_ci                       NativeEngine* engine)
14233eb0b6dSopenharmony_ci{
14333eb0b6dSopenharmony_ci    if (nativeAsyncContext != nullptr) {
14433eb0b6dSopenharmony_ci        nativeAsyncContext->CloseCallbackScope(engine, callbackScope);
14533eb0b6dSopenharmony_ci    } else {
14633eb0b6dSopenharmony_ci        if (callbackScopeMgr != nullptr) {
14733eb0b6dSopenharmony_ci            callbackScopeMgr->Close(callbackScope);
14833eb0b6dSopenharmony_ci        }
14933eb0b6dSopenharmony_ci    }
15033eb0b6dSopenharmony_ci}
15133eb0b6dSopenharmony_ci
15233eb0b6dSopenharmony_cistatic panda::JSValueRef* InternalMakeCallback(NativeEngine* engine, panda::FunctionRef* funRef,
15333eb0b6dSopenharmony_ci                                               panda::JSValueRef* obj, panda::JSValueRef* const argv[],
15433eb0b6dSopenharmony_ci                                               size_t argc, NativeAsyncHookContext* asyncContext)
15533eb0b6dSopenharmony_ci{
15633eb0b6dSopenharmony_ci    auto vm = engine->GetEcmaVm();
15733eb0b6dSopenharmony_ci    if (funRef == nullptr) {
15833eb0b6dSopenharmony_ci        HILOG_ERROR("funRef is nullptr!");
15933eb0b6dSopenharmony_ci        return *panda::JSValueRef::Undefined(vm);
16033eb0b6dSopenharmony_ci    }
16133eb0b6dSopenharmony_ci
16233eb0b6dSopenharmony_ci    bool useAsyncHooksTrampoline = false;
16333eb0b6dSopenharmony_ci    panda::FunctionRef* rstFunRef = AsyncHooksCallbackTrampoline();
16433eb0b6dSopenharmony_ci    if (rstFunRef != nullptr) {
16533eb0b6dSopenharmony_ci        useAsyncHooksTrampoline = true;
16633eb0b6dSopenharmony_ci    }
16733eb0b6dSopenharmony_ci    NativeCallbackScopeManager* callbackScopeMgr = nullptr;
16833eb0b6dSopenharmony_ci    NativeCallbackScope* callbackScope;
16933eb0b6dSopenharmony_ci    if (asyncContext == nullptr) {
17033eb0b6dSopenharmony_ci        callbackScopeMgr = engine->GetCallbackScopeManager();
17133eb0b6dSopenharmony_ci        callbackScope = callbackScopeMgr->Open(engine,
17233eb0b6dSopenharmony_ci            panda::Local<panda::ObjectRef>(reinterpret_cast<uintptr_t>(obj)), {0, 0});
17333eb0b6dSopenharmony_ci    } else {
17433eb0b6dSopenharmony_ci        callbackScope = asyncContext->OpenCallbackScope();
17533eb0b6dSopenharmony_ci    }
17633eb0b6dSopenharmony_ci
17733eb0b6dSopenharmony_ci    panda::JSValueRef* callBackRst;
17833eb0b6dSopenharmony_ci    if (useAsyncHooksTrampoline) {
17933eb0b6dSopenharmony_ci        callBackRst = nullptr; // not support async_hook yet, and the actions need to be improved in the future
18033eb0b6dSopenharmony_ci    } else {
18133eb0b6dSopenharmony_ci        callBackRst = funRef->CallForNapi(vm, obj, argv, argc);
18233eb0b6dSopenharmony_ci    }
18333eb0b6dSopenharmony_ci
18433eb0b6dSopenharmony_ci    if (callbackScope != nullptr) {
18533eb0b6dSopenharmony_ci        if (callBackRst == nullptr) {
18633eb0b6dSopenharmony_ci            callbackScope->MarkAsFailed();
18733eb0b6dSopenharmony_ci        }
18833eb0b6dSopenharmony_ci        callbackScope->Close();
18933eb0b6dSopenharmony_ci    }
19033eb0b6dSopenharmony_ci
19133eb0b6dSopenharmony_ci    CloseScope(asyncContext, callbackScopeMgr, callbackScope, engine);
19233eb0b6dSopenharmony_ci    return callBackRst;
19333eb0b6dSopenharmony_ci}
19433eb0b6dSopenharmony_ci
19533eb0b6dSopenharmony_cipanda::JSValueRef* MakeCallback(NativeEngine* engine, panda::FunctionRef* funRef,
19633eb0b6dSopenharmony_ci                                panda::JSValueRef* obj, size_t argc,
19733eb0b6dSopenharmony_ci                                panda::JSValueRef* const argv[], NativeAsyncHookContext* asyncContext)
19833eb0b6dSopenharmony_ci{
19933eb0b6dSopenharmony_ci    auto vm = engine->GetEcmaVm();
20033eb0b6dSopenharmony_ci    panda::JSValueRef* rst = InternalMakeCallback(engine, funRef, obj, argv, argc, asyncContext);
20133eb0b6dSopenharmony_ci    NativeCallbackScopeManager* callbackScopeMgr = engine->GetCallbackScopeManager();
20233eb0b6dSopenharmony_ci    size_t depth = callbackScopeMgr->GetAsyncCallbackScopeDepth();
20333eb0b6dSopenharmony_ci    if (rst == nullptr && depth == 0) {
20433eb0b6dSopenharmony_ci        return *panda::JSValueRef::Undefined(vm);
20533eb0b6dSopenharmony_ci    }
20633eb0b6dSopenharmony_ci    return rst;
20733eb0b6dSopenharmony_ci}
20833eb0b6dSopenharmony_ci
20933eb0b6dSopenharmony_ci#endif /* FOUNDATION_ACE_NAPI_NATIVE_ENGINE_NATIVE_ASYNC_HOOK_CONTEXT_H */
210