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