• 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 #define STATUS_CALL(call)            \
194     do {                             \
195         JSVM_Status status = (call); \
196         if (status != JSVM_OK)       \
197             return status;           \
198     } while (0)
199 
200 namespace v8impl {
201 
202 class TryCatch : public v8::TryCatch {
203 public:
TryCatch(JSVM_Env env)204     explicit TryCatch(JSVM_Env env) : v8::TryCatch(env->isolate), env(env) {}
205 
~TryCatch()206     ~TryCatch()
207     {
208         if (HasCaught()) {
209             env->lastException.Reset(env->isolate, Exception());
210         }
211     }
212 
213 private:
214     JSVM_Env env;
215 };
216 
217 typedef JSVM_Value (*GetterCallback)(JSVM_Env, JSVM_Value, JSVM_Value, JSVM_Value);
218 typedef JSVM_Value (*SetterCallback)(JSVM_Env, JSVM_Value, JSVM_Value, JSVM_Value, JSVM_Value);
219 typedef JSVM_Value (*DeleterCallback)(JSVM_Env, JSVM_Value, JSVM_Value, JSVM_Value);
220 typedef JSVM_Value (*EnumeratorCallback)(JSVM_Env, JSVM_Value, JSVM_Value);
221 
222 struct JSVM_PropertyHandlerCfgStruct {
223     GetterCallback namedGetterCallback;
224     SetterCallback namedSetterCallback;
225     DeleterCallback nameDeleterCallback;
226     EnumeratorCallback namedEnumeratorCallback;
227     GetterCallback indexedGetterCallback;
228     SetterCallback indexedSetterCallback;
229     DeleterCallback indexedDeleterCallback;
230     EnumeratorCallback indexedEnumeratorCallback;
231     JSVM_Ref namedPropertyData;
232     JSVM_Ref indexedPropertyData;
233 };
234 
CreatePropertyCfg(JSVM_Env env,JSVM_PropertyHandlerCfg propertyCfg)235 inline JSVM_PropertyHandlerCfgStruct* CreatePropertyCfg(JSVM_Env env, JSVM_PropertyHandlerCfg propertyCfg)
236 {
237     JSVM_PropertyHandlerCfgStruct* newPropertyCfg = new JSVM_PropertyHandlerCfgStruct;
238     if (newPropertyCfg != nullptr && propertyCfg != nullptr) {
239         newPropertyCfg->namedGetterCallback = propertyCfg->genericNamedPropertyGetterCallback;
240         newPropertyCfg->namedSetterCallback = propertyCfg->genericNamedPropertySetterCallback;
241         newPropertyCfg->nameDeleterCallback = propertyCfg->genericNamedPropertyDeleterCallback;
242         newPropertyCfg->namedEnumeratorCallback = propertyCfg->genericNamedPropertyEnumeratorCallback;
243         newPropertyCfg->indexedGetterCallback = propertyCfg->genericIndexedPropertyGetterCallback;
244         newPropertyCfg->indexedSetterCallback = propertyCfg->genericIndexedPropertySetterCallback;
245         newPropertyCfg->indexedDeleterCallback = propertyCfg->genericIndexedPropertyDeleterCallback;
246         newPropertyCfg->indexedEnumeratorCallback = propertyCfg->genericIndexedPropertyEnumeratorCallback;
247         newPropertyCfg->namedPropertyData = nullptr;
248         newPropertyCfg->indexedPropertyData = nullptr;
249         if (propertyCfg->namedPropertyData != nullptr) {
250             v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(propertyCfg->namedPropertyData);
251             v8impl::UserReference* reference = v8impl::UserReference::New(env, v8_value, 1);
252             newPropertyCfg->namedPropertyData = reinterpret_cast<JSVM_Ref>(reference);
253         }
254 
255         if (propertyCfg->indexedPropertyData != nullptr) {
256             v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(propertyCfg->indexedPropertyData);
257             v8impl::UserReference* reference = v8impl::UserReference::New(env, v8_value, 1);
258             newPropertyCfg->indexedPropertyData = reinterpret_cast<JSVM_Ref>(reference);
259         }
260     }
261 
262     return newPropertyCfg;
263 }
264 
CfgFinalizedCallback(JSVM_Env env,void * finalizeData,void * finalizeHint)265 inline void CfgFinalizedCallback(JSVM_Env env, void* finalizeData, void* finalizeHint)
266 {
267     auto cfg = reinterpret_cast<JSVM_PropertyHandlerCfgStruct*>(finalizeData);
268     if (cfg->namedPropertyData != nullptr) {
269         delete reinterpret_cast<v8impl::UserReference*>(cfg->namedPropertyData);
270     }
271     if (cfg->indexedPropertyData != nullptr) {
272         delete reinterpret_cast<v8impl::UserReference*>(cfg->indexedPropertyData);
273     }
274     delete cfg;
275 }
276 
277 } // end of namespace v8impl
278 
279 #endif // SRC_JS_NATIVE_API_V8_H_
280