1 /**
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "plugins/ets/runtime/interop_js/interop_context.h"
17 #include "plugins/ets/runtime/interop_js/interop_common.h"
18
19 namespace ark::ets::interop::js {
20
InteropFatal(const char * message)21 [[noreturn]] void InteropFatal(const char *message)
22 {
23 InteropCtx::Fatal(message);
24 UNREACHABLE();
25 }
26
InteropFatal(const std::string & message)27 [[noreturn]] void InteropFatal(const std::string &message)
28 {
29 InteropCtx::Fatal(message.c_str());
30 UNREACHABLE();
31 }
32
InteropFatal(const char * message,napi_status status)33 [[noreturn]] void InteropFatal(const char *message, napi_status status)
34 {
35 InteropCtx::Fatal(std::string(message) + " status=" + std::to_string(status));
36 UNREACHABLE();
37 }
38
InteropTrace(const char * func,const char * file,int line)39 void InteropTrace(const char *func, const char *file, int line)
40 {
41 INTEROP_LOG(DEBUG) << "trace: " << func << ":" << file << ":" << line;
42 }
43
GetBigInt(napi_env env,napi_value jsVal)44 std::pair<SmallVector<uint64_t, 4U>, int> GetBigInt(napi_env env, napi_value jsVal)
45 {
46 size_t wordCount;
47 NAPI_ASSERT_OK(napi_get_value_bigint_words(env, jsVal, nullptr, &wordCount, nullptr));
48
49 int signBit;
50 SmallVector<uint64_t, 4U> words;
51 if (wordCount == 0) {
52 bool lossless;
53 signBit = 0;
54 words.resize(1);
55 NAPI_ASSERT_OK(napi_get_value_bigint_uint64(env, jsVal, &words[0], &lossless));
56 } else {
57 words.resize(wordCount);
58 NAPI_ASSERT_OK(napi_get_value_bigint_words(env, jsVal, &signBit, &wordCount, words.data()));
59 }
60
61 return {words, signBit};
62 }
63
ConvertBigIntArrayFromEtsToJs(const std::vector<uint32_t> & etsArray)64 SmallVector<uint64_t, 4U> ConvertBigIntArrayFromEtsToJs(const std::vector<uint32_t> &etsArray)
65 {
66 ASSERT(BIT_64 % BIGINT_BITS_NUM == 0);
67 // BigInt in ArkTS is stored in EtsInt array. Put these bits into int64_t array
68 size_t jsArraySize = etsArray.size() / 2 + etsArray.size() % 2;
69 SmallVector<uint64_t, 4U> jsArray;
70 jsArray.resize(jsArraySize, 0);
71
72 size_t curJSArrayElemPos = 0;
73 size_t curBitPos = 0;
74 for (auto etsElem : etsArray) {
75 jsArray[curJSArrayElemPos] |= static_cast<uint64_t>(etsElem) << curBitPos;
76 curBitPos = curBitPos + BIGINT_BITS_NUM;
77 if (curBitPos == BIT_64) {
78 curBitPos = 0;
79 ++curJSArrayElemPos;
80 }
81 }
82
83 return jsArray;
84 }
85
GetBigIntEtsArraySize(size_t jsArraySize,uint64_t lastElem)86 static inline size_t GetBigIntEtsArraySize(size_t jsArraySize, uint64_t lastElem)
87 {
88 if (jsArraySize == 1 && lastElem == 0) {
89 return 0;
90 }
91
92 size_t etsSize = jsArraySize * 2 - 1;
93
94 if ((lastElem >> BIGINT_BITS_NUM) > 0) {
95 ++etsSize;
96 }
97
98 return etsSize;
99 }
100
ConvertBigIntArrayFromJsToEts(SmallVector<uint64_t,4U> & jsArray)101 std::vector<EtsInt> ConvertBigIntArrayFromJsToEts(SmallVector<uint64_t, 4U> &jsArray)
102 {
103 size_t etsArraySize = GetBigIntEtsArraySize(jsArray.size(), jsArray.back());
104 std::vector<EtsInt> etsArray(etsArraySize, 0);
105
106 size_t curJSArrayElemPos = 0;
107 size_t curBitPos = 0;
108 for (size_t i = 0; i < etsArraySize; ++i) {
109 etsArray[i] = static_cast<uint32_t>(jsArray[curJSArrayElemPos] >> curBitPos);
110
111 curBitPos = curBitPos + BIGINT_BITS_NUM;
112 if (curBitPos == BIT_64) {
113 curBitPos = 0;
114 ++curJSArrayElemPos;
115 }
116 }
117
118 return etsArray;
119 }
120
ThrowNoInteropContextException()121 void ThrowNoInteropContextException()
122 {
123 auto *thread = ManagedThread::GetCurrent();
124 ASSERT(thread != nullptr);
125 auto ctx = thread->GetVM()->GetLanguageContext();
126 auto descriptor = utf::CStringAsMutf8(panda_file_items::class_descriptors::NO_INTEROP_CONTEXT_ERROR.data());
127 PandaString msg = "Interop call may be done only from _main_ or exclusive worker";
128 ThrowException(ctx, thread, descriptor, utf::CStringAsMutf8(msg.c_str()));
129 }
130
GetPropertyStatusHandling(napi_env env,napi_status rc)131 static bool GetPropertyStatusHandling([[maybe_unused]] napi_env env, napi_status rc)
132 {
133 #if !defined(PANDA_TARGET_OHOS) && !defined(PANDA_JS_ETS_HYBRID_MODE)
134 if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
135 ASSERT(NapiIsExceptionPending(env));
136 return false;
137 }
138 #else
139 if (UNLIKELY(rc == napi_object_expected && !NapiIsExceptionPending(env))) {
140 InteropCtx::ThrowJSTypeError(env, "Cannot convert undefined or null to object");
141 return false;
142 }
143
144 if (UNLIKELY(rc == napi_pending_exception || NapiThrownGeneric(rc))) {
145 ASSERT(NapiIsExceptionPending(env));
146 return false;
147 }
148 #endif
149 INTEROP_FATAL_IF(rc != napi_ok);
150 return true;
151 }
152
NapiGetProperty(napi_env env,napi_value object,napi_value key,napi_value * result)153 bool NapiGetProperty(napi_env env, napi_value object, napi_value key, napi_value *result)
154 {
155 napi_status rc = napi_get_property(env, object, key, result);
156 return GetPropertyStatusHandling(env, rc);
157 }
158
NapiGetNamedProperty(napi_env env,napi_value object,const char * utf8name,napi_value * result)159 bool NapiGetNamedProperty(napi_env env, napi_value object, const char *utf8name, napi_value *result)
160 {
161 napi_status rc = napi_get_named_property(env, object, utf8name, result);
162 return GetPropertyStatusHandling(env, rc);
163 }
164
165 } // namespace ark::ets::interop::js
166