• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 #ifndef SRC_JS_NATIVE_API_V8_H_
17 #define SRC_JS_NATIVE_API_V8_H_
18 
19 #include "jsvm_env.h"
20 #include "jsvm_util.h"
21 #include "type_conversion.h"
22 
23 inline JSVM_Status SetLastError(JSVM_Env env,
24                                 JSVM_Status errorCode,
25                                 uint32_t engineErrorCode = 0,
26                                 void* engineReserved = nullptr)
27 {
28     env->lastError.errorCode = errorCode;
29     env->lastError.engineErrorCode = engineErrorCode;
30     env->lastError.engineReserved = engineReserved;
31     return errorCode;
32 }
33 
34 #define RETURN_STATUS_IF_FALSE(env, condition, status) \
35     do {                                               \
36         if (!(condition)) {                            \
37             return SetLastError((env), (status));      \
38         }                                              \
39     } while (0)
40 
41 #define RETURN_STATUS_IF_FALSE_WITHOUT_ENV(condition, status) \
42     do {                                                      \
43         if (!(condition)) {                                   \
44             return status;                                    \
45         }                                                     \
46     } while (0)
47 
48 #define RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, condition, status)                              \
49     do {                                                                                          \
50         if (!(condition)) {                                                                       \
51             return SetLastError((env), tryCatch.HasCaught() ? JSVM_PENDING_EXCEPTION : (status)); \
52         }                                                                                         \
53     } while (0)
54 
55 #define RETURN_IF_EXCEPTION_HAS_CAUGHT(env)                     \
56     do {                                                        \
57         if (tryCatch.HasCaught()) {                             \
58             return SetLastError((env), JSVM_PENDING_EXCEPTION); \
59         }                                                       \
60     } while (0)
61 
62 #define CHECK_ENV(env)               \
63     do {                             \
64         if ((env) == nullptr) {      \
65             return JSVM_INVALID_ARG; \
66         }                            \
67     } while (0)
68 
69 #define CHECK_ENV_NOT_IN_GC(env) \
70     do {                         \
71         CHECK_ENV((env));        \
72         (env)->CheckGCAccess();  \
73     } while (0)
74 
75 #define CHECK_ARG(env, arg) RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), JSVM_INVALID_ARG)
76 
77 #define CHECK_ARG_WITHOUT_ENV(arg) RETURN_STATUS_IF_FALSE_WITHOUT_ENV(((arg) != nullptr), JSVM_INVALID_ARG)
78 
79 #define CHECK_ARG_NOT_ZERO(env, arg) RETURN_STATUS_IF_FALSE((env), ((arg) != 0), JSVM_INVALID_ARG)
80 
81 #define CHECK_ARG_WITH_PREAMBLE(env, arg) \
82     RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), ((arg) != nullptr), JSVM_INVALID_ARG)
83 
84 #define CHECK_MAYBE_EMPTY(env, maybe, status) RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status))
85 
86 #define CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, status) \
87     RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsEmpty()), (status))
88 
89 #define CHECK_MAYBE_NOTHING(env, maybe, status) RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status))
90 
91 #define CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe, status) \
92     RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsNothing()), (status))
93 
94 // JSVM_PREAMBLE is not wrapped in do..while: tryCatch must have function scope
95 #define JSVM_PREAMBLE(env)                                                                               \
96     CHECK_ENV((env));                                                                                    \
97     RETURN_STATUS_IF_FALSE((env), (env)->lastException.IsEmpty(), JSVM_PENDING_EXCEPTION);               \
98     RETURN_STATUS_IF_FALSE((env), (env)->CanCallIntoJS(),                                                \
99                            ((env)->GetVersion() == JSVM_VERSION_EXPERIMENTAL ? JSVM_CANNOT_RUN_JS        \
100                                                                              : JSVM_PENDING_EXCEPTION)); \
101     ClearLastError((env));                                                                               \
102     v8impl::TryCatch tryCatch((env))
103 
104 #define CHECK_TO_TYPE(env, type, context, result, src, status)                    \
105     do {                                                                          \
106         CHECK_ARG((env), (src));                                                  \
107         auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \
108         CHECK_MAYBE_EMPTY((env), maybe, (status));                                \
109         (result) = maybe.ToLocalChecked();                                        \
110     } while (0)
111 
112 #define CHECK_TO_TYPE_WITH_PREAMBLE(env, type, context, result, src, status)      \
113     do {                                                                          \
114         CHECK_ARG_WITH_PREAMBLE((env), (src));                                    \
115         auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \
116         CHECK_MAYBE_EMPTY_WITH_PREAMBLE((env), maybe, (status));                  \
117         (result) = maybe.ToLocalChecked();                                        \
118     } while (0)
119 
120 #define CHECK_TO_FUNCTION(env, result, src)                                     \
121     do {                                                                        \
122         CHECK_ARG((env), (src));                                                \
123         v8::Local<v8::Value> v8value = v8impl::V8LocalValueFromJsValue((src));  \
124         RETURN_STATUS_IF_FALSE((env), v8value->IsFunction(), JSVM_INVALID_ARG); \
125         (result) = v8value.As<v8::Function>();                                  \
126     } while (0)
127 
128 #define CHECK_TO_OBJECT(env, context, result, src) \
129     CHECK_TO_TYPE((env), Object, (context), (result), (src), JSVM_OBJECT_EXPECTED)
130 
131 #define CHECK_TO_BIGINT(env, context, result, src) \
132     CHECK_TO_TYPE((env), BigInt, (context), (result), (src), JSVM_BIGINT_EXPECTED)
133 
134 #define CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, result, src) \
135     CHECK_TO_TYPE_WITH_PREAMBLE((env), Object, (context), (result), (src), JSVM_OBJECT_EXPECTED)
136 
137 #define CHECK_TO_STRING(env, context, result, src) \
138     CHECK_TO_TYPE((env), String, (context), (result), (src), JSVM_STRING_EXPECTED)
139 
140 #define CHECK_TO_NUMBER(env, context, result, src) \
141     CHECK_TO_TYPE((env), Number, (context), (result), (src), JSVM_NUMBER_EXPECTED)
142 
143 #define GET_RETURN_STATUS(env) (!tryCatch.HasCaught() ? JSVM_OK : SetLastError((env), JSVM_PENDING_EXCEPTION))
144 
145 #define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \
146     do {                                                           \
147         if (!(condition)) {                                        \
148             OH_JSVM_ThrowRangeError((env), (error), (message));    \
149             return SetLastError((env), JSVM_GENERIC_FAILURE);      \
150         }                                                          \
151     } while (0)
152 
153 // jsvm-api defines JSVM_AUTO_LENGTH as the indicator that a string
154 // is null terminated. For V8 the equivalent is -1. The assert
155 // validates that our cast of JSVM_AUTO_LENGTH results in -1 as
156 // needed by V8.
157 #define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len)                                                                \
158     do {                                                                                                              \
159         static_assert(static_cast<int>(JSVM_AUTO_LENGTH) == -1, "Casting JSVM_AUTO_LENGTH to int must result in -1"); \
160         RETURN_STATUS_IF_FALSE((env), ((len) == JSVM_AUTO_LENGTH) || (len) <= INT_MAX, JSVM_INVALID_ARG);             \
161         RETURN_STATUS_IF_FALSE((env), (str) != nullptr, JSVM_INVALID_ARG);                                            \
162         auto str_maybe =                                                                                              \
163             v8::String::NewFromUtf8((env)->isolate, (str), v8::NewStringType::kInternalized, static_cast<int>(len));  \
164         CHECK_MAYBE_EMPTY((env), str_maybe, JSVM_GENERIC_FAILURE);                                                    \
165         (result) = str_maybe.ToLocalChecked();                                                                        \
166     } while (0)
167 
168 #define CHECK_NEW_FROM_UTF8(env, result, str) CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), JSVM_AUTO_LENGTH)
169 
170 #define CHECK_NEW_STRING_ARGS(env, str, length, result)                                                         \
171     do {                                                                                                        \
172         CHECK_ENV_NOT_IN_GC((env));                                                                             \
173         if ((length) > 0)                                                                                       \
174             CHECK_ARG((env), (str));                                                                            \
175         CHECK_ARG((env), (result));                                                                             \
176         RETURN_STATUS_IF_FALSE((env), ((length) == JSVM_AUTO_LENGTH) || (length) <= INT_MAX, JSVM_INVALID_ARG); \
177     } while (0)
178 
179 #define CREATE_TYPED_ARRAY(env, type, sizeOfElement, buffer, byteOffset, length, out)                          \
180     do {                                                                                                       \
181         if ((sizeOfElement) > 1) {                                                                             \
182             THROW_RANGE_ERROR_IF_FALSE((env), (byteOffset) % (sizeOfElement) == 0,                             \
183                                        "ERR_JSVM_INVALID_TYPEDARRAY_ALIGNMENT",                                \
184                                        "start offset of " #type " should be a multiple of " #sizeOfElement);   \
185         }                                                                                                      \
186         THROW_RANGE_ERROR_IF_FALSE((env), (length) * (sizeOfElement) + (byteOffset) <= (buffer)->ByteLength(), \
187                                    "ERR_JSVM_INVALID_TYPEDARRAY_LENGTH", "Invalid typed array length");        \
188         (out) = v8::type::New((buffer), (byteOffset), (length));                                               \
189     } while (0)
190 
191 #define JSVM_PRIVATE_KEY(isolate, suffix) (v8impl::GetIsolateData(isolate)->suffix##Key.Get(isolate))
192 
193 FORCE_NOINLINE void AddValueToScopeCheck(JSVM_Env env, JSVM_Value val);
194 FORCE_NOINLINE void AddValueToEscapeScopeCheck(JSVM_Env env, JSVM_Value val);
195 FORCE_NOINLINE void CheckScope(JSVM_Env env, JSVM_Value val, const char *callerFunctionName);
196 
197 #define ADD_VAL_TO_SCOPE_CHECK(env, val)              \
198     do {                                              \
199         if (UNLIKELY((env)->debugFlags)) {            \
200             AddValueToScopeCheck(env, val, __func__); \
201         }                                             \
202     } while (0)
203 
204 #define ADD_VAL_TO_ESCAPE_SCOPE_CHECK(env, val)             \
205     do {                                                    \
206         if (UNLIKELY((env)->debugFlags)) {                  \
207             AddValueToScopeCheck(env, val, __func__, true); \
208         }                                                   \
209     } while (0)
210 
211 #define CHECK_SCOPE(env, val)               \
212     do {                                    \
213         if (UNLIKELY((env)->debugFlags)) {  \
214             CheckScope(env, val, __func__); \
215         }                                   \
216     } while (0)
217 
218 #define CHECK_SCOPE_ISTYPE(env, val, func)  \
219     do {                                    \
220         if (UNLIKELY((env)->debugFlags)) {  \
221             CheckScope(env, val, __func__); \
222             return func;                    \
223         }                                   \
224     } while (0)
225 
226 #define STATUS_CALL(call)            \
227     do {                             \
228         JSVM_Status status = (call); \
229         if (status != JSVM_OK)       \
230             return status;           \
231     } while (0)
232 
233 namespace v8impl {
234 
235 class TryCatch : public v8::TryCatch {
236 public:
TryCatch(JSVM_Env env)237     explicit TryCatch(JSVM_Env env) : v8::TryCatch(env->isolate), env(env) {}
238 
~TryCatch()239     ~TryCatch()
240     {
241         if (HasCaught()) {
242             env->lastException.Reset(env->isolate, Exception());
243         }
244     }
245 
246 private:
247     JSVM_Env env;
248 };
249 
250 typedef JSVM_Value (*GetterCallback)(JSVM_Env, JSVM_Value, JSVM_Value, JSVM_Value);
251 typedef JSVM_Value (*SetterCallback)(JSVM_Env, JSVM_Value, JSVM_Value, JSVM_Value, JSVM_Value);
252 typedef JSVM_Value (*DeleterCallback)(JSVM_Env, JSVM_Value, JSVM_Value, JSVM_Value);
253 typedef JSVM_Value (*EnumeratorCallback)(JSVM_Env, JSVM_Value, JSVM_Value);
254 
255 struct JSVM_PropertyHandlerCfgStruct {
256     GetterCallback namedGetterCallback;
257     SetterCallback namedSetterCallback;
258     DeleterCallback nameDeleterCallback;
259     EnumeratorCallback namedEnumeratorCallback;
260     GetterCallback indexedGetterCallback;
261     SetterCallback indexedSetterCallback;
262     DeleterCallback indexedDeleterCallback;
263     EnumeratorCallback indexedEnumeratorCallback;
264     JSVM_Ref namedPropertyData;
265     JSVM_Ref indexedPropertyData;
266 };
267 
CreatePropertyCfg(JSVM_Env env,JSVM_PropertyHandlerCfg propertyCfg)268 inline JSVM_PropertyHandlerCfgStruct* CreatePropertyCfg(JSVM_Env env, JSVM_PropertyHandlerCfg propertyCfg)
269 {
270     JSVM_PropertyHandlerCfgStruct* newPropertyCfg = new JSVM_PropertyHandlerCfgStruct;
271     if (newPropertyCfg != nullptr && propertyCfg != nullptr) {
272         newPropertyCfg->namedGetterCallback = propertyCfg->genericNamedPropertyGetterCallback;
273         newPropertyCfg->namedSetterCallback = propertyCfg->genericNamedPropertySetterCallback;
274         newPropertyCfg->nameDeleterCallback = propertyCfg->genericNamedPropertyDeleterCallback;
275         newPropertyCfg->namedEnumeratorCallback = propertyCfg->genericNamedPropertyEnumeratorCallback;
276         newPropertyCfg->indexedGetterCallback = propertyCfg->genericIndexedPropertyGetterCallback;
277         newPropertyCfg->indexedSetterCallback = propertyCfg->genericIndexedPropertySetterCallback;
278         newPropertyCfg->indexedDeleterCallback = propertyCfg->genericIndexedPropertyDeleterCallback;
279         newPropertyCfg->indexedEnumeratorCallback = propertyCfg->genericIndexedPropertyEnumeratorCallback;
280         newPropertyCfg->namedPropertyData = nullptr;
281         newPropertyCfg->indexedPropertyData = nullptr;
282         if (propertyCfg->namedPropertyData != nullptr) {
283             v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(propertyCfg->namedPropertyData);
284             v8impl::UserReference* reference = v8impl::UserReference::New(env, v8_value, 1);
285             newPropertyCfg->namedPropertyData = reinterpret_cast<JSVM_Ref>(reference);
286         }
287 
288         if (propertyCfg->indexedPropertyData != nullptr) {
289             v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(propertyCfg->indexedPropertyData);
290             v8impl::UserReference* reference = v8impl::UserReference::New(env, v8_value, 1);
291             newPropertyCfg->indexedPropertyData = reinterpret_cast<JSVM_Ref>(reference);
292         }
293     }
294 
295     return newPropertyCfg;
296 }
297 
CfgFinalizedCallback(JSVM_Env env,void * finalizeData,void * finalizeHint)298 inline void CfgFinalizedCallback(JSVM_Env env, void* finalizeData, void* finalizeHint)
299 {
300     auto cfg = reinterpret_cast<JSVM_PropertyHandlerCfgStruct*>(finalizeData);
301     if (cfg->namedPropertyData != nullptr) {
302         delete reinterpret_cast<v8impl::UserReference*>(cfg->namedPropertyData);
303     }
304     if (cfg->indexedPropertyData != nullptr) {
305         delete reinterpret_cast<v8impl::UserReference*>(cfg->indexedPropertyData);
306     }
307     delete cfg;
308 }
309 
310 } // end of namespace v8impl
311 
312 #endif // SRC_JS_NATIVE_API_V8_H_
313