1 #include <unistd.h>
2 #include <algorithm>
3 #include <atomic>
4 #include <climits> // INT_MAX
5 #include <cmath>
6 #include "v8-debug.h"
7 #include "v8-internal.h"
8 #include "v8-local-handle.h"
9 #include "v8-primitive.h"
10 #include "v8-statistics.h"
11 #include "v8-version-string.h"
12 #define JSVM_EXPERIMENTAL
13 #include "env-inl.h"
14 #include "jsvm.h"
15 #include "js_native_api_v8.h"
16 #include "js_native_api_v8_inspector.h"
17 #include "libplatform/libplatform.h"
18 #include "util-inl.h"
19 #include "util.h"
20 #include "sourcemap.def"
21
22 #define SECARGCNT 2
23
24 #define CHECK_MAYBE_NOTHING(env, maybe, status) \
25 RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status))
26
27 #define CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe, status) \
28 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsNothing()), (status))
29
30 #define CHECK_TO_NUMBER(env, context, result, src) \
31 CHECK_TO_TYPE((env), Number, (context), (result), (src), JSVM_NUMBER_EXPECTED)
32
33 // n-api defines JSVM_AUTO_LENGTH as the indicator that a string
34 // is null terminated. For V8 the equivalent is -1. The assert
35 // validates that our cast of JSVM_AUTO_LENGTH results in -1 as
36 // needed by V8.
37 #define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \
38 do { \
39 static_assert(static_cast<int>(JSVM_AUTO_LENGTH) == -1, \
40 "Casting JSVM_AUTO_LENGTH to int must result in -1"); \
41 RETURN_STATUS_IF_FALSE( \
42 (env), (len == JSVM_AUTO_LENGTH) || len <= INT_MAX, JSVM_INVALID_ARG); \
43 RETURN_STATUS_IF_FALSE((env), (str) != nullptr, JSVM_INVALID_ARG); \
44 auto str_maybe = v8::String::NewFromUtf8((env)->isolate, \
45 (str), \
46 v8::NewStringType::kInternalized, \
47 static_cast<int>(len)); \
48 CHECK_MAYBE_EMPTY((env), str_maybe, JSVM_GENERIC_FAILURE); \
49 (result) = str_maybe.ToLocalChecked(); \
50 } while (0)
51
52 #define CHECK_NEW_FROM_UTF8(env, result, str) \
53 CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), JSVM_AUTO_LENGTH)
54
55 #define CHECK_NEW_STRING_ARGS(env, str, length, result) \
56 do { \
57 CHECK_ENV_NOT_IN_GC((env)); \
58 if ((length) > 0) CHECK_ARG((env), (str)); \
59 CHECK_ARG((env), (result)); \
60 RETURN_STATUS_IF_FALSE( \
61 (env), \
62 ((length) == JSVM_AUTO_LENGTH) || (length) <= INT_MAX, \
63 JSVM_INVALID_ARG); \
64 } while (0)
65
66 #define CREATE_TYPED_ARRAY( \
67 env, type, size_of_element, buffer, byteOffset, length, out) \
68 do { \
69 if ((size_of_element) > 1) { \
70 THROW_RANGE_ERROR_IF_FALSE( \
71 (env), \
72 (byteOffset) % (size_of_element) == 0, \
73 "ERR_JSVM_INVALID_TYPEDARRAY_ALIGNMENT", \
74 "start offset of " #type \
75 " should be a multiple of " #size_of_element); \
76 } \
77 THROW_RANGE_ERROR_IF_FALSE( \
78 (env), \
79 (length) * (size_of_element) + (byteOffset) <= buffer->ByteLength(), \
80 "ERR_JSVM_INVALID_TYPEDARRAY_LENGTH", \
81 "Invalid typed array length"); \
82 (out) = v8::type::New((buffer), (byteOffset), (length)); \
83 } while (0)
84
JSVM_Env__(v8::Isolate * isolate,int32_t module_api_version)85 JSVM_Env__::JSVM_Env__(v8::Isolate* isolate, int32_t module_api_version)
86 : isolate(isolate), module_api_version(module_api_version) {
87 inspector_agent_ = new v8impl::Agent(this);
88 jsvm_clear_last_error(this);
89 }
90
DeleteMe()91 void JSVM_Env__::DeleteMe() {
92 // First we must finalize those references that have `napi_finalizer`
93 // callbacks. The reason is that addons might store other references which
94 // they delete during their `napi_finalizer` callbacks. If we deleted such
95 // references here first, they would be doubly deleted when the
96 // `napi_finalizer` deleted them subsequently.
97 v8impl::RefTracker::FinalizeAll(&finalizing_reflist);
98 v8impl::RefTracker::FinalizeAll(&reflist);
99 {
100 v8::Context::Scope context_scope(context());
101 if (inspector_agent_->IsActive()) {
102 inspector_agent_->WaitForDisconnect();
103 }
104 delete inspector_agent_;
105 }
106 delete this;
107 }
108
RunAndClearInterrupts()109 void JSVM_Env__::RunAndClearInterrupts() {
110 while (native_immediates_interrupts_.size() > 0) {
111 NativeImmediateQueue queue;
112 {
113 node::Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
114 queue.ConcatMove(std::move(native_immediates_interrupts_));
115 }
116 node::DebugSealHandleScope seal_handle_scope(isolate);
117
118 while (auto head = queue.Shift())
119 head->Call(this);
120 }
121 }
122
InvokeFinalizerFromGC(v8impl::RefTracker * finalizer)123 void JSVM_Env__::InvokeFinalizerFromGC(v8impl::RefTracker* finalizer) {
124 // The experimental code calls finalizers immediately to release native
125 // objects as soon as possible. In that state any code that may affect GC
126 // state causes a fatal error. To work around this issue the finalizer code
127 // can call node_api_post_finalizer.
128 auto restore_state = node::OnScopeLeave(
129 [this, saved = in_gc_finalizer] { in_gc_finalizer = saved; });
130 in_gc_finalizer = true;
131 finalizer->Finalize();
132 }
133
134 namespace v8impl {
135
136 namespace {
137
138 enum IsolateDataSlot {
139 kIsolateData = 0,
140 kIsolateSnapshotCreatorSlot = 1,
141 };
142
143 enum ContextEmbedderIndex {
144 kContextEnvIndex = 1,
145 };
146
147 struct IsolateData {
IsolateDatav8impl::__anonc72d966b0211::IsolateData148 IsolateData(v8::StartupData* blob) : blob(blob) {}
149
~IsolateDatav8impl::__anonc72d966b0211::IsolateData150 ~IsolateData() {
151 delete blob;
152 }
153
154 v8::StartupData* blob;
155 v8::Eternal<v8::Private> jsvm_type_tag_key;
156 v8::Eternal<v8::Private> jsvm_wrapper_key;
157 };
158
CreateIsolateData(v8::Isolate * isolate,v8::StartupData * blob)159 static void CreateIsolateData(v8::Isolate* isolate, v8::StartupData* blob) {
160 auto data = new IsolateData(blob);
161 v8::Isolate::Scope isolate_scope(isolate);
162 v8::HandleScope handle_scope(isolate);
163 if (blob) {
164 // NOTE: The order of getting the data must be consistent with the order of
165 // adding data in OH_JSVM_CreateSnapshot.
166 auto wrapper_key = isolate->GetDataFromSnapshotOnce<v8::Private>(0);
167 auto type_tag_key = isolate->GetDataFromSnapshotOnce<v8::Private>(1);
168 data->jsvm_wrapper_key.Set(isolate, wrapper_key.ToLocalChecked());
169 data->jsvm_type_tag_key.Set(isolate, type_tag_key.ToLocalChecked());
170 } else {
171 data->jsvm_wrapper_key.Set(isolate, v8::Private::New(isolate));
172 data->jsvm_type_tag_key.Set(isolate, v8::Private::New(isolate));
173 }
174 isolate->SetData(v8impl::kIsolateData, data);
175 }
176
GetIsolateData(v8::Isolate * isolate)177 static IsolateData* GetIsolateData(v8::Isolate* isolate) {
178 auto data = isolate->GetData(v8impl::kIsolateData);
179 return reinterpret_cast<IsolateData*>(data);
180 }
181
SetIsolateSnapshotCreator(v8::Isolate * isolate,v8::SnapshotCreator * creator)182 static void SetIsolateSnapshotCreator(v8::Isolate* isolate,
183 v8::SnapshotCreator* creator) {
184 isolate->SetData(v8impl::kIsolateSnapshotCreatorSlot, creator);
185 }
186
GetIsolateSnapshotCreator(v8::Isolate * isolate)187 static v8::SnapshotCreator* GetIsolateSnapshotCreator(v8::Isolate* isolate) {
188 auto data = isolate->GetData(v8impl::kIsolateSnapshotCreatorSlot);
189 return reinterpret_cast<v8::SnapshotCreator*>(data);
190 }
191
SetContextEnv(v8::Local<v8::Context> context,JSVM_Env env)192 static void SetContextEnv(v8::Local<v8::Context> context, JSVM_Env env) {
193 context->SetAlignedPointerInEmbedderData(kContextEnvIndex, env);
194 }
195
GetContextEnv(v8::Local<v8::Context> context)196 static JSVM_Env GetContextEnv(v8::Local<v8::Context> context) {
197 auto data = context->GetAlignedPointerFromEmbedderData(kContextEnvIndex);
198 return reinterpret_cast<JSVM_Env>(data);
199 }
200
201 class OutputStream : public v8::OutputStream {
202 public:
OutputStream(JSVM_OutputStream stream,void * data,int chunk_size=65536)203 OutputStream(JSVM_OutputStream stream, void* data, int chunk_size = 65536)
204 : stream_(stream), stream_data_(data), chunk_size_(chunk_size) {}
205
GetChunkSize()206 int GetChunkSize() override { return chunk_size_; }
207
EndOfStream()208 void EndOfStream() override {
209 stream_(nullptr, 0, stream_data_);
210 }
211
WriteAsciiChunk(char * data,const int size)212 WriteResult WriteAsciiChunk(char* data, const int size) override {
213 return stream_(data, size, stream_data_) ? kContinue : kAbort;
214 }
215
216 private:
217 JSVM_OutputStream stream_;
218 void* stream_data_;
219 int chunk_size_;
220 };
221
222 static std::unique_ptr<v8::Platform> g_platform = v8::platform::NewDefaultPlatform();
223
224 static std::vector<intptr_t> externalReferenceRegistry;
225
226 static std::unordered_map<std::string, std::string> sourceMapUrlMap;
227
228 static std::unique_ptr<v8::ArrayBuffer::Allocator> defaultArrayBufferAllocator;
229
GetOrCreateDefaultArrayBufferAllocator()230 static v8::ArrayBuffer::Allocator *GetOrCreateDefaultArrayBufferAllocator() {
231 if (!defaultArrayBufferAllocator) {
232 defaultArrayBufferAllocator.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());
233 }
234 return defaultArrayBufferAllocator.get();
235 }
236
SetFileToSourceMapMapping(std::string && file,std::string && sourceMapUrl)237 static void SetFileToSourceMapMapping(std::string &&file, std::string &&sourceMapUrl) {
238 auto it = sourceMapUrlMap.find(file);
239 if (it == sourceMapUrlMap.end()) {
240 sourceMapUrlMap.emplace(file, sourceMapUrl);
241 return;
242 }
243 auto &&prevSourceMapUrl = it->second;
244 CHECK(prevSourceMapUrl == sourceMapUrl);
245 }
246
GetSourceMapFromFileName(std::string && file)247 static std::string GetSourceMapFromFileName(std::string &&file) {
248 auto it = sourceMapUrlMap.find(file);
249 if (it != sourceMapUrlMap.end()) {
250 return it->second;
251 }
252 return "";
253 }
254
255 template <typename CCharType, typename StringMaker>
NewString(JSVM_Env env,const CCharType * str,size_t length,JSVM_Value * result,StringMaker string_maker)256 JSVM_Status NewString(JSVM_Env env,
257 const CCharType* str,
258 size_t length,
259 JSVM_Value* result,
260 StringMaker string_maker) {
261 CHECK_NEW_STRING_ARGS(env, str, length, result);
262
263 auto isolate = env->isolate;
264 auto str_maybe = string_maker(isolate);
265 CHECK_MAYBE_EMPTY(env, str_maybe, JSVM_GENERIC_FAILURE);
266 *result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
267 return jsvm_clear_last_error(env);
268 }
269
270 template <typename CharType, typename CreateAPI, typename StringMaker>
NewExternalString(JSVM_Env env,CharType * str,size_t length,JSVM_Finalize finalizeCallback,void * finalizeHint,JSVM_Value * result,bool * copied,CreateAPI create_api,StringMaker string_maker)271 JSVM_Status NewExternalString(JSVM_Env env,
272 CharType* str,
273 size_t length,
274 JSVM_Finalize finalizeCallback,
275 void* finalizeHint,
276 JSVM_Value* result,
277 bool* copied,
278 CreateAPI create_api,
279 StringMaker string_maker) {
280 CHECK_NEW_STRING_ARGS(env, str, length, result);
281 JSVM_Status status;
282 #if defined(V8_ENABLE_SANDBOX)
283 status = create_api(env, str, length, result);
284 if (status == JSVM_OK) {
285 if (copied != nullptr) {
286 *copied = true;
287 }
288 if (finalizeCallback) {
289 env->CallFinalizer(
290 finalizeCallback, static_cast<CharType*>(str), finalizeHint);
291 }
292 }
293 #else
294 status = NewString(env, str, length, result, string_maker);
295 if (status == JSVM_OK && copied != nullptr) {
296 *copied = false;
297 }
298 #endif // V8_ENABLE_SANDBOX
299 return status;
300 }
301
302 class TrackedStringResource : public Finalizer, RefTracker {
303 public:
TrackedStringResource(JSVM_Env env,JSVM_Finalize finalizeCallback,void * data,void * finalizeHint)304 TrackedStringResource(JSVM_Env env,
305 JSVM_Finalize finalizeCallback,
306 void* data,
307 void* finalizeHint)
308 : Finalizer(env, finalizeCallback, data, finalizeHint) {
309 Link(finalizeCallback == nullptr ? &env->reflist
310 : &env->finalizing_reflist);
311 }
312
313 protected:
314 // The only time Finalize() gets called before Dispose() is if the
315 // environment is dying. Finalize() expects that the item will be unlinked,
316 // so we do it here. V8 will still call Dispose() on us later, so we don't do
317 // any deleting here. We just null out env_ to avoid passing a stale pointer
318 // to the user's finalizer when V8 does finally call Dispose().
Finalize()319 void Finalize() override {
320 Unlink();
321 env_ = nullptr;
322 }
323
~TrackedStringResource()324 ~TrackedStringResource() {
325 if (finalize_callback_ == nullptr) return;
326 if (env_ == nullptr) {
327 // The environment is dead. Call the finalizer directly.
328 finalize_callback_(nullptr, finalize_data_, finalize_hint_);
329 } else {
330 // The environment is still alive. Let's remove ourselves from its list
331 // of references and call the user's finalizer.
332 Unlink();
333 env_->CallFinalizer(finalize_callback_, finalize_data_, finalize_hint_);
334 }
335 }
336 };
337
338 class ExternalOneByteStringResource
339 : public v8::String::ExternalOneByteStringResource,
340 TrackedStringResource {
341 public:
ExternalOneByteStringResource(JSVM_Env env,char * string,const size_t length,JSVM_Finalize finalizeCallback,void * finalizeHint)342 ExternalOneByteStringResource(JSVM_Env env,
343 char* string,
344 const size_t length,
345 JSVM_Finalize finalizeCallback,
346 void* finalizeHint)
347 : TrackedStringResource(env, finalizeCallback, string, finalizeHint),
348 string_(string),
349 length_(length) {}
350
data() const351 const char* data() const override { return string_; }
length() const352 size_t length() const override { return length_; }
353
354 private:
355 const char* string_;
356 const size_t length_;
357 };
358
359 class ExternalStringResource : public v8::String::ExternalStringResource,
360 TrackedStringResource {
361 public:
ExternalStringResource(JSVM_Env env,char16_t * string,const size_t length,JSVM_Finalize finalizeCallback,void * finalizeHint)362 ExternalStringResource(JSVM_Env env,
363 char16_t* string,
364 const size_t length,
365 JSVM_Finalize finalizeCallback,
366 void* finalizeHint)
367 : TrackedStringResource(env, finalizeCallback, string, finalizeHint),
368 string_(reinterpret_cast<uint16_t*>(string)),
369 length_(length) {}
370
data() const371 const uint16_t* data() const override { return string_; }
length() const372 size_t length() const override { return length_; }
373
374 private:
375 const uint16_t* string_;
376 const size_t length_;
377 };
378
V8NameFromPropertyDescriptor(JSVM_Env env,const JSVM_PropertyDescriptor * p,v8::Local<v8::Name> * result)379 inline JSVM_Status V8NameFromPropertyDescriptor(
380 JSVM_Env env,
381 const JSVM_PropertyDescriptor* p,
382 v8::Local<v8::Name>* result) {
383 if (p->utf8name != nullptr) {
384 CHECK_NEW_FROM_UTF8(env, *result, p->utf8name);
385 } else {
386 v8::Local<v8::Value> property_value =
387 v8impl::V8LocalValueFromJsValue(p->name);
388
389 RETURN_STATUS_IF_FALSE(env, property_value->IsName(), JSVM_NAME_EXPECTED);
390 *result = property_value.As<v8::Name>();
391 }
392
393 return JSVM_OK;
394 }
395
396 // convert from n-api property attributes to v8::PropertyAttribute
V8PropertyAttributesFromDescriptor(const JSVM_PropertyDescriptor * descriptor)397 inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor(
398 const JSVM_PropertyDescriptor* descriptor) {
399 unsigned int attribute_flags = v8::PropertyAttribute::None;
400
401 // The JSVM_WRITABLE attribute is ignored for accessor descriptors, but
402 // V8 would throw `TypeError`s on assignment with nonexistence of a setter.
403 if ((descriptor->getter == nullptr && descriptor->setter == nullptr) &&
404 (descriptor->attributes & JSVM_WRITABLE) == 0) {
405 attribute_flags |= v8::PropertyAttribute::ReadOnly;
406 }
407
408 if ((descriptor->attributes & JSVM_ENUMERABLE) == 0) {
409 attribute_flags |= v8::PropertyAttribute::DontEnum;
410 }
411 if ((descriptor->attributes & JSVM_CONFIGURABLE) == 0) {
412 attribute_flags |= v8::PropertyAttribute::DontDelete;
413 }
414
415 return static_cast<v8::PropertyAttribute>(attribute_flags);
416 }
417
JsDeferredFromNodePersistent(v8impl::Persistent<v8::Value> * local)418 inline JSVM_Deferred JsDeferredFromNodePersistent(
419 v8impl::Persistent<v8::Value>* local) {
420 return reinterpret_cast<JSVM_Deferred>(local);
421 }
422
NodePersistentFromJsDeferred(JSVM_Deferred local)423 inline v8impl::Persistent<v8::Value>* NodePersistentFromJsDeferred(
424 JSVM_Deferred local) {
425 return reinterpret_cast<v8impl::Persistent<v8::Value>*>(local);
426 }
427
428 class HandleScopeWrapper {
429 public:
HandleScopeWrapper(v8::Isolate * isolate)430 explicit HandleScopeWrapper(v8::Isolate* isolate) : scope(isolate) {}
431
432 private:
433 v8::HandleScope scope;
434 };
435
436 // In node v0.10 version of v8, there is no EscapableHandleScope and the
437 // node v0.10 port use HandleScope::Close(Local<T> v) to mimic the behavior
438 // of a EscapableHandleScope::Escape(Local<T> v), but it is not the same
439 // semantics. This is an example of where the api abstraction fail to work
440 // across different versions.
441 class EscapableHandleScopeWrapper {
442 public:
EscapableHandleScopeWrapper(v8::Isolate * isolate)443 explicit EscapableHandleScopeWrapper(v8::Isolate* isolate)
444 : scope(isolate), escape_called_(false) {}
escape_called() const445 bool escape_called() const { return escape_called_; }
446 template <typename T>
Escape(v8::Local<T> handle)447 v8::Local<T> Escape(v8::Local<T> handle) {
448 escape_called_ = true;
449 return scope.Escape(handle);
450 }
451
452 private:
453 v8::EscapableHandleScope scope;
454 bool escape_called_;
455 };
456
JsHandleScopeFromV8HandleScope(HandleScopeWrapper * s)457 inline JSVM_HandleScope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) {
458 return reinterpret_cast<JSVM_HandleScope>(s);
459 }
460
V8HandleScopeFromJsHandleScope(JSVM_HandleScope s)461 inline HandleScopeWrapper* V8HandleScopeFromJsHandleScope(JSVM_HandleScope s) {
462 return reinterpret_cast<HandleScopeWrapper*>(s);
463 }
464
465 inline JSVM_EscapableHandleScope
JsEscapableHandleScopeFromV8EscapableHandleScope(EscapableHandleScopeWrapper * s)466 JsEscapableHandleScopeFromV8EscapableHandleScope(
467 EscapableHandleScopeWrapper* s) {
468 return reinterpret_cast<JSVM_EscapableHandleScope>(s);
469 }
470
471 inline EscapableHandleScopeWrapper*
V8EscapableHandleScopeFromJsEscapableHandleScope(JSVM_EscapableHandleScope s)472 V8EscapableHandleScopeFromJsEscapableHandleScope(
473 JSVM_EscapableHandleScope s) {
474 return reinterpret_cast<EscapableHandleScopeWrapper*>(s);
475 }
476
ConcludeDeferred(JSVM_Env env,JSVM_Deferred deferred,JSVM_Value result,bool is_resolved)477 inline JSVM_Status ConcludeDeferred(JSVM_Env env,
478 JSVM_Deferred deferred,
479 JSVM_Value result,
480 bool is_resolved) {
481 JSVM_PREAMBLE(env);
482 CHECK_ARG(env, result);
483
484 v8::Local<v8::Context> context = env->context();
485 v8impl::Persistent<v8::Value>* deferred_ref =
486 NodePersistentFromJsDeferred(deferred);
487 v8::Local<v8::Value> v8_deferred =
488 v8::Local<v8::Value>::New(env->isolate, *deferred_ref);
489
490 auto v8_resolver = v8_deferred.As<v8::Promise::Resolver>();
491
492 v8::Maybe<bool> success =
493 is_resolved ? v8_resolver->Resolve(
494 context, v8impl::V8LocalValueFromJsValue(result))
495 : v8_resolver->Reject(
496 context, v8impl::V8LocalValueFromJsValue(result));
497
498 delete deferred_ref;
499
500 RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), JSVM_GENERIC_FAILURE);
501
502 return GET_RETURN_STATUS(env);
503 }
504
505 enum UnwrapAction { KeepWrap, RemoveWrap };
506
Unwrap(JSVM_Env env,JSVM_Value jsObject,void ** result,UnwrapAction action)507 inline JSVM_Status Unwrap(JSVM_Env env,
508 JSVM_Value jsObject,
509 void** result,
510 UnwrapAction action) {
511 JSVM_PREAMBLE(env);
512 CHECK_ARG(env, jsObject);
513 if (action == KeepWrap) {
514 CHECK_ARG(env, result);
515 }
516
517 v8::Local<v8::Context> context = env->context();
518
519 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(jsObject);
520 RETURN_STATUS_IF_FALSE(env, value->IsObject(), JSVM_INVALID_ARG);
521 v8::Local<v8::Object> obj = value.As<v8::Object>();
522
523 auto val = obj->GetPrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper))
524 .ToLocalChecked();
525 RETURN_STATUS_IF_FALSE(env, val->IsExternal(), JSVM_INVALID_ARG);
526 Reference* reference =
527 static_cast<v8impl::Reference*>(val.As<v8::External>()->Value());
528
529 if (result) {
530 *result = reference->Data();
531 }
532
533 if (action == RemoveWrap) {
534 CHECK(obj->DeletePrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper))
535 .FromJust());
536 if (reference->ownership() == Ownership::kUserland) {
537 // When the wrap is been removed, the finalizer should be reset.
538 reference->ResetFinalizer();
539 } else {
540 delete reference;
541 }
542 }
543
544 return GET_RETURN_STATUS(env);
545 }
546
547 //=== Function JSVM_Callback wrapper =================================
548
549 // Use this data structure to associate callback data with each N-API function
550 // exposed to JavaScript. The structure is stored in a v8::External which gets
551 // passed into our callback wrapper. This reduces the performance impact of
552 // calling through N-API.
553 // Ref: benchmark/misc/function_call
554 // Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072
555 class CallbackBundle {
556 public:
557 // Creates an object to be made available to the static function callback
558 // wrapper, used to retrieve the native callback function and data pointer.
New(JSVM_Env env,JSVM_Callback cb)559 static inline v8::Local<v8::Value> New(JSVM_Env env,
560 JSVM_Callback cb) {
561 return v8::External::New(env->isolate, cb);
562 }
563
New(JSVM_Env env,v8impl::JSVM_PropertyHandlerCfgStruct * cb)564 static inline v8::Local<v8::Value> New(JSVM_Env env,
565 v8impl::JSVM_PropertyHandlerCfgStruct* cb) {
566 return v8::External::New(env->isolate, cb);
567 }
568 };
569
570 // Base class extended by classes that wrap V8 function and property callback
571 // info.
572 class CallbackWrapper {
573 public:
CallbackWrapper(JSVM_Value thisArg,size_t args_length,void * data)574 inline CallbackWrapper(JSVM_Value thisArg, size_t args_length, void* data)
575 : _this(thisArg), _args_length(args_length), _data(data) {}
576
GetNewTarget()577 virtual JSVM_Value GetNewTarget() {};
Args(JSVM_Value * buffer,size_t bufferlength)578 virtual void Args(JSVM_Value* buffer, size_t bufferlength) {};
579 virtual void SetReturnValue(JSVM_Value value) = 0;
580
This()581 JSVM_Value This() { return _this; }
582
ArgsLength()583 size_t ArgsLength() { return _args_length; }
584
Data()585 void* Data() { return _data; }
586
587 protected:
588 const JSVM_Value _this;
589 const size_t _args_length;
590 void* _data;
591 };
592
593 class CallbackWrapperBase : public CallbackWrapper {
594 public:
CallbackWrapperBase(const v8::FunctionCallbackInfo<v8::Value> & cbinfo,const size_t args_length)595 inline CallbackWrapperBase(const v8::FunctionCallbackInfo<v8::Value>& cbinfo,
596 const size_t args_length)
597 : CallbackWrapper(
598 JsValueFromV8LocalValue(cbinfo.This()), args_length, nullptr),
599 _cbinfo(cbinfo) {
600 _cb = (JSVM_Callback)cbinfo.Data().As<v8::External>()->Value();
601 _data = _cb->data;
602 }
603
604 protected:
InvokeCallback()605 inline void InvokeCallback() {
606 JSVM_CallbackInfo cbinfo_wrapper = reinterpret_cast<JSVM_CallbackInfo>(
607 static_cast<CallbackWrapper*>(this));
608
609 // All other pointers we need are stored in `_bundle`
610 auto context = _cbinfo.GetIsolate()->GetCurrentContext();
611 auto env = v8impl::GetContextEnv(context);
612 auto cb = _cb->callback;
613
614 JSVM_Value result = nullptr;
615 bool exceptionOccurred = false;
616 env->CallIntoModule([&](JSVM_Env env) { result = cb(env, cbinfo_wrapper); },
617 [&](JSVM_Env env, v8::Local<v8::Value> value) {
618 exceptionOccurred = true;
619 if (env->terminatedOrTerminating()) {
620 return;
621 }
622 env->isolate->ThrowException(value);
623 });
624
625 if (!exceptionOccurred && (result != nullptr)) {
626 this->SetReturnValue(result);
627 }
628 }
629
630 const v8::FunctionCallbackInfo<v8::Value>& _cbinfo;
631 JSVM_Callback _cb;
632 };
633
634 class FunctionCallbackWrapper : public CallbackWrapperBase {
635 public:
Invoke(const v8::FunctionCallbackInfo<v8::Value> & info)636 static void Invoke(const v8::FunctionCallbackInfo<v8::Value>& info) {
637 FunctionCallbackWrapper cbwrapper(info);
638 cbwrapper.InvokeCallback();
639 }
640
NewFunction(JSVM_Env env,JSVM_Callback cb,v8::Local<v8::Function> * result)641 static inline JSVM_Status NewFunction(JSVM_Env env,
642 JSVM_Callback cb,
643 v8::Local<v8::Function>* result) {
644 v8::Local<v8::Value> cbdata = v8impl::CallbackBundle::New(env, cb);
645 RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), JSVM_GENERIC_FAILURE);
646
647 v8::MaybeLocal<v8::Function> maybe_function =
648 v8::Function::New(env->context(), Invoke, cbdata);
649 CHECK_MAYBE_EMPTY(env, maybe_function, JSVM_GENERIC_FAILURE);
650
651 *result = maybe_function.ToLocalChecked();
652 return jsvm_clear_last_error(env);
653 }
654
NewTemplate(JSVM_Env env,JSVM_Callback cb,v8::Local<v8::FunctionTemplate> * result,v8::Local<v8::Signature> sig=v8::Local<v8::Signature> ())655 static inline JSVM_Status NewTemplate(
656 JSVM_Env env,
657 JSVM_Callback cb,
658 v8::Local<v8::FunctionTemplate>* result,
659 v8::Local<v8::Signature> sig = v8::Local<v8::Signature>()) {
660 v8::Local<v8::Value> cbdata = v8impl::CallbackBundle::New(env, cb);
661 RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), JSVM_GENERIC_FAILURE);
662
663 *result = v8::FunctionTemplate::New(env->isolate, Invoke, cbdata, sig);
664 return jsvm_clear_last_error(env);
665 }
666
FunctionCallbackWrapper(const v8::FunctionCallbackInfo<v8::Value> & cbinfo)667 explicit FunctionCallbackWrapper(
668 const v8::FunctionCallbackInfo<v8::Value>& cbinfo)
669 : CallbackWrapperBase(cbinfo, cbinfo.Length()) {}
670
GetNewTarget()671 JSVM_Value GetNewTarget() override {
672 if (_cbinfo.IsConstructCall()) {
673 return v8impl::JsValueFromV8LocalValue(_cbinfo.NewTarget());
674 } else {
675 return nullptr;
676 }
677 }
678
679 /*virtual*/
Args(JSVM_Value * buffer,size_t buffer_length)680 void Args(JSVM_Value* buffer, size_t buffer_length) override {
681 size_t i = 0;
682 size_t min = std::min(buffer_length, _args_length);
683
684 for (; i < min; i += 1) {
685 buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]);
686 }
687
688 if (i < buffer_length) {
689 JSVM_Value undefined =
690 v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate()));
691 for (; i < buffer_length; i += 1) {
692 buffer[i] = undefined;
693 }
694 }
695 }
696
697 /*virtual*/
SetReturnValue(JSVM_Value value)698 void SetReturnValue(JSVM_Value value) override {
699 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
700 _cbinfo.GetReturnValue().Set(val);
701 }
702 };
703
704 template <typename T>
705 class PropertyCallbackWrapperBase : public CallbackWrapper {
706 public:
PropertyCallbackWrapperBase(uint32_t index,v8::Local<v8::Name> property,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<T> & cbinfo,const size_t args_length)707 inline PropertyCallbackWrapperBase(uint32_t index, v8::Local<v8::Name> property, v8::Local<v8::Value> value,
708 const v8::PropertyCallbackInfo<T>& cbinfo, const size_t args_length)
709 : CallbackWrapper(
710 JsValueFromV8LocalValue(cbinfo.This()), args_length, nullptr),
711 _cbinfo(cbinfo), _property(property), _value(value), _index(index) {
712 _cb = (v8impl::JSVM_PropertyHandlerCfgStruct *)_cbinfo.Data().template As<v8::External>()->Value();
713 }
714
715 protected:
NameSetterInvokeCallback()716 inline void NameSetterInvokeCallback() {
717 auto context = _cbinfo.GetIsolate()->GetCurrentContext();
718 auto env = v8impl::GetContextEnv(context);
719 auto setterCb = _cb->namedSetterCallback_;
720
721 JSVM_Value innerData = nullptr;
722 if (_cb->namedPropertyData_ != nullptr) {
723 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(_cb->namedPropertyData_);
724 innerData = v8impl::JsValueFromV8LocalValue(reference->Get());
725 }
726
727 bool exceptionOccurred = false;
728 JSVM_Value result = nullptr;
729 JSVM_Value name = JsValueFromV8LocalValue(_property);
730 JSVM_Value value = JsValueFromV8LocalValue(_value);
731 JSVM_Value thisArg = this->This();
732 env->CallIntoModule([&](JSVM_Env env) {
733 if (setterCb) {
734 result = setterCb(env, name, value, thisArg, innerData);
735 }
736 },
737 [&](JSVM_Env env, v8::Local<v8::Value> value) {
738 exceptionOccurred = true;
739 if (env->terminatedOrTerminating()) {
740 return;
741 }
742 env->isolate->ThrowException(value);
743 });
744 if (!exceptionOccurred && (result != nullptr)) {
745 this->SetReturnValue(result);
746 }
747 }
748
NameGetterInvokeCallback()749 inline void NameGetterInvokeCallback() {
750 auto context = _cbinfo.GetIsolate()->GetCurrentContext();
751 auto env = v8impl::GetContextEnv(context);
752 auto getterCb = _cb->namedGetterCallback_;
753
754 JSVM_Value innerData = nullptr;
755 if (_cb->namedPropertyData_ != nullptr) {
756 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(_cb->namedPropertyData_);
757 innerData = v8impl::JsValueFromV8LocalValue(reference->Get());
758 }
759 bool exceptionOccurred = false;
760 JSVM_Value result = nullptr;
761 JSVM_Value name = JsValueFromV8LocalValue(_property);
762 JSVM_Value thisArg = this->This();
763 env->CallIntoModule([&](JSVM_Env env) {
764 if (getterCb) {
765 result = getterCb(env, name, thisArg, innerData);
766 }
767 },
768 [&](JSVM_Env env, v8::Local<v8::Value> value) {
769 exceptionOccurred = true;
770 if (env->terminatedOrTerminating()) {
771 return;
772 }
773 env->isolate->ThrowException(value);
774 });
775 if (!exceptionOccurred && (result != nullptr)) {
776 this->SetReturnValue(result);
777 }
778 }
779
NameDeleterInvokeCallback()780 inline void NameDeleterInvokeCallback() {
781 auto context = _cbinfo.GetIsolate()->GetCurrentContext();
782 auto env = v8impl::GetContextEnv(context);
783 auto deleterCb = _cb->nameDeleterCallback_;
784
785 JSVM_Value innerData = nullptr;
786 if (_cb->namedPropertyData_ != nullptr) {
787 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(_cb->namedPropertyData_);
788 innerData = v8impl::JsValueFromV8LocalValue(reference->Get());
789 }
790
791 bool exceptionOccurred = false;
792 JSVM_Value result = nullptr;
793 JSVM_Value name = JsValueFromV8LocalValue(_property);
794 JSVM_Value thisArg = this->This();
795 env->CallIntoModule([&](JSVM_Env env) {
796 if (deleterCb) {
797 result = deleterCb(env, name, thisArg, innerData);
798 }
799 },
800 [&](JSVM_Env env, v8::Local<v8::Value> value) {
801 exceptionOccurred = true;
802 if (env->terminatedOrTerminating()) {
803 return;
804 }
805 env->isolate->ThrowException(value);
806 });
807 if (!exceptionOccurred && (result != nullptr)) {
808 if (v8impl::V8LocalValueFromJsValue(result)->IsBoolean()) {
809 this->SetReturnValue(result);
810 }
811 }
812 }
813
NameEnumeratorInvokeCallback()814 inline void NameEnumeratorInvokeCallback() {
815 auto context = _cbinfo.GetIsolate()->GetCurrentContext();
816 auto env = v8impl::GetContextEnv(context);
817 auto enumeratorCb = _cb->namedEnumeratorCallback_;
818
819 JSVM_Value innerData = nullptr;
820 if (_cb->namedPropertyData_ != nullptr) {
821 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(_cb->namedPropertyData_);
822 innerData = v8impl::JsValueFromV8LocalValue(reference->Get());
823 }
824
825 bool exceptionOccurred = false;
826 JSVM_Value result = nullptr;
827 JSVM_Value thisArg = this->This();
828 env->CallIntoModule([&](JSVM_Env env) {
829 if (enumeratorCb) {
830 result = enumeratorCb(env, thisArg, innerData);
831 }
832 },
833 [&](JSVM_Env env, v8::Local<v8::Value> value) {
834 exceptionOccurred = true;
835 if (env->terminatedOrTerminating()) {
836 return;
837 }
838 env->isolate->ThrowException(value);
839 });
840 if (!exceptionOccurred && (result != nullptr)) {
841 if (v8impl::V8LocalValueFromJsValue(result)->IsArray()) {
842 this->SetReturnValue(result);
843 }
844 }
845 }
846
IndexSetterInvokeCallback()847 inline void IndexSetterInvokeCallback() {
848 auto context = _cbinfo.GetIsolate()->GetCurrentContext();
849 auto env = v8impl::GetContextEnv(context);
850 auto indexSetterCb = _cb->indexedSetterCallback_;
851
852 JSVM_Value innerData = nullptr;
853 if (_cb->indexedPropertyData_ != nullptr) {
854 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(_cb->indexedPropertyData_);
855 innerData = v8impl::JsValueFromV8LocalValue(reference->Get());
856 }
857
858 bool exceptionOccurred = false;
859 JSVM_Value result = nullptr;
860 JSVM_Value index = JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, _index));
861 JSVM_Value value = JsValueFromV8LocalValue(_value);
862 JSVM_Value thisArg = this->This();
863 env->CallIntoModule([&](JSVM_Env env) {
864 if (indexSetterCb) {
865 result = indexSetterCb(env, index, value, thisArg, innerData);
866 }
867 },
868 [&](JSVM_Env env, v8::Local<v8::Value> value) {
869 exceptionOccurred = true;
870 if (env->terminatedOrTerminating()) {
871 return;
872 }
873 env->isolate->ThrowException(value);
874 });
875 if (!exceptionOccurred && (result != nullptr)) {
876 this->SetReturnValue(result);
877 }
878 }
879
IndexGetterInvokeCallback()880 inline void IndexGetterInvokeCallback() {
881 auto context = _cbinfo.GetIsolate()->GetCurrentContext();
882 auto env = v8impl::GetContextEnv(context);
883 auto indexGetterCb = _cb->indexedGetterCallback_;
884
885 JSVM_Value innerData = nullptr;
886 if (_cb->indexedPropertyData_ != nullptr) {
887 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(_cb->indexedPropertyData_);
888 innerData = v8impl::JsValueFromV8LocalValue(reference->Get());
889 }
890
891 JSVM_Value result = nullptr;
892 bool exceptionOccurred = false;
893 JSVM_Value index = JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, _index));
894 JSVM_Value thisArg = this->This();
895 env->CallIntoModule([&](JSVM_Env env) {
896 if (indexGetterCb) {
897 result = indexGetterCb(env, index, thisArg, innerData);
898 }
899 },
900 [&](JSVM_Env env, v8::Local<v8::Value> value) {
901 exceptionOccurred = true;
902 if (env->terminatedOrTerminating()) {
903 return;
904 }
905 env->isolate->ThrowException(value);
906 });
907 if (!exceptionOccurred && (result != nullptr)) {
908 this->SetReturnValue(result);
909 }
910 }
911
IndexDeleterInvokeCallback()912 inline void IndexDeleterInvokeCallback() {
913 auto context = _cbinfo.GetIsolate()->GetCurrentContext();
914 auto env = v8impl::GetContextEnv(context);
915 auto indexDeleterCb = _cb->indexedDeleterCallback_;
916
917 JSVM_Value innerData = nullptr;
918 if (_cb->indexedPropertyData_ != nullptr) {
919 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(_cb->indexedPropertyData_);
920 innerData = v8impl::JsValueFromV8LocalValue(reference->Get());
921 }
922
923 bool exceptionOccurred = false;
924 JSVM_Value result = nullptr;
925 JSVM_Value index = JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, _index));
926 JSVM_Value thisArg = this->This();
927 env->CallIntoModule([&](JSVM_Env env) {
928 if (indexDeleterCb) {
929 result = indexDeleterCb(env, index, thisArg, innerData);
930 }
931 },
932 [&](JSVM_Env env, v8::Local<v8::Value> value) {
933 exceptionOccurred = true;
934 if (env->terminatedOrTerminating()) {
935 return;
936 }
937 env->isolate->ThrowException(value);
938 });
939 if (!exceptionOccurred && (result != nullptr)) {
940 if (v8impl::V8LocalValueFromJsValue(result)->IsBoolean()) {
941 this->SetReturnValue(result);
942 }
943 }
944 }
945
IndexEnumeratorInvokeCallback()946 inline void IndexEnumeratorInvokeCallback() {
947 auto context = _cbinfo.GetIsolate()->GetCurrentContext();
948 auto env = v8impl::GetContextEnv(context);
949 auto enumeratorCb = _cb->indexedEnumeratorCallback_;
950
951 JSVM_Value innerData = nullptr;
952 if (_cb->indexedPropertyData_ != nullptr) {
953 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(_cb->indexedPropertyData_);
954 innerData = v8impl::JsValueFromV8LocalValue(reference->Get());
955 }
956
957 bool exceptionOccurred = false;
958 JSVM_Value result = nullptr;
959 JSVM_Value thisArg = this->This();
960 env->CallIntoModule([&](JSVM_Env env) {
961 if (enumeratorCb) {
962 result = enumeratorCb(env, thisArg, innerData);
963 }
964 },
965 [&](JSVM_Env env, v8::Local<v8::Value> value) {
966 exceptionOccurred = true;
967 if (env->terminatedOrTerminating()) {
968 return;
969 }
970 env->isolate->ThrowException(value);
971 });
972 if (!exceptionOccurred && (result != nullptr)) {
973 if (v8impl::V8LocalValueFromJsValue(result)->IsArray()) {
974 this->SetReturnValue(result);
975 }
976 }
977 }
978
979 const v8::PropertyCallbackInfo<T>& _cbinfo;
980 JSVM_PropertyHandlerCfgStruct* _cb;
981 v8::Local<v8::Name> _property;
982 v8::Local<v8::Value> _value;
983 uint32_t _index;
984 };
985
986 template <typename T>
987 class PropertyCallbackWrapper : public PropertyCallbackWrapperBase<T> {
988 public:
NameSetterInvoke(v8::Local<v8::Name> property,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<v8::Value> & info)989 static void NameSetterInvoke(v8::Local<v8::Name> property, v8::Local<v8::Value> value,
990 const v8::PropertyCallbackInfo<v8::Value>& info) {
991 PropertyCallbackWrapper<v8::Value> propertyCbWrapper(property, value, info);
992 propertyCbWrapper.NameSetterInvokeCallback();
993 }
994
NameGetterInvoke(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Value> & info)995 static void NameGetterInvoke(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Value>& info) {
996 PropertyCallbackWrapper<v8::Value> propertyCbWrapper(property, v8::Local<v8::Value>(), info);
997 propertyCbWrapper.NameGetterInvokeCallback();
998 }
999
NameDeleterInvoke(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Boolean> & info)1000 static void NameDeleterInvoke(v8::Local<v8::Name> property, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
1001 PropertyCallbackWrapper<v8::Boolean> propertyCbWrapper(property, v8::Local<v8::Value>(), info);
1002 propertyCbWrapper.NameDeleterInvokeCallback();
1003 }
1004
NameEnumeratorInvoke(const v8::PropertyCallbackInfo<v8::Array> & info)1005 static void NameEnumeratorInvoke(const v8::PropertyCallbackInfo<v8::Array>& info) {
1006 PropertyCallbackWrapper<v8::Array> propertyCbWrapper(v8::Local<v8::Name>(), v8::Local<v8::Value>(), info);
1007 propertyCbWrapper.NameEnumeratorInvokeCallback();
1008 }
1009
IndexSetterInvoke(uint32_t index,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<v8::Value> & info)1010 static void IndexSetterInvoke(uint32_t index, v8::Local<v8::Value> value,
1011 const v8::PropertyCallbackInfo<v8::Value>& info) {
1012 PropertyCallbackWrapper<v8::Value> propertyCbWrapper(index, value, info);
1013 propertyCbWrapper.IndexSetterInvokeCallback();
1014 }
1015
IndexGetterInvoke(uint32_t index,const v8::PropertyCallbackInfo<v8::Value> & info)1016 static void IndexGetterInvoke(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) {
1017 PropertyCallbackWrapper<v8::Value> propertyCbWrapper(index, v8::Local<v8::Value>(), info);
1018 propertyCbWrapper.IndexGetterInvokeCallback();
1019 }
1020
IndexDeleterInvoke(uint32_t index,const v8::PropertyCallbackInfo<v8::Boolean> & info)1021 static void IndexDeleterInvoke(uint32_t index, const v8::PropertyCallbackInfo<v8::Boolean>& info) {
1022 PropertyCallbackWrapper<v8::Boolean> propertyCbWrapper(index, v8::Local<v8::Value>(), info);
1023 propertyCbWrapper.IndexDeleterInvokeCallback();
1024 }
1025
IndexEnumeratorInvoke(const v8::PropertyCallbackInfo<v8::Array> & info)1026 static void IndexEnumeratorInvoke(const v8::PropertyCallbackInfo<v8::Array>& info) {
1027 PropertyCallbackWrapper<v8::Array> propertyCbWrapper(0, v8::Local<v8::Value>(), info);
1028 propertyCbWrapper.IndexEnumeratorInvokeCallback();
1029 }
1030
PropertyCallbackWrapper(v8::Local<v8::Name> name,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<T> & cbinfo)1031 explicit PropertyCallbackWrapper(v8::Local<v8::Name> name, v8::Local<v8::Value> value,
1032 const v8::PropertyCallbackInfo<T>& cbinfo)
1033 : PropertyCallbackWrapperBase<T>(0, name, value, cbinfo, 0), _cbInfo(cbinfo) {}
1034
PropertyCallbackWrapper(uint32_t index,v8::Local<v8::Value> value,const v8::PropertyCallbackInfo<T> & cbinfo)1035 explicit PropertyCallbackWrapper(uint32_t index, v8::Local<v8::Value> value,
1036 const v8::PropertyCallbackInfo<T>& cbinfo)
1037 : PropertyCallbackWrapperBase<T>(index, v8::Local<v8::Name>(), value, cbinfo, 0), _cbInfo(cbinfo) {}
1038
1039 /*virtual*/
SetReturnValue(JSVM_Value value)1040 void SetReturnValue(JSVM_Value value) override {
1041 v8::Local<T> val = v8impl::V8LocalValueFromJsValue(value).As<T>();
1042 _cbInfo.GetReturnValue().Set(val);
1043 }
1044
1045 protected:
1046 const v8::PropertyCallbackInfo<T>& _cbInfo;
1047 };
1048
Wrap(JSVM_Env env,JSVM_Value jsObject,void * nativeObject,JSVM_Finalize finalizeCb,void * finalizeHint,JSVM_Ref * result)1049 inline JSVM_Status Wrap(JSVM_Env env,
1050 JSVM_Value jsObject,
1051 void* nativeObject,
1052 JSVM_Finalize finalizeCb,
1053 void* finalizeHint,
1054 JSVM_Ref* result) {
1055 JSVM_PREAMBLE(env);
1056 CHECK_ARG(env, jsObject);
1057
1058 v8::Local<v8::Context> context = env->context();
1059
1060 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(jsObject);
1061 RETURN_STATUS_IF_FALSE(env, value->IsObject(), JSVM_INVALID_ARG);
1062 v8::Local<v8::Object> obj = value.As<v8::Object>();
1063
1064 // If we've already wrapped this object, we error out.
1065 RETURN_STATUS_IF_FALSE(
1066 env,
1067 !obj->HasPrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper)).FromJust(),
1068 JSVM_INVALID_ARG);
1069
1070 v8impl::Reference* reference = nullptr;
1071 if (result != nullptr) {
1072 // The returned reference should be deleted via OH_JSVM_DeleteReference()
1073 // ONLY in response to the finalize callback invocation. (If it is deleted
1074 // before then, then the finalize callback will never be invoked.)
1075 // Therefore a finalize callback is required when returning a reference.
1076 CHECK_ARG(env, finalizeCb);
1077 reference = v8impl::Reference::New(env,
1078 obj,
1079 0,
1080 v8impl::Ownership::kUserland,
1081 finalizeCb,
1082 nativeObject,
1083 finalizeHint);
1084 *result = reinterpret_cast<JSVM_Ref>(reference);
1085 } else {
1086 // Create a self-deleting reference.
1087 reference = v8impl::Reference::New(
1088 env,
1089 obj,
1090 0,
1091 v8impl::Ownership::kRuntime,
1092 finalizeCb,
1093 nativeObject,
1094 finalizeCb == nullptr ? nullptr : finalizeHint);
1095 }
1096
1097 CHECK(obj->SetPrivate(context,
1098 JSVM_PRIVATE_KEY(env->isolate, wrapper),
1099 v8::External::New(env->isolate, reference))
1100 .FromJust());
1101
1102 return GET_RETURN_STATUS(env);
1103 }
1104
1105 // In JavaScript, weak references can be created for object types (Object,
1106 // Function, and external Object) and for local symbols that are created with
1107 // the `Symbol` function call. Global symbols created with the `Symbol.for`
1108 // method cannot be weak references because they are never collected.
1109 //
1110 // Currently, V8 has no API to detect if a symbol is local or global.
1111 // Until we have a V8 API for it, we consider that all symbols can be weak.
1112 // This matches the current Node-API behavior.
CanBeHeldWeakly(v8::Local<v8::Value> value)1113 inline bool CanBeHeldWeakly(v8::Local<v8::Value> value) {
1114 return value->IsObject() || value->IsSymbol();
1115 }
1116
1117 } // end of anonymous namespace
1118
ResetFinalizer()1119 void Finalizer::ResetFinalizer() {
1120 finalize_callback_ = nullptr;
1121 finalize_data_ = nullptr;
1122 finalize_hint_ = nullptr;
1123 }
1124
TrackedFinalizer(JSVM_Env env,JSVM_Finalize finalizeCallback,void * finalizeData,void * finalizeHint)1125 TrackedFinalizer::TrackedFinalizer(JSVM_Env env,
1126 JSVM_Finalize finalizeCallback,
1127 void* finalizeData,
1128 void* finalizeHint)
1129 : Finalizer(env, finalizeCallback, finalizeData, finalizeHint),
1130 RefTracker() {
1131 Link(finalizeCallback == nullptr ? &env->reflist : &env->finalizing_reflist);
1132 }
1133
New(JSVM_Env env,JSVM_Finalize finalizeCallback,void * finalizeData,void * finalizeHint)1134 TrackedFinalizer* TrackedFinalizer::New(JSVM_Env env,
1135 JSVM_Finalize finalizeCallback,
1136 void* finalizeData,
1137 void* finalizeHint) {
1138 return new TrackedFinalizer(
1139 env, finalizeCallback, finalizeData, finalizeHint);
1140 }
1141
1142 // When a TrackedFinalizer is being deleted, it may have been queued to call its
1143 // finalizer.
~TrackedFinalizer()1144 TrackedFinalizer::~TrackedFinalizer() {
1145 // Remove from the env's tracked list.
1146 Unlink();
1147 // Try to remove the finalizer from the scheduled second pass callback.
1148 env_->DequeueFinalizer(this);
1149 }
1150
Finalize()1151 void TrackedFinalizer::Finalize() {
1152 FinalizeCore(/*deleteMe:*/ true);
1153 }
1154
FinalizeCore(bool deleteMe)1155 void TrackedFinalizer::FinalizeCore(bool deleteMe) {
1156 // Swap out the field finalize_callback so that it can not be accidentally
1157 // called more than once.
1158 JSVM_Finalize finalizeCallback = finalize_callback_;
1159 void* finalizeData = finalize_data_;
1160 void* finalizeHint = finalize_hint_;
1161 ResetFinalizer();
1162
1163 // Either the RefBase is going to be deleted in the finalize_callback or not,
1164 // it should be removed from the tracked list.
1165 Unlink();
1166 // If the finalize_callback is present, it should either delete the
1167 // derived RefBase, or the RefBase ownership was set to Ownership::kRuntime
1168 // and the deleteMe parameter is true.
1169 if (finalizeCallback != nullptr) {
1170 env_->CallFinalizer(finalizeCallback, finalizeData, finalizeHint);
1171 }
1172
1173 if (deleteMe) {
1174 delete this;
1175 }
1176 }
1177
1178 // Wrapper around v8impl::Persistent that implements reference counting.
RefBase(JSVM_Env env,uint32_t initialRefcount,Ownership ownership,JSVM_Finalize finalizeCallback,void * finalizeData,void * finalizeHint)1179 RefBase::RefBase(JSVM_Env env,
1180 uint32_t initialRefcount,
1181 Ownership ownership,
1182 JSVM_Finalize finalizeCallback,
1183 void* finalizeData,
1184 void* finalizeHint)
1185 : TrackedFinalizer(env, finalizeCallback, finalizeData, finalizeHint),
1186 refcount_(initialRefcount),
1187 ownership_(ownership) {}
1188
New(JSVM_Env env,uint32_t initialRefcount,Ownership ownership,JSVM_Finalize finalizeCallback,void * finalizeData,void * finalizeHint)1189 RefBase* RefBase::New(JSVM_Env env,
1190 uint32_t initialRefcount,
1191 Ownership ownership,
1192 JSVM_Finalize finalizeCallback,
1193 void* finalizeData,
1194 void* finalizeHint) {
1195 return new RefBase(env,
1196 initialRefcount,
1197 ownership,
1198 finalizeCallback,
1199 finalizeData,
1200 finalizeHint);
1201 }
1202
Data()1203 void* RefBase::Data() {
1204 return finalize_data_;
1205 }
1206
Ref()1207 uint32_t RefBase::Ref() {
1208 return ++refcount_;
1209 }
1210
Unref()1211 uint32_t RefBase::Unref() {
1212 if (refcount_ == 0) {
1213 return 0;
1214 }
1215 return --refcount_;
1216 }
1217
RefCount()1218 uint32_t RefBase::RefCount() {
1219 return refcount_;
1220 }
1221
Finalize()1222 void RefBase::Finalize() {
1223 // If the RefBase is not Ownership::kRuntime, userland code should delete it.
1224 // Delete it if it is Ownership::kRuntime.
1225 FinalizeCore(/*deleteMe:*/ ownership_ == Ownership::kRuntime);
1226 }
1227
1228 template <typename... Args>
Reference(JSVM_Env env,v8::Local<v8::Value> value,Args &&...args)1229 Reference::Reference(JSVM_Env env, v8::Local<v8::Value> value, Args&&... args)
1230 : RefBase(env, std::forward<Args>(args)...),
1231 persistent_(env->isolate, value),
1232 can_be_weak_(CanBeHeldWeakly(value)),
1233 deleted_by_user(false),
1234 wait_callback(false) {
1235 if (RefCount() == 0) {
1236 SetWeak();
1237 }
1238 }
1239
~Reference()1240 Reference::~Reference() {
1241 // Reset the handle. And no weak callback will be invoked.
1242 persistent_.Reset();
1243 }
1244
New(JSVM_Env env,v8::Local<v8::Value> value,uint32_t initialRefcount,Ownership ownership,JSVM_Finalize finalizeCallback,void * finalizeData,void * finalizeHint)1245 Reference* Reference::New(JSVM_Env env,
1246 v8::Local<v8::Value> value,
1247 uint32_t initialRefcount,
1248 Ownership ownership,
1249 JSVM_Finalize finalizeCallback,
1250 void* finalizeData,
1251 void* finalizeHint) {
1252 return new Reference(env,
1253 value,
1254 initialRefcount,
1255 ownership,
1256 finalizeCallback,
1257 finalizeData,
1258 finalizeHint);
1259 }
1260
Ref()1261 uint32_t Reference::Ref() {
1262 // When the persistent_ is cleared in the WeakCallback, and a second pass
1263 // callback is pending, return 0 unconditionally.
1264 if (persistent_.IsEmpty()) {
1265 return 0;
1266 }
1267 uint32_t refcount = RefBase::Ref();
1268 if (refcount == 1 && can_be_weak_) {
1269 persistent_.ClearWeak();
1270 wait_callback = false;
1271 }
1272 return refcount;
1273 }
1274
Unref()1275 uint32_t Reference::Unref() {
1276 // When the persistent_ is cleared in the WeakCallback, and a second pass
1277 // callback is pending, return 0 unconditionally.
1278 if (persistent_.IsEmpty()) {
1279 return 0;
1280 }
1281 uint32_t old_refcount = RefCount();
1282 uint32_t refcount = RefBase::Unref();
1283 if (old_refcount == 1 && refcount == 0) {
1284 SetWeak();
1285 }
1286 return refcount;
1287 }
1288
Get()1289 v8::Local<v8::Value> Reference::Get() {
1290 if (persistent_.IsEmpty()) {
1291 return v8::Local<v8::Value>();
1292 } else {
1293 return v8::Local<v8::Value>::New(env_->isolate, persistent_);
1294 }
1295 }
1296
Delete()1297 void Reference::Delete() {
1298 assert(Ownership() == kUserland);
1299 if (!wait_callback) {
1300 delete this;
1301 } else {
1302 deleted_by_user = true;
1303 }
1304 }
1305
Finalize()1306 void Reference::Finalize() {
1307 // Unconditionally reset the persistent handle so that no weak callback will
1308 // be invoked again.
1309 persistent_.Reset();
1310
1311 // Chain up to perform the rest of the finalization.
1312 RefBase::Finalize();
1313 }
1314
1315 // Mark the reference as weak and eligible for collection
1316 // by the gc.
SetWeak()1317 void Reference::SetWeak() {
1318 if (can_be_weak_) {
1319 wait_callback = true;
1320 persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
1321 } else {
1322 persistent_.Reset();
1323 }
1324 }
1325
1326 // The N-API finalizer callback may make calls into the engine. V8's heap is
1327 // not in a consistent state during the weak callback, and therefore it does
1328 // not support calls back into it. Enqueue the invocation of the finalizer.
WeakCallback(const v8::WeakCallbackInfo<Reference> & data)1329 void Reference::WeakCallback(const v8::WeakCallbackInfo<Reference>& data) {
1330 Reference* reference = data.GetParameter();
1331 // The reference must be reset during the weak callback as the API protocol.
1332 reference->persistent_.Reset();
1333 assert(reference->wait_callback);
1334 // For owership == kRuntime, deleted_by_user is always false.
1335 // Due to reference may be free in InvokeFinalizerFromGC, the status of
1336 // reference should be set before finalize call.
1337 bool need_delete = reference->deleted_by_user;
1338 reference->wait_callback = false;
1339 reference->env_->InvokeFinalizerFromGC(reference);
1340 if (need_delete) {
1341 delete reference;
1342 }
1343 }
1344
1345 } // end of namespace v8impl
1346
platform()1347 v8::Platform* JSVM_Env__::platform() {
1348 return v8impl::g_platform.get();
1349 }
1350
1351 JSVM_Status JSVM_CDECL
OH_JSVM_Init(const JSVM_InitOptions * options)1352 OH_JSVM_Init(const JSVM_InitOptions* options) {
1353 static std::atomic<bool> initialized(false);
1354 if (initialized.load()) {
1355 return JSVM_GENERIC_FAILURE;
1356 }
1357 initialized.store(true);
1358 #ifdef TARGET_OHOS
1359 v8impl::ResourceSchedule::ReportKeyThread(getuid(), getprocpid(), getproctid(),
1360 v8impl::ResourceSchedule::ResType::ThreadRole::IMPORTANT_DISPLAY);
1361 #endif
1362 v8::V8::InitializePlatform(v8impl::g_platform.get());
1363
1364 if (node::ReadSystemXpmState()) {
1365 int secArgc = SECARGCNT;
1366 char *secArgv[SECARGCNT] = {"jsvm", "--jitless"};
1367 v8::V8::SetFlagsFromCommandLine(&secArgc, secArgv, false);
1368 }
1369
1370 if (options && options->argc && options->argv) {
1371 v8::V8::SetFlagsFromCommandLine(options->argc, options->argv, options->removeFlags);
1372 }
1373 v8::V8::Initialize();
1374
1375 const auto cb = v8impl::FunctionCallbackWrapper::Invoke;
1376 v8impl::externalReferenceRegistry.push_back((intptr_t)cb);
1377 if (auto p = options ? options->externalReferences : nullptr) {
1378 for (; *p != 0; p++) {
1379 v8impl::externalReferenceRegistry.push_back(*p);
1380 }
1381 }
1382 v8impl::externalReferenceRegistry.push_back(0);
1383 return JSVM_OK;
1384 }
1385
OH_JSVM_GetVM(JSVM_Env env,JSVM_VM * result)1386 JSVM_Status JSVM_CDECL OH_JSVM_GetVM(JSVM_Env env,
1387 JSVM_VM* result) {
1388 *result = reinterpret_cast<JSVM_VM>(env->isolate);
1389 return JSVM_OK;
1390 }
1391
1392 JSVM_Status JSVM_CDECL
OH_JSVM_CreateVM(const JSVM_CreateVMOptions * options,JSVM_VM * result)1393 OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, JSVM_VM* result) {
1394 #ifdef TARGET_OHOS
1395 v8impl::ResourceSchedule::ReportKeyThread(getuid(), getprocpid(), getproctid(),
1396 v8impl::ResourceSchedule::ResType::ThreadRole::USER_INTERACT);
1397 #endif
1398 v8::Isolate::CreateParams create_params;
1399 auto externalReferences = v8impl::externalReferenceRegistry.data();
1400 create_params.external_references = externalReferences;
1401
1402 v8::StartupData* snapshotBlob = nullptr;
1403 if (options && options->snapshotBlobData) {
1404 snapshotBlob = new v8::StartupData();
1405 snapshotBlob->data = options->snapshotBlobData;
1406 snapshotBlob->raw_size = options->snapshotBlobSize;
1407
1408 if (!snapshotBlob->IsValid()) {
1409 // TODO: Is VerifyCheckSum necessay if there has been a validity check?
1410 delete snapshotBlob;
1411 return JSVM_INVALID_ARG;
1412 }
1413 create_params.snapshot_blob = snapshotBlob;
1414 }
1415
1416 v8::Isolate* isolate;
1417 if (options && options->isForSnapshotting) {
1418 isolate = v8::Isolate::Allocate();
1419 auto creator = new v8::SnapshotCreator(isolate, externalReferences);
1420 v8impl::SetIsolateSnapshotCreator(isolate, creator);
1421 } else {
1422 create_params.array_buffer_allocator =
1423 v8impl::GetOrCreateDefaultArrayBufferAllocator();
1424 isolate = v8::Isolate::New(create_params);
1425 }
1426 v8impl::CreateIsolateData(isolate, snapshotBlob);
1427 *result = reinterpret_cast<JSVM_VM>(isolate);
1428
1429 return JSVM_OK;
1430 }
1431
1432 JSVM_Status JSVM_CDECL
OH_JSVM_DestroyVM(JSVM_VM vm)1433 OH_JSVM_DestroyVM(JSVM_VM vm) {
1434 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
1435 auto creator = v8impl::GetIsolateSnapshotCreator(isolate);
1436 auto data = v8impl::GetIsolateData(isolate);
1437
1438 if (creator != nullptr) {
1439 delete creator;
1440 } else {
1441 isolate->Dispose();
1442 }
1443 if (data != nullptr) {
1444 delete data;
1445 }
1446
1447 return JSVM_OK;
1448 }
1449
OH_JSVM_OpenVMScope(JSVM_VM vm,JSVM_VMScope * result)1450 JSVM_Status JSVM_CDECL OH_JSVM_OpenVMScope(JSVM_VM vm, JSVM_VMScope* result) {
1451 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
1452 auto scope = new v8::Isolate::Scope(isolate);
1453 *result = reinterpret_cast<JSVM_VMScope>(scope);
1454 return JSVM_OK;
1455 }
1456
1457 JSVM_Status JSVM_CDECL
OH_JSVM_CloseVMScope(JSVM_VM vm,JSVM_VMScope scope)1458 OH_JSVM_CloseVMScope(JSVM_VM vm, JSVM_VMScope scope) {
1459 auto v8scope = reinterpret_cast<v8::Isolate::Scope*>(scope);
1460 delete v8scope;
1461 return JSVM_OK;
1462 }
1463
1464 JSVM_Status JSVM_CDECL
OH_JSVM_CreateEnv(JSVM_VM vm,size_t propertyCount,const JSVM_PropertyDescriptor * properties,JSVM_Env * result)1465 OH_JSVM_CreateEnv(JSVM_VM vm,
1466 size_t propertyCount,
1467 const JSVM_PropertyDescriptor* properties,
1468 JSVM_Env* result) {
1469 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
1470 auto env = new JSVM_Env__(isolate, NODE_API_DEFAULT_MODULE_API_VERSION);
1471 v8::HandleScope handle_scope(isolate);
1472 auto global_template = v8::ObjectTemplate::New(isolate);
1473
1474 for (size_t i = 0; i < propertyCount; i++) {
1475 const JSVM_PropertyDescriptor* p = properties + i;
1476
1477 if ((p->attributes & JSVM_STATIC) != 0) {
1478 //Ignore static properties.
1479 continue;
1480 }
1481
1482 v8::Local<v8::Name> property_name =
1483 v8::String::NewFromUtf8(isolate, p->utf8name, v8::NewStringType::kInternalized)
1484 .ToLocalChecked();
1485
1486 v8::PropertyAttribute attributes =
1487 v8impl::V8PropertyAttributesFromDescriptor(p);
1488
1489 if (p->getter != nullptr || p->setter != nullptr) {
1490 v8::Local<v8::FunctionTemplate> getter_tpl;
1491 v8::Local<v8::FunctionTemplate> setter_tpl;
1492 if (p->getter != nullptr) {
1493 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
1494 env, p->getter, &getter_tpl));
1495 }
1496 if (p->setter != nullptr) {
1497 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
1498 env, p->setter, &setter_tpl));
1499 }
1500
1501 global_template->SetAccessorProperty(
1502 property_name, getter_tpl, setter_tpl, attributes);
1503 } else if (p->method != nullptr) {
1504 v8::Local<v8::FunctionTemplate> method_tpl;
1505 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
1506 env, p->method, &method_tpl));
1507
1508 global_template->Set(property_name, method_tpl, attributes);
1509 } else {
1510 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value);
1511 global_template->Set(property_name, value, attributes);
1512 }
1513 }
1514
1515 v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global_template);
1516 env->context_persistent.Reset(isolate, context);
1517 v8impl::SetContextEnv(context, env);
1518 *result = env;
1519 return JSVM_OK;
1520 }
1521
1522 JSVM_EXTERN JSVM_Status JSVM_CDECL
OH_JSVM_CreateEnvFromSnapshot(JSVM_VM vm,size_t index,JSVM_Env * result)1523 OH_JSVM_CreateEnvFromSnapshot(JSVM_VM vm, size_t index, JSVM_Env* result) {
1524 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
1525 v8::HandleScope handle_scope(isolate);
1526 auto maybe = v8::Context::FromSnapshot(isolate, index);
1527
1528 if (maybe.IsEmpty()) {
1529 *result = nullptr;
1530 // TODO: return error message.
1531 return JSVM_GENERIC_FAILURE;
1532 }
1533
1534 auto env = new JSVM_Env__(isolate, NODE_API_DEFAULT_MODULE_API_VERSION);
1535 auto context = maybe.ToLocalChecked();
1536 env->context_persistent.Reset(isolate, context);
1537 v8impl::SetContextEnv(context, env);
1538 *result = env;
1539
1540 return JSVM_OK;
1541 }
1542
1543 JSVM_Status JSVM_CDECL
OH_JSVM_DestroyEnv(JSVM_Env env)1544 OH_JSVM_DestroyEnv(JSVM_Env env) {
1545 env->DeleteMe();
1546 return JSVM_OK;
1547 }
1548
1549 JSVM_Status JSVM_CDECL
OH_JSVM_OpenEnvScope(JSVM_Env env,JSVM_EnvScope * result)1550 OH_JSVM_OpenEnvScope(JSVM_Env env, JSVM_EnvScope* result) {
1551 auto v8scope = new v8::Context::Scope(env->context());
1552 *result = reinterpret_cast<JSVM_EnvScope>(v8scope);
1553 return JSVM_OK;
1554 }
1555
1556 JSVM_Status JSVM_CDECL
OH_JSVM_CloseEnvScope(JSVM_Env env,JSVM_EnvScope scope)1557 OH_JSVM_CloseEnvScope(JSVM_Env env, JSVM_EnvScope scope) {
1558 auto v8scope = reinterpret_cast<v8::Context::Scope*>(scope);
1559 delete v8scope;
1560 return JSVM_OK;
1561 }
1562
1563 JSVM_Status JSVM_CDECL
OH_JSVM_CompileScript(JSVM_Env env,JSVM_Value script,const uint8_t * cachedData,size_t cachedDataLength,bool eagerCompile,bool * cacheRejected,JSVM_Script * result)1564 OH_JSVM_CompileScript(JSVM_Env env,
1565 JSVM_Value script,
1566 const uint8_t *cachedData,
1567 size_t cachedDataLength,
1568 bool eagerCompile,
1569 bool* cacheRejected,
1570 JSVM_Script* result) {
1571 JSVM_PREAMBLE(env);
1572 CHECK_ARG(env, script);
1573 CHECK_ARG(env, result);
1574
1575 v8::Local<v8::Value> v8_script = v8impl::V8LocalValueFromJsValue(script);
1576
1577 RETURN_STATUS_IF_FALSE(env, v8_script->IsString(), JSVM_STRING_EXPECTED);
1578
1579 v8::Local<v8::Context> context = env->context();
1580
1581 v8::ScriptCompiler::CachedData* cache = cachedData
1582 ? new v8::ScriptCompiler::CachedData(cachedData, cachedDataLength) : nullptr;
1583 v8::ScriptCompiler::Source scriptSource(v8_script.As<v8::String>(), cache);
1584 auto option = cache ? v8::ScriptCompiler::kConsumeCodeCache
1585 : (eagerCompile ? v8::ScriptCompiler::kEagerCompile : v8::ScriptCompiler::kNoCompileOptions);
1586
1587 auto maybe_script = v8::ScriptCompiler::Compile(context, &scriptSource, option);
1588
1589 if (cache && cacheRejected) {
1590 *cacheRejected = cache->rejected;
1591 }
1592
1593 CHECK_MAYBE_EMPTY(env, maybe_script, JSVM_GENERIC_FAILURE);
1594 v8::Local<v8::Script> compiled_script = maybe_script.ToLocalChecked();
1595 *result = reinterpret_cast<JSVM_Script>(env->NewJsvmData(compiled_script));
1596
1597 return GET_RETURN_STATUS(env);
1598 }
1599
CreateScriptOrigin(v8::Isolate * isolate,v8::Local<v8::String> resource_name,v8::ScriptType type)1600 v8::ScriptOrigin CreateScriptOrigin(
1601 v8::Isolate* isolate, v8::Local<v8::String> resource_name, v8::ScriptType type) {
1602 const int kOptionsLength = 2;
1603 const uint32_t kOptionsMagicConstant = 0xF1F2F3F0;
1604 auto options = v8::PrimitiveArray::New(isolate, kOptionsLength);
1605 options->Set(isolate, 0, v8::Uint32::New(isolate, kOptionsMagicConstant));
1606 options->Set(isolate, 1, resource_name);
1607 return v8::ScriptOrigin(isolate, resource_name, 0, 0, false, -1, v8::Local<v8::Value>(),
1608 false, false, type == v8::ScriptType::kModule, options);
1609 }
1610
PrepareStackTraceCallback(v8::Local<v8::Context> context,v8::Local<v8::Value> error,v8::Local<v8::Array> trace)1611 v8::MaybeLocal<v8::Value> PrepareStackTraceCallback(
1612 v8::Local<v8::Context> context, v8::Local<v8::Value> error, v8::Local<v8::Array> trace) {
1613 auto *isolate = context->GetIsolate();
1614 v8::TryCatch try_catch(isolate);
1615 v8::Local<v8::String> moduleName =
1616 v8::String::NewFromUtf8(isolate, "sourcemap").ToLocalChecked();
1617 v8::Local<v8::String> moduleSourceString =
1618 v8::String::NewFromUtf8(isolate, SourceMapRunner.c_str()).ToLocalChecked();
1619
1620 v8::ScriptOrigin moduleOrigin =
1621 CreateScriptOrigin(isolate, moduleName, v8::ScriptType::kClassic);
1622 v8::Local<v8::Context> moduleContext = v8::Context::New(isolate);
1623 v8::ScriptCompiler::Source moduleSource(moduleSourceString, moduleOrigin);
1624 auto script =
1625 v8::Script::Compile(moduleContext, moduleSourceString, &moduleOrigin).ToLocalChecked();
1626 auto result = script->Run(moduleContext).ToLocalChecked();
1627 auto resultFunc = v8::Local<v8::Function>::Cast(result);
1628
1629 v8::Local<v8::Value> element = trace->Get(context, 0).ToLocalChecked();
1630 std::string fileName = "";
1631 if (element->IsObject()) {
1632 auto obj = element->ToObject(context);
1633 auto getFileName = v8::String::NewFromUtf8(isolate, "getFileName", v8::NewStringType::kNormal);
1634 auto function = obj.ToLocalChecked()->Get(context, getFileName.ToLocalChecked()).ToLocalChecked();
1635 auto lineNumberFunction = v8::Local<v8::Function>::Cast(function);
1636 auto fileNameObj = lineNumberFunction->Call(context, obj.ToLocalChecked(), 0, {});
1637 fileName = std::string(*v8::String::Utf8Value(isolate, fileNameObj.ToLocalChecked()));
1638 }
1639 auto &&sourceMapUrl = (!fileName.empty()) ? v8impl::GetSourceMapFromFileName(std::move(fileName)) : "";
1640 std::ifstream sourceMapfile(sourceMapUrl);
1641 std::string content = "";
1642 if (sourceMapfile.good()) {
1643 std::stringstream buffer;
1644 buffer << sourceMapfile.rdbuf();
1645 content = buffer.str();
1646 }
1647 auto sourceMapObject = v8::String::NewFromUtf8(
1648 isolate, content.c_str(), v8::NewStringType::kNormal, content.length());
1649 v8::Local<v8::Value> args[] = { error, trace, sourceMapObject.ToLocalChecked() };
1650 return resultFunc->Call(moduleContext, v8::Undefined(isolate), node::arraysize(args), args);
1651 }
1652
1653 JSVM_Status JSVM_CDECL
OH_JSVM_CompileScriptWithOrigin(JSVM_Env env,JSVM_Value script,const uint8_t * cachedData,size_t cachedDataLength,bool eagerCompile,bool * cacheRejected,JSVM_ScriptOrigin * origin,JSVM_Script * result)1654 OH_JSVM_CompileScriptWithOrigin(JSVM_Env env,
1655 JSVM_Value script,
1656 const uint8_t* cachedData,
1657 size_t cachedDataLength,
1658 bool eagerCompile,
1659 bool* cacheRejected,
1660 JSVM_ScriptOrigin* origin,
1661 JSVM_Script* result) {
1662 JSVM_PREAMBLE(env);
1663 CHECK_ARG(env, script);
1664 CHECK_ARG(env, result);
1665 CHECK_NOT_NULL(origin->resourceName);
1666
1667 v8::Local<v8::Value> v8_script = v8impl::V8LocalValueFromJsValue(script);
1668
1669 RETURN_STATUS_IF_FALSE(env, v8_script->IsString(), JSVM_STRING_EXPECTED);
1670
1671 v8::Local<v8::Context> context = env->context();
1672 auto *isolate = context->GetIsolate();
1673
1674 if (origin->sourceMapUrl) {
1675 v8impl::SetFileToSourceMapMapping(origin->resourceName, origin->sourceMapUrl);
1676 isolate->SetPrepareStackTraceCallback(PrepareStackTraceCallback);
1677 }
1678 auto sourceMapUrl = !origin->sourceMapUrl ? v8::Local<v8::Value>() :
1679 v8::String::NewFromUtf8(isolate, origin->sourceMapUrl).ToLocalChecked().As<v8::Value>();
1680 auto resourceName =
1681 v8::String::NewFromUtf8(isolate, origin->resourceName).ToLocalChecked();
1682 v8::ScriptOrigin scriptOrigin(isolate, resourceName,
1683 origin->resourceLineOffset, origin->resourceColumnOffset, false, -1, sourceMapUrl);
1684
1685 v8::ScriptCompiler::CachedData* cache = cachedData
1686 ? new v8::ScriptCompiler::CachedData(cachedData, cachedDataLength) : nullptr;
1687 v8::ScriptCompiler::Source scriptSource(v8_script.As<v8::String>(), scriptOrigin, cache);
1688 auto option = cache ? v8::ScriptCompiler::kConsumeCodeCache
1689 : (eagerCompile ? v8::ScriptCompiler::kEagerCompile : v8::ScriptCompiler::kNoCompileOptions);
1690
1691 auto maybe_script = v8::ScriptCompiler::Compile(context, &scriptSource, option);
1692
1693 if (cache && cacheRejected) {
1694 *cacheRejected = cache->rejected;
1695 }
1696
1697 CHECK_MAYBE_EMPTY(env, maybe_script, JSVM_GENERIC_FAILURE);
1698 v8::Local<v8::Script> compiled_script = maybe_script.ToLocalChecked();
1699 *result = reinterpret_cast<JSVM_Script>(env->NewJsvmData(compiled_script));
1700
1701 return GET_RETURN_STATUS(env);
1702 }
1703
1704 class CompileOptionResolver {
1705 public:
CompileOptionResolver(size_t length,JSVM_CompileOptions options[],v8::Isolate * isolate)1706 CompileOptionResolver(size_t length, JSVM_CompileOptions options[], v8::Isolate *isolate) {
1707 for (size_t i = 0; i < length; i++) {
1708 switch(options[i].id) {
1709 case JSVM_COMPILE_MODE: {
1710 v8Option = static_cast<v8::ScriptCompiler::CompileOptions>(options[i].content.num);
1711 break;
1712 }
1713 case JSVM_COMPILE_CODE_CACHE: {
1714 auto cache = static_cast<JSVM_CodeCache*>(options[i].content.ptr);
1715 cachedData = cache->cache ?
1716 new v8::ScriptCompiler::CachedData(cache->cache, cache->length) : nullptr;
1717 break;
1718 }
1719 case JSVM_COMPILE_SCRIPT_ORIGIN: {
1720 jsvmOrigin = static_cast<JSVM_ScriptOrigin*>(options[i].content.ptr);
1721 break;
1722 }
1723 case JSVM_COMPILE_COMPILE_PROFILE: {
1724 profile = static_cast<JSVM_CompileProfile*>(options[i].content.ptr);
1725 break;
1726 }
1727 case JSVM_COMPILE_ENABLE_SOURCE_MAP: {
1728 enableSourceMap = options[i].content.boolean;
1729 break;
1730 }
1731 default: {
1732 continue;
1733 }
1734 }
1735 }
1736 auto sourceString = jsvmOrigin ? jsvmOrigin->resourceName :
1737 "script_" + std::to_string(compileCount++);
1738 auto sourceMapPtr = jsvmOrigin && jsvmOrigin->sourceMapUrl ?
1739 jsvmOrigin->sourceMapUrl : nullptr;
1740 auto sourceMapUrl = (jsvmOrigin && jsvmOrigin->sourceMapUrl) ?
1741 v8::String::NewFromUtf8(isolate, jsvmOrigin->sourceMapUrl).ToLocalChecked().As<v8::Value>() :
1742 v8::Local<v8::Value>();
1743 auto resourceName = v8::String::NewFromUtf8(isolate, sourceString.c_str()).ToLocalChecked();
1744 v8Origin = new v8::ScriptOrigin(isolate, resourceName,
1745 jsvmOrigin ? jsvmOrigin->resourceLineOffset : 0,
1746 jsvmOrigin ? jsvmOrigin->resourceColumnOffset : 0,
1747 false, -1, sourceMapUrl);
1748 if (enableSourceMap && sourceMapPtr) {
1749 v8impl::SetFileToSourceMapMapping(jsvmOrigin->resourceName, sourceMapPtr);
1750 isolate->SetPrepareStackTraceCallback(PrepareStackTraceCallback);
1751 }
1752 if (v8Option == v8::ScriptCompiler::kConsumeCodeCache && !cachedData) {
1753 hasInvalidOption = true;
1754 }
1755 }
1756
~CompileOptionResolver()1757 ~CompileOptionResolver() {
1758 delete v8Origin;
1759 v8Origin = nullptr;
1760 }
1761
1762 v8::ScriptCompiler::CompileOptions v8Option =
1763 v8::ScriptCompiler::kNoCompileOptions;
1764 v8::ScriptCompiler::CachedData *cachedData = nullptr;
1765 v8::ScriptOrigin *v8Origin = nullptr;
1766 JSVM_CompileProfile *profile = nullptr;
1767 JSVM_ScriptOrigin *jsvmOrigin = nullptr;
1768 bool enableSourceMap = false;
1769 static size_t compileCount;
1770 bool hasInvalidOption = false;
1771 };
1772
1773 size_t CompileOptionResolver::compileCount = 0;
1774
1775 JSVM_Status JSVM_CDECL
OH_JSVM_CompileScriptWithOptions(JSVM_Env env,JSVM_Value script,size_t optionCount,JSVM_CompileOptions options[],JSVM_Script * result)1776 OH_JSVM_CompileScriptWithOptions(JSVM_Env env,
1777 JSVM_Value script,
1778 size_t optionCount,
1779 JSVM_CompileOptions options[],
1780 JSVM_Script* result) {
1781 JSVM_PREAMBLE(env);
1782 CHECK_ARG(env, script);
1783 CHECK_ARG(env, result);
1784
1785 v8::Local<v8::Context> context = env->context();
1786 auto *isolate = context->GetIsolate();
1787 CompileOptionResolver optionResolver(optionCount, options, isolate);
1788 RETURN_STATUS_IF_FALSE(env, !optionResolver.hasInvalidOption, JSVM_INVALID_ARG);
1789
1790 v8::Local<v8::Value> v8_script = v8impl::V8LocalValueFromJsValue(script);
1791
1792 RETURN_STATUS_IF_FALSE(env, v8_script->IsString(), JSVM_STRING_EXPECTED);
1793
1794 v8::ScriptCompiler::Source scriptSource(v8_script.As<v8::String>(),
1795 *optionResolver.v8Origin, optionResolver.cachedData);
1796 auto maybe_script = v8::ScriptCompiler::Compile(context, &scriptSource, optionResolver.v8Option);
1797
1798 CHECK_MAYBE_EMPTY(env, maybe_script, JSVM_GENERIC_FAILURE);
1799 v8::Local<v8::Script> compiled_script = maybe_script.ToLocalChecked();
1800 *result = reinterpret_cast<JSVM_Script>(env->NewJsvmData(compiled_script));
1801
1802 return GET_RETURN_STATUS(env);
1803 }
1804
1805 JSVM_Status JSVM_CDECL
OH_JSVM_CreateCodeCache(JSVM_Env env,JSVM_Script script,const uint8_t ** data,size_t * length)1806 OH_JSVM_CreateCodeCache(JSVM_Env env,
1807 JSVM_Script script,
1808 const uint8_t** data,
1809 size_t* length) {
1810 auto jsvmData = reinterpret_cast<JSVM_Data__*>(script);
1811 auto v8script = jsvmData->ToV8Local<v8::Script>(env->isolate);
1812 v8::ScriptCompiler::CachedData* cache;
1813 cache = v8::ScriptCompiler::CreateCodeCache(v8script->GetUnboundScript());
1814
1815 if (cache == nullptr) {
1816 // TODO: return error
1817 return jsvm_set_last_error(env, JSVM_GENERIC_FAILURE);
1818 }
1819
1820 *data = cache->data;
1821 *length = cache->length;
1822 cache->buffer_policy = v8::ScriptCompiler::CachedData::BufferNotOwned;
1823 delete cache;
1824 return JSVM_OK;
1825 }
1826
1827 JSVM_Status JSVM_CDECL
OH_JSVM_RunScript(JSVM_Env env,JSVM_Script script,JSVM_Value * result)1828 OH_JSVM_RunScript(JSVM_Env env, JSVM_Script script, JSVM_Value* result) {
1829 JSVM_PREAMBLE(env);
1830 CHECK_ARG(env, script);
1831 CHECK_ARG(env, result);
1832
1833 auto jsvmData = reinterpret_cast<JSVM_Data__*>(script);
1834 auto v8script = jsvmData->ToV8Local<v8::Script>(env->isolate);
1835 auto script_result = v8script->Run(env->context());
1836 CHECK_MAYBE_EMPTY(env, script_result, JSVM_GENERIC_FAILURE);
1837 *result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked());
1838
1839 return GET_RETURN_STATUS(env);
1840 }
1841
1842 JSVM_Status JSVM_CDECL
OH_JSVM_JsonParse(JSVM_Env env,JSVM_Value json_string,JSVM_Value * result)1843 OH_JSVM_JsonParse(JSVM_Env env, JSVM_Value json_string, JSVM_Value* result) {
1844 JSVM_PREAMBLE(env);
1845 CHECK_ARG(env, json_string);
1846
1847 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(json_string);
1848 RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED);
1849
1850 auto maybe = v8::JSON::Parse(env->context(), val.As<v8::String>());
1851 CHECK_MAYBE_EMPTY(env, maybe,JSVM_GENERIC_FAILURE);
1852 *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
1853
1854 return GET_RETURN_STATUS(env);
1855 }
1856
1857 JSVM_Status JSVM_CDECL
OH_JSVM_JsonStringify(JSVM_Env env,JSVM_Value json_object,JSVM_Value * result)1858 OH_JSVM_JsonStringify(JSVM_Env env, JSVM_Value json_object, JSVM_Value* result) {
1859 JSVM_PREAMBLE(env);
1860 CHECK_ARG(env, json_object);
1861
1862 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(json_object);
1863
1864 auto maybe = v8::JSON::Stringify(env->context(), val);
1865 CHECK_MAYBE_EMPTY(env, maybe,JSVM_GENERIC_FAILURE);
1866 *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
1867
1868 return GET_RETURN_STATUS(env);
1869 }
1870
1871 JSVM_Status JSVM_CDECL
OH_JSVM_CreateSnapshot(JSVM_VM vm,size_t contextCount,const JSVM_Env * contexts,const char ** blobData,size_t * blobSize)1872 OH_JSVM_CreateSnapshot(JSVM_VM vm,
1873 size_t contextCount,
1874 const JSVM_Env* contexts,
1875 const char** blobData,
1876 size_t* blobSize) {
1877 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
1878 auto creator = v8impl::GetIsolateSnapshotCreator(isolate);
1879
1880 if (creator == nullptr) {
1881 // TODO: return specific error message.
1882 return JSVM_GENERIC_FAILURE;
1883 }
1884 {
1885 v8::HandleScope scope(isolate);
1886 v8::Local<v8::Context> default_context = v8::Context::New(isolate);
1887 creator->SetDefaultContext(default_context);
1888 // NOTE: The order of the added data must be consistent with the order of
1889 // getting data in v8impl::CreateIsolateData.
1890 creator->AddData(JSVM_PRIVATE_KEY(isolate, wrapper));
1891 creator->AddData(JSVM_PRIVATE_KEY(isolate, type_tag));
1892
1893 for (size_t i = 0; i < contextCount; i++) {
1894 auto ctx = contexts[i]->context();
1895 creator->AddData(ctx, ctx);
1896 creator->AddContext(ctx);
1897 }
1898 }
1899 auto blob = creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kKeep);
1900 *blobData = blob.data;
1901 *blobSize = blob.raw_size;
1902
1903 return JSVM_OK;
1904 }
1905
OH_JSVM_GetVMInfo(JSVM_VMInfo * result)1906 JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_GetVMInfo(JSVM_VMInfo* result) {
1907 result->apiVersion = 1;
1908 result->engine = "v8";
1909 result->version = V8_VERSION_STRING;
1910 result->cachedDataVersionTag = v8::ScriptCompiler::CachedDataVersionTag();
1911 return JSVM_OK;
1912 }
1913
1914 JSVM_EXTERN JSVM_Status JSVM_CDECL
OH_JSVM_MemoryPressureNotification(JSVM_Env env,JSVM_MemoryPressureLevel level)1915 OH_JSVM_MemoryPressureNotification(JSVM_Env env,
1916 JSVM_MemoryPressureLevel level) {
1917 CHECK_ENV(env);
1918 env->isolate->MemoryPressureNotification(v8::MemoryPressureLevel(level));
1919 return jsvm_clear_last_error(env);
1920 }
1921
1922 JSVM_EXTERN JSVM_Status JSVM_CDECL
OH_JSVM_GetHeapStatistics(JSVM_VM vm,JSVM_HeapStatistics * result)1923 OH_JSVM_GetHeapStatistics(JSVM_VM vm, JSVM_HeapStatistics* result) {
1924 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
1925 v8::HeapStatistics stats;
1926 isolate->GetHeapStatistics(&stats);
1927 result->totalHeapSize = stats.total_heap_size();
1928 result->totalHeapSizeExecutable = stats.total_heap_size_executable();
1929 result->totalPhysicalSize = stats.total_physical_size();
1930 result->totalAvailableSize = stats.total_available_size();
1931 result->usedHeapSize = stats.used_heap_size();
1932 result->heapSizeLimit = stats.heap_size_limit();
1933 result->mallocedMemory = stats.malloced_memory();
1934 result->externalMemory = stats.external_memory();
1935 result->peakMallocedMemory = stats.peak_malloced_memory();
1936 result->numberOfNativeContexts = stats.number_of_native_contexts();
1937 result->numberOfDetachedContexts = stats.number_of_detached_contexts();
1938 result->totalGlobalHandlesSize = stats.total_global_handles_size();
1939 result->usedGlobalHandlesSize = stats.used_global_handles_size();
1940 return JSVM_OK;
1941 }
1942
1943 JSVM_EXTERN JSVM_Status JSVM_CDECL
OH_JSVM_StartCpuProfiler(JSVM_VM vm,JSVM_CpuProfiler * result)1944 OH_JSVM_StartCpuProfiler(JSVM_VM vm, JSVM_CpuProfiler* result) {
1945 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
1946 auto profiler = v8::CpuProfiler::New(isolate);
1947 v8::HandleScope scope(isolate);
1948 v8::CpuProfilingOptions options;
1949 profiler->Start(v8::String::Empty(isolate), std::move(options));
1950 *result = reinterpret_cast<JSVM_CpuProfiler>(profiler);
1951 return JSVM_OK;
1952 }
1953
1954 JSVM_EXTERN JSVM_Status JSVM_CDECL
OH_JSVM_StopCpuProfiler(JSVM_VM vm,JSVM_CpuProfiler profiler,JSVM_OutputStream stream,void * streamData)1955 OH_JSVM_StopCpuProfiler(JSVM_VM vm, JSVM_CpuProfiler profiler,
1956 JSVM_OutputStream stream, void* streamData) {
1957 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
1958 auto v8profiler = reinterpret_cast<v8::CpuProfiler*>(profiler);
1959 v8::HandleScope scope(isolate);
1960 auto profile = v8profiler->StopProfiling(v8::String::Empty(isolate));
1961 v8impl::OutputStream os(stream, streamData);
1962 profile->Serialize(&os);
1963 return JSVM_OK;
1964 }
1965
1966 JSVM_EXTERN JSVM_Status JSVM_CDECL
OH_JSVM_TakeHeapSnapshot(JSVM_VM vm,JSVM_OutputStream stream,void * streamData)1967 OH_JSVM_TakeHeapSnapshot(JSVM_VM vm,
1968 JSVM_OutputStream stream, void* streamData) {
1969 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
1970 auto profiler = isolate->GetHeapProfiler();
1971 auto snapshot = profiler->TakeHeapSnapshot();
1972 v8impl::OutputStream os(stream, streamData);
1973 snapshot->Serialize(&os);
1974 return JSVM_OK;
1975 }
1976
1977 JSVM_EXTERN JSVM_Status JSVM_CDECL
OH_JSVM_OpenInspector(JSVM_Env env,const char * host,uint16_t port)1978 OH_JSVM_OpenInspector(JSVM_Env env, const char* host, uint16_t port) {
1979 JSVM_PREAMBLE(env);
1980
1981 std::string inspectorPath;
1982 std::string hostName(host);
1983 auto hostPort =
1984 std::make_shared<node::ExclusiveAccess<node::HostPort>>(hostName, port);
1985 env->inspector_agent()->Start(inspectorPath, hostPort, true, false);
1986
1987 return GET_RETURN_STATUS(env);
1988 }
1989
OH_JSVM_CloseInspector(JSVM_Env env)1990 JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_CloseInspector(JSVM_Env env) {
1991 JSVM_PREAMBLE(env);
1992 auto agent = env->inspector_agent();
1993 if (!agent->IsActive()) {
1994 return JSVM_GENERIC_FAILURE;
1995 }
1996 agent->Stop();
1997 return GET_RETURN_STATUS(env);
1998 }
1999
OH_JSVM_WaitForDebugger(JSVM_Env env,bool breakNextLine)2000 JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_WaitForDebugger(JSVM_Env env, bool breakNextLine) {
2001 JSVM_PREAMBLE(env);
2002 auto agent = env->inspector_agent();
2003 if (!agent->IsActive()) {
2004 return JSVM_GENERIC_FAILURE;
2005 }
2006
2007 agent->WaitForConnect();
2008 if (breakNextLine) {
2009 agent->PauseOnNextJavascriptStatement("Break on debugger attached");
2010 }
2011
2012 return GET_RETURN_STATUS(env);
2013 }
2014
OH_JSVM_PumpMessageLoop(JSVM_VM vm,bool * result)2015 JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_PumpMessageLoop(JSVM_VM vm, bool* result) {
2016 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
2017 *result = v8::platform::PumpMessageLoop(v8impl::g_platform.get(), isolate);
2018 return JSVM_OK;
2019 }
2020
OH_JSVM_PerformMicrotaskCheckpoint(JSVM_VM vm)2021 JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_PerformMicrotaskCheckpoint(JSVM_VM vm) {
2022 auto isolate = reinterpret_cast<v8::Isolate*>(vm);
2023 isolate->PerformMicrotaskCheckpoint();
2024 return JSVM_OK;
2025 }
2026
2027 // Warning: Keep in-sync with JSVM_Status enum
2028 static const char* error_messages[] = {
2029 nullptr,
2030 "Invalid argument",
2031 "An object was expected",
2032 "A string was expected",
2033 "A string or symbol was expected",
2034 "A function was expected",
2035 "A number was expected",
2036 "A boolean was expected",
2037 "An array was expected",
2038 "Unknown failure",
2039 "An exception is pending",
2040 "The async work item was cancelled",
2041 "OH_JSVM_EscapeHandle already called on scope",
2042 "Invalid handle scope usage",
2043 "Invalid callback scope usage",
2044 "Thread-safe function queue is full",
2045 "Thread-safe function handle is closing",
2046 "A bigint was expected",
2047 "A date was expected",
2048 "An arraybuffer was expected",
2049 "A detachable arraybuffer was expected",
2050 "Main thread would deadlock",
2051 "External buffers are not allowed",
2052 "Cannot run JavaScript",
2053 };
2054
OH_JSVM_GetLastErrorInfo(JSVM_Env env,const JSVM_ExtendedErrorInfo ** result)2055 JSVM_Status JSVM_CDECL OH_JSVM_GetLastErrorInfo(
2056 JSVM_Env env, const JSVM_ExtendedErrorInfo** result) {
2057 CHECK_ENV(env);
2058 CHECK_ARG(env, result);
2059
2060 // The value of the constant below must be updated to reference the last
2061 // message in the `JSVM_Status` enum each time a new error message is added.
2062 // We don't have a jsvm_status_last as this would result in an ABI
2063 // change each time a message was added.
2064 const int last_status = JSVM_CANNOT_RUN_JS;
2065
2066 static_assert(JSVM_ARRAYSIZE(error_messages) == last_status + 1,
2067 "Count of error messages must match count of error values");
2068 CHECK_LE(env->last_error.errorCode, last_status);
2069 // Wait until someone requests the last error information to fetch the error
2070 // message string
2071 env->last_error.errorMessage = error_messages[env->last_error.errorCode];
2072
2073 if (env->last_error.errorCode == JSVM_OK) {
2074 jsvm_clear_last_error(env);
2075 }
2076 *result = &(env->last_error);
2077 return JSVM_OK;
2078 }
2079
OH_JSVM_CreateFunction(JSVM_Env env,const char * utf8name,size_t length,JSVM_Callback cb,JSVM_Value * result)2080 JSVM_Status JSVM_CDECL OH_JSVM_CreateFunction(JSVM_Env env,
2081 const char* utf8name,
2082 size_t length,
2083 JSVM_Callback cb,
2084 JSVM_Value* result) {
2085 JSVM_PREAMBLE(env);
2086 CHECK_ARG(env, result);
2087 CHECK_ARG(env, cb);
2088
2089 v8::Local<v8::Function> return_value;
2090 v8::EscapableHandleScope scope(env->isolate);
2091 v8::Local<v8::Function> fn;
2092 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(
2093 env, cb, &fn));
2094 return_value = scope.Escape(fn);
2095
2096 if (utf8name != nullptr) {
2097 v8::Local<v8::String> name_string;
2098 CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length);
2099 return_value->SetName(name_string);
2100 }
2101
2102 *result = v8impl::JsValueFromV8LocalValue(return_value);
2103
2104 return GET_RETURN_STATUS(env);
2105 }
2106
OH_JSVM_CreateFunctionWithScript(JSVM_Env env,const char * funcName,size_t length,size_t argc,const JSVM_Value * argv,JSVM_Value script,JSVM_Value * result)2107 JSVM_Status JSVM_CDECL OH_JSVM_CreateFunctionWithScript(JSVM_Env env,
2108 const char* funcName,
2109 size_t length,
2110 size_t argc,
2111 const JSVM_Value* argv,
2112 JSVM_Value script,
2113 JSVM_Value* result) {
2114 JSVM_PREAMBLE(env);
2115 CHECK_ARG(env, script);
2116 CHECK_ARG(env, result);
2117 if (argc > 0) {
2118 CHECK_ARG(env, argv);
2119 for (auto i = 0; i < argc; i++) {
2120 RETURN_STATUS_IF_FALSE(env, v8impl::V8LocalValueFromJsValue(argv[i])->IsString(),
2121 JSVM_STRING_EXPECTED);
2122 }
2123 }
2124
2125 v8::Local<v8::Value> v8_script = v8impl::V8LocalValueFromJsValue(script);
2126
2127 RETURN_STATUS_IF_FALSE(env, v8_script->IsString(), JSVM_STRING_EXPECTED);
2128
2129 v8::ScriptCompiler::Source scriptSource(v8_script.As<v8::String>());
2130
2131 v8::Local<v8::Context> context = env->context();
2132
2133 v8::MaybeLocal<v8::Function> maybe_fun =
2134 v8::ScriptCompiler::CompileFunction(context, &scriptSource, argc,
2135 reinterpret_cast<v8::Local<v8::String>*>(const_cast<JSVM_Value*>(argv)));
2136
2137 CHECK_MAYBE_EMPTY(env, maybe_fun, JSVM_GENERIC_FAILURE);
2138
2139 v8::Local<v8::Function> func = maybe_fun.ToLocalChecked();
2140
2141 if (funcName != nullptr) {
2142 v8::Local<v8::String> funcNameString;
2143 CHECK_NEW_FROM_UTF8_LEN(env, funcNameString, funcName, length);
2144 func->SetName(funcNameString);
2145 }
2146
2147 *result =
2148 v8impl::JsValueFromV8LocalValue(func);
2149
2150 return GET_RETURN_STATUS(env);
2151 }
2152
2153 JSVM_Status JSVM_CDECL
OH_JSVM_DefineClass(JSVM_Env env,const char * utf8name,size_t length,JSVM_Callback constructor,size_t propertyCount,const JSVM_PropertyDescriptor * properties,JSVM_Value * result)2154 OH_JSVM_DefineClass(JSVM_Env env,
2155 const char* utf8name,
2156 size_t length,
2157 JSVM_Callback constructor,
2158 size_t propertyCount,
2159 const JSVM_PropertyDescriptor* properties,
2160 JSVM_Value* result) {
2161 JSVM_PREAMBLE(env);
2162 CHECK_ARG(env, result);
2163 CHECK_ARG(env, constructor);
2164
2165 if (propertyCount > 0) {
2166 CHECK_ARG(env, properties);
2167 }
2168
2169 v8::Isolate* isolate = env->isolate;
2170
2171 v8::EscapableHandleScope scope(isolate);
2172 v8::Local<v8::FunctionTemplate> tpl;
2173 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
2174 env, constructor, &tpl));
2175
2176 v8::Local<v8::String> name_string;
2177 CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length);
2178 tpl->SetClassName(name_string);
2179
2180 size_t static_property_count = 0;
2181 for (size_t i = 0; i < propertyCount; i++) {
2182 const JSVM_PropertyDescriptor* p = properties + i;
2183
2184 if ((p->attributes & JSVM_STATIC) != 0) {
2185 // Static properties are handled separately below.
2186 static_property_count++;
2187 continue;
2188 }
2189
2190 v8::Local<v8::Name> property_name;
2191 STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name));
2192
2193 v8::PropertyAttribute attributes =
2194 v8impl::V8PropertyAttributesFromDescriptor(p);
2195
2196 // This code is similar to that in OH_JSVM_DefineProperties(); the
2197 // difference is it applies to a template instead of an object,
2198 // and preferred PropertyAttribute for lack of PropertyDescriptor
2199 // support on ObjectTemplate.
2200 if (p->getter != nullptr || p->setter != nullptr) {
2201 v8::Local<v8::FunctionTemplate> getter_tpl;
2202 v8::Local<v8::FunctionTemplate> setter_tpl;
2203 if (p->getter != nullptr) {
2204 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
2205 env, p->getter, &getter_tpl));
2206 }
2207 if (p->setter != nullptr) {
2208 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
2209 env, p->setter, &setter_tpl));
2210 }
2211
2212 tpl->PrototypeTemplate()->SetAccessorProperty(property_name,
2213 getter_tpl,
2214 setter_tpl,
2215 attributes,
2216 v8::AccessControl::DEFAULT);
2217 } else if (p->method != nullptr) {
2218 v8::Local<v8::FunctionTemplate> t;
2219 if (p->attributes & JSVM_NO_RECEIVER_CHECK) {
2220 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
2221 env, p->method, &t));
2222 } else {
2223 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
2224 env, p->method, &t, v8::Signature::New(isolate, tpl)));
2225 }
2226
2227 tpl->PrototypeTemplate()->Set(property_name, t, attributes);
2228 } else {
2229 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value);
2230 tpl->PrototypeTemplate()->Set(property_name, value, attributes);
2231 }
2232 }
2233
2234 v8::Local<v8::Context> context = env->context();
2235 *result = v8impl::JsValueFromV8LocalValue(
2236 scope.Escape(tpl->GetFunction(context).ToLocalChecked()));
2237
2238 if (static_property_count > 0) {
2239 std::vector<JSVM_PropertyDescriptor> static_descriptors;
2240 static_descriptors.reserve(static_property_count);
2241
2242 for (size_t i = 0; i < propertyCount; i++) {
2243 const JSVM_PropertyDescriptor* p = properties + i;
2244 if ((p->attributes & JSVM_STATIC) != 0) {
2245 static_descriptors.push_back(*p);
2246 }
2247 }
2248
2249 STATUS_CALL(OH_JSVM_DefineProperties(
2250 env, *result, static_descriptors.size(), static_descriptors.data()));
2251 }
2252
2253 return GET_RETURN_STATUS(env);
2254 }
2255
OH_JSVM_GetPropertyNames(JSVM_Env env,JSVM_Value object,JSVM_Value * result)2256 JSVM_Status JSVM_CDECL OH_JSVM_GetPropertyNames(JSVM_Env env,
2257 JSVM_Value object,
2258 JSVM_Value* result) {
2259 return OH_JSVM_GetAllPropertyNames(
2260 env,
2261 object,
2262 JSVM_KEY_INCLUDE_PROTOTYPES,
2263 static_cast<JSVM_KeyFilter>(JSVM_KEY_ENUMERABLE | JSVM_KEY_SKIP_SYMBOLS),
2264 JSVM_KEY_NUMBERS_TO_STRINGS,
2265 result);
2266 }
2267
2268 JSVM_Status JSVM_CDECL
OH_JSVM_GetAllPropertyNames(JSVM_Env env,JSVM_Value object,JSVM_KeyCollectionMode keyMode,JSVM_KeyFilter keyFilter,JSVM_KeyConversion keyConversion,JSVM_Value * result)2269 OH_JSVM_GetAllPropertyNames(JSVM_Env env,
2270 JSVM_Value object,
2271 JSVM_KeyCollectionMode keyMode,
2272 JSVM_KeyFilter keyFilter,
2273 JSVM_KeyConversion keyConversion,
2274 JSVM_Value* result) {
2275 JSVM_PREAMBLE(env);
2276 CHECK_ARG(env, result);
2277
2278 v8::Local<v8::Context> context = env->context();
2279 v8::Local<v8::Object> obj;
2280 CHECK_TO_OBJECT(env, context, obj, object);
2281
2282 v8::PropertyFilter filter = v8::PropertyFilter::ALL_PROPERTIES;
2283 if (keyFilter & JSVM_KEY_WRITABLE) {
2284 filter = static_cast<v8::PropertyFilter>(filter |
2285 v8::PropertyFilter::ONLY_WRITABLE);
2286 }
2287 if (keyFilter & JSVM_KEY_ENUMERABLE) {
2288 filter = static_cast<v8::PropertyFilter>(
2289 filter | v8::PropertyFilter::ONLY_ENUMERABLE);
2290 }
2291 if (keyFilter & JSVM_KEY_CONFIGURABLE) {
2292 filter = static_cast<v8::PropertyFilter>(
2293 filter | v8::PropertyFilter::ONLY_CONFIGURABLE);
2294 }
2295 if (keyFilter & JSVM_KEY_SKIP_STRINGS) {
2296 filter = static_cast<v8::PropertyFilter>(filter |
2297 v8::PropertyFilter::SKIP_STRINGS);
2298 }
2299 if (keyFilter & JSVM_KEY_SKIP_SYMBOLS) {
2300 filter = static_cast<v8::PropertyFilter>(filter |
2301 v8::PropertyFilter::SKIP_SYMBOLS);
2302 }
2303 v8::KeyCollectionMode collection_mode;
2304 v8::KeyConversionMode conversion_mode;
2305
2306 switch (keyMode) {
2307 case JSVM_KEY_INCLUDE_PROTOTYPES:
2308 collection_mode = v8::KeyCollectionMode::kIncludePrototypes;
2309 break;
2310 case JSVM_KEY_OWN_ONLY:
2311 collection_mode = v8::KeyCollectionMode::kOwnOnly;
2312 break;
2313 default:
2314 return jsvm_set_last_error(env, JSVM_INVALID_ARG);
2315 }
2316
2317 switch (keyConversion) {
2318 case JSVM_KEY_KEEP_NUMBERS:
2319 conversion_mode = v8::KeyConversionMode::kKeepNumbers;
2320 break;
2321 case JSVM_KEY_NUMBERS_TO_STRINGS:
2322 conversion_mode = v8::KeyConversionMode::kConvertToString;
2323 break;
2324 default:
2325 return jsvm_set_last_error(env, JSVM_INVALID_ARG);
2326 }
2327
2328 v8::MaybeLocal<v8::Array> maybe_all_propertynames =
2329 obj->GetPropertyNames(context,
2330 collection_mode,
2331 filter,
2332 v8::IndexFilter::kIncludeIndices,
2333 conversion_mode);
2334
2335 CHECK_MAYBE_EMPTY_WITH_PREAMBLE(
2336 env, maybe_all_propertynames, JSVM_GENERIC_FAILURE);
2337
2338 *result =
2339 v8impl::JsValueFromV8LocalValue(maybe_all_propertynames.ToLocalChecked());
2340 return GET_RETURN_STATUS(env);
2341 }
2342
OH_JSVM_SetProperty(JSVM_Env env,JSVM_Value object,JSVM_Value key,JSVM_Value value)2343 JSVM_Status JSVM_CDECL OH_JSVM_SetProperty(JSVM_Env env,
2344 JSVM_Value object,
2345 JSVM_Value key,
2346 JSVM_Value value) {
2347 JSVM_PREAMBLE(env);
2348 CHECK_ARG(env, key);
2349 CHECK_ARG(env, value);
2350
2351 v8::Local<v8::Context> context = env->context();
2352 v8::Local<v8::Object> obj;
2353
2354 CHECK_TO_OBJECT(env, context, obj, object);
2355
2356 v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
2357 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2358
2359 v8::Maybe<bool> set_maybe = obj->Set(context, k, val);
2360
2361 RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), JSVM_GENERIC_FAILURE);
2362 return GET_RETURN_STATUS(env);
2363 }
2364
OH_JSVM_HasProperty(JSVM_Env env,JSVM_Value object,JSVM_Value key,bool * result)2365 JSVM_Status JSVM_CDECL OH_JSVM_HasProperty(JSVM_Env env,
2366 JSVM_Value object,
2367 JSVM_Value key,
2368 bool* result) {
2369 JSVM_PREAMBLE(env);
2370 CHECK_ARG(env, result);
2371 CHECK_ARG(env, key);
2372
2373 v8::Local<v8::Context> context = env->context();
2374 v8::Local<v8::Object> obj;
2375
2376 CHECK_TO_OBJECT(env, context, obj, object);
2377
2378 v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
2379 v8::Maybe<bool> has_maybe = obj->Has(context, k);
2380
2381 CHECK_MAYBE_NOTHING(env, has_maybe, JSVM_GENERIC_FAILURE);
2382
2383 *result = has_maybe.FromMaybe(false);
2384 return GET_RETURN_STATUS(env);
2385 }
2386
OH_JSVM_GetProperty(JSVM_Env env,JSVM_Value object,JSVM_Value key,JSVM_Value * result)2387 JSVM_Status JSVM_CDECL OH_JSVM_GetProperty(JSVM_Env env,
2388 JSVM_Value object,
2389 JSVM_Value key,
2390 JSVM_Value* result) {
2391 JSVM_PREAMBLE(env);
2392 CHECK_ARG(env, key);
2393 CHECK_ARG(env, result);
2394
2395 v8::Local<v8::Context> context = env->context();
2396 v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
2397 v8::Local<v8::Object> obj;
2398
2399 CHECK_TO_OBJECT(env, context, obj, object);
2400
2401 auto get_maybe = obj->Get(context, k);
2402
2403 CHECK_MAYBE_EMPTY(env, get_maybe, JSVM_GENERIC_FAILURE);
2404
2405 v8::Local<v8::Value> val = get_maybe.ToLocalChecked();
2406 *result = v8impl::JsValueFromV8LocalValue(val);
2407 return GET_RETURN_STATUS(env);
2408 }
2409
OH_JSVM_DeleteProperty(JSVM_Env env,JSVM_Value object,JSVM_Value key,bool * result)2410 JSVM_Status JSVM_CDECL OH_JSVM_DeleteProperty(JSVM_Env env,
2411 JSVM_Value object,
2412 JSVM_Value key,
2413 bool* result) {
2414 JSVM_PREAMBLE(env);
2415 CHECK_ARG(env, key);
2416
2417 v8::Local<v8::Context> context = env->context();
2418 v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
2419 v8::Local<v8::Object> obj;
2420
2421 CHECK_TO_OBJECT(env, context, obj, object);
2422 v8::Maybe<bool> delete_maybe = obj->Delete(context, k);
2423 CHECK_MAYBE_NOTHING(env, delete_maybe, JSVM_GENERIC_FAILURE);
2424
2425 if (result != nullptr) *result = delete_maybe.FromMaybe(false);
2426
2427 return GET_RETURN_STATUS(env);
2428 }
2429
OH_JSVM_HasOwnProperty(JSVM_Env env,JSVM_Value object,JSVM_Value key,bool * result)2430 JSVM_Status JSVM_CDECL OH_JSVM_HasOwnProperty(JSVM_Env env,
2431 JSVM_Value object,
2432 JSVM_Value key,
2433 bool* result) {
2434 JSVM_PREAMBLE(env);
2435 CHECK_ARG(env, key);
2436 CHECK_ARG(env, result);
2437
2438 v8::Local<v8::Context> context = env->context();
2439 v8::Local<v8::Object> obj;
2440
2441 CHECK_TO_OBJECT(env, context, obj, object);
2442 v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key);
2443 RETURN_STATUS_IF_FALSE(env, k->IsName(), JSVM_NAME_EXPECTED);
2444 v8::Maybe<bool> has_maybe = obj->HasOwnProperty(context, k.As<v8::Name>());
2445 CHECK_MAYBE_NOTHING(env, has_maybe, JSVM_GENERIC_FAILURE);
2446 *result = has_maybe.FromMaybe(false);
2447
2448 return GET_RETURN_STATUS(env);
2449 }
2450
OH_JSVM_SetNamedProperty(JSVM_Env env,JSVM_Value object,const char * utf8name,JSVM_Value value)2451 JSVM_Status JSVM_CDECL OH_JSVM_SetNamedProperty(JSVM_Env env,
2452 JSVM_Value object,
2453 const char* utf8name,
2454 JSVM_Value value) {
2455 JSVM_PREAMBLE(env);
2456 CHECK_ARG(env, value);
2457
2458 v8::Local<v8::Context> context = env->context();
2459 v8::Local<v8::Object> obj;
2460
2461 CHECK_TO_OBJECT(env, context, obj, object);
2462
2463 v8::Local<v8::Name> key;
2464 CHECK_NEW_FROM_UTF8(env, key, utf8name);
2465
2466 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2467
2468 v8::Maybe<bool> set_maybe = obj->Set(context, key, val);
2469
2470 RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), JSVM_GENERIC_FAILURE);
2471 return GET_RETURN_STATUS(env);
2472 }
2473
OH_JSVM_HasNamedProperty(JSVM_Env env,JSVM_Value object,const char * utf8name,bool * result)2474 JSVM_Status JSVM_CDECL OH_JSVM_HasNamedProperty(JSVM_Env env,
2475 JSVM_Value object,
2476 const char* utf8name,
2477 bool* result) {
2478 JSVM_PREAMBLE(env);
2479 CHECK_ARG(env, result);
2480
2481 v8::Local<v8::Context> context = env->context();
2482 v8::Local<v8::Object> obj;
2483
2484 CHECK_TO_OBJECT(env, context, obj, object);
2485
2486 v8::Local<v8::Name> key;
2487 CHECK_NEW_FROM_UTF8(env, key, utf8name);
2488
2489 v8::Maybe<bool> has_maybe = obj->Has(context, key);
2490
2491 CHECK_MAYBE_NOTHING(env, has_maybe, JSVM_GENERIC_FAILURE);
2492
2493 *result = has_maybe.FromMaybe(false);
2494 return GET_RETURN_STATUS(env);
2495 }
2496
OH_JSVM_GetNamedProperty(JSVM_Env env,JSVM_Value object,const char * utf8name,JSVM_Value * result)2497 JSVM_Status JSVM_CDECL OH_JSVM_GetNamedProperty(JSVM_Env env,
2498 JSVM_Value object,
2499 const char* utf8name,
2500 JSVM_Value* result) {
2501 JSVM_PREAMBLE(env);
2502 CHECK_ARG(env, result);
2503
2504 v8::Local<v8::Context> context = env->context();
2505
2506 v8::Local<v8::Name> key;
2507 CHECK_NEW_FROM_UTF8(env, key, utf8name);
2508
2509 v8::Local<v8::Object> obj;
2510
2511 CHECK_TO_OBJECT(env, context, obj, object);
2512
2513 auto get_maybe = obj->Get(context, key);
2514
2515 CHECK_MAYBE_EMPTY(env, get_maybe, JSVM_GENERIC_FAILURE);
2516
2517 v8::Local<v8::Value> val = get_maybe.ToLocalChecked();
2518 *result = v8impl::JsValueFromV8LocalValue(val);
2519 return GET_RETURN_STATUS(env);
2520 }
2521
OH_JSVM_SetElement(JSVM_Env env,JSVM_Value object,uint32_t index,JSVM_Value value)2522 JSVM_Status JSVM_CDECL OH_JSVM_SetElement(JSVM_Env env,
2523 JSVM_Value object,
2524 uint32_t index,
2525 JSVM_Value value) {
2526 JSVM_PREAMBLE(env);
2527 CHECK_ARG(env, value);
2528
2529 v8::Local<v8::Context> context = env->context();
2530 v8::Local<v8::Object> obj;
2531
2532 CHECK_TO_OBJECT(env, context, obj, object);
2533
2534 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2535 auto set_maybe = obj->Set(context, index, val);
2536
2537 RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), JSVM_GENERIC_FAILURE);
2538
2539 return GET_RETURN_STATUS(env);
2540 }
2541
OH_JSVM_HasElement(JSVM_Env env,JSVM_Value object,uint32_t index,bool * result)2542 JSVM_Status JSVM_CDECL OH_JSVM_HasElement(JSVM_Env env,
2543 JSVM_Value object,
2544 uint32_t index,
2545 bool* result) {
2546 JSVM_PREAMBLE(env);
2547 CHECK_ARG(env, result);
2548
2549 v8::Local<v8::Context> context = env->context();
2550 v8::Local<v8::Object> obj;
2551
2552 CHECK_TO_OBJECT(env, context, obj, object);
2553
2554 v8::Maybe<bool> has_maybe = obj->Has(context, index);
2555
2556 CHECK_MAYBE_NOTHING(env, has_maybe, JSVM_GENERIC_FAILURE);
2557
2558 *result = has_maybe.FromMaybe(false);
2559 return GET_RETURN_STATUS(env);
2560 }
2561
OH_JSVM_GetElement(JSVM_Env env,JSVM_Value object,uint32_t index,JSVM_Value * result)2562 JSVM_Status JSVM_CDECL OH_JSVM_GetElement(JSVM_Env env,
2563 JSVM_Value object,
2564 uint32_t index,
2565 JSVM_Value* result) {
2566 JSVM_PREAMBLE(env);
2567 CHECK_ARG(env, result);
2568
2569 v8::Local<v8::Context> context = env->context();
2570 v8::Local<v8::Object> obj;
2571
2572 CHECK_TO_OBJECT(env, context, obj, object);
2573
2574 auto get_maybe = obj->Get(context, index);
2575
2576 CHECK_MAYBE_EMPTY(env, get_maybe, JSVM_GENERIC_FAILURE);
2577
2578 *result = v8impl::JsValueFromV8LocalValue(get_maybe.ToLocalChecked());
2579 return GET_RETURN_STATUS(env);
2580 }
2581
OH_JSVM_DeleteElement(JSVM_Env env,JSVM_Value object,uint32_t index,bool * result)2582 JSVM_Status JSVM_CDECL OH_JSVM_DeleteElement(JSVM_Env env,
2583 JSVM_Value object,
2584 uint32_t index,
2585 bool* result) {
2586 JSVM_PREAMBLE(env);
2587
2588 v8::Local<v8::Context> context = env->context();
2589 v8::Local<v8::Object> obj;
2590
2591 CHECK_TO_OBJECT(env, context, obj, object);
2592 v8::Maybe<bool> delete_maybe = obj->Delete(context, index);
2593 CHECK_MAYBE_NOTHING(env, delete_maybe, JSVM_GENERIC_FAILURE);
2594
2595 if (result != nullptr) *result = delete_maybe.FromMaybe(false);
2596
2597 return GET_RETURN_STATUS(env);
2598 }
2599
2600 JSVM_Status JSVM_CDECL
OH_JSVM_DefineProperties(JSVM_Env env,JSVM_Value object,size_t propertyCount,const JSVM_PropertyDescriptor * properties)2601 OH_JSVM_DefineProperties(JSVM_Env env,
2602 JSVM_Value object,
2603 size_t propertyCount,
2604 const JSVM_PropertyDescriptor* properties) {
2605 JSVM_PREAMBLE(env);
2606 if (propertyCount > 0) {
2607 CHECK_ARG(env, properties);
2608 }
2609
2610 v8::Local<v8::Context> context = env->context();
2611
2612 v8::Local<v8::Object> obj;
2613 CHECK_TO_OBJECT(env, context, obj, object);
2614
2615 for (size_t i = 0; i < propertyCount; i++) {
2616 const JSVM_PropertyDescriptor* p = &properties[i];
2617
2618 v8::Local<v8::Name> property_name;
2619 STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name));
2620
2621 if (p->getter != nullptr || p->setter != nullptr) {
2622 v8::Local<v8::Function> local_getter;
2623 v8::Local<v8::Function> local_setter;
2624
2625 if (p->getter != nullptr) {
2626 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(
2627 env, p->getter, &local_getter));
2628 }
2629 if (p->setter != nullptr) {
2630 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(
2631 env, p->setter, &local_setter));
2632 }
2633
2634 v8::PropertyDescriptor descriptor(local_getter, local_setter);
2635 descriptor.set_enumerable((p->attributes & JSVM_ENUMERABLE) != 0);
2636 descriptor.set_configurable((p->attributes & JSVM_CONFIGURABLE) != 0);
2637
2638 auto define_maybe =
2639 obj->DefineProperty(context, property_name, descriptor);
2640
2641 if (!define_maybe.FromMaybe(false)) {
2642 return jsvm_set_last_error(env, JSVM_INVALID_ARG);
2643 }
2644 } else if (p->method != nullptr) {
2645 v8::Local<v8::Function> method;
2646 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(
2647 env, p->method, &method));
2648 v8::PropertyDescriptor descriptor(method,
2649 (p->attributes & JSVM_WRITABLE) != 0);
2650 descriptor.set_enumerable((p->attributes & JSVM_ENUMERABLE) != 0);
2651 descriptor.set_configurable((p->attributes & JSVM_CONFIGURABLE) != 0);
2652
2653 auto define_maybe =
2654 obj->DefineProperty(context, property_name, descriptor);
2655
2656 if (!define_maybe.FromMaybe(false)) {
2657 return jsvm_set_last_error(env, JSVM_GENERIC_FAILURE);
2658 }
2659 } else {
2660 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value);
2661 bool defined_successfully = false;
2662
2663 if ((p->attributes & JSVM_ENUMERABLE) &&
2664 (p->attributes & JSVM_WRITABLE) &&
2665 (p->attributes & JSVM_CONFIGURABLE)) {
2666 // Use a fast path for this type of data property.
2667 auto define_maybe =
2668 obj->CreateDataProperty(context, property_name, value);
2669 defined_successfully = define_maybe.FromMaybe(false);
2670 } else {
2671 v8::PropertyDescriptor descriptor(value,
2672 (p->attributes & JSVM_WRITABLE) != 0);
2673 descriptor.set_enumerable((p->attributes & JSVM_ENUMERABLE) != 0);
2674 descriptor.set_configurable((p->attributes & JSVM_CONFIGURABLE) != 0);
2675
2676 auto define_maybe =
2677 obj->DefineProperty(context, property_name, descriptor);
2678 defined_successfully = define_maybe.FromMaybe(false);
2679 }
2680
2681 if (!defined_successfully) {
2682 return jsvm_set_last_error(env, JSVM_INVALID_ARG);
2683 }
2684 }
2685 }
2686
2687 return GET_RETURN_STATUS(env);
2688 }
2689
OH_JSVM_ObjectFreeze(JSVM_Env env,JSVM_Value object)2690 JSVM_Status JSVM_CDECL OH_JSVM_ObjectFreeze(JSVM_Env env, JSVM_Value object) {
2691 JSVM_PREAMBLE(env);
2692
2693 v8::Local<v8::Context> context = env->context();
2694 v8::Local<v8::Object> obj;
2695
2696 CHECK_TO_OBJECT(env, context, obj, object);
2697
2698 v8::Maybe<bool> set_frozen =
2699 obj->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen);
2700
2701 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(
2702 env, set_frozen.FromMaybe(false), JSVM_GENERIC_FAILURE);
2703
2704 return GET_RETURN_STATUS(env);
2705 }
2706
OH_JSVM_ObjectSeal(JSVM_Env env,JSVM_Value object)2707 JSVM_Status JSVM_CDECL OH_JSVM_ObjectSeal(JSVM_Env env, JSVM_Value object) {
2708 JSVM_PREAMBLE(env);
2709
2710 v8::Local<v8::Context> context = env->context();
2711 v8::Local<v8::Object> obj;
2712
2713 CHECK_TO_OBJECT(env, context, obj, object);
2714
2715 v8::Maybe<bool> set_sealed =
2716 obj->SetIntegrityLevel(context, v8::IntegrityLevel::kSealed);
2717
2718 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(
2719 env, set_sealed.FromMaybe(false), JSVM_GENERIC_FAILURE);
2720
2721 return GET_RETURN_STATUS(env);
2722 }
2723
OH_JSVM_IsArray(JSVM_Env env,JSVM_Value value,bool * result)2724 JSVM_Status JSVM_CDECL OH_JSVM_IsArray(JSVM_Env env,
2725 JSVM_Value value,
2726 bool* result) {
2727 CHECK_ENV(env);
2728 CHECK_ARG(env, value);
2729 CHECK_ARG(env, result);
2730
2731 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2732
2733 *result = val->IsArray();
2734 return jsvm_clear_last_error(env);
2735 }
2736
OH_JSVM_IsRegExp(JSVM_Env env,JSVM_Value value,bool * result)2737 JSVM_Status JSVM_CDECL OH_JSVM_IsRegExp(JSVM_Env env,
2738 JSVM_Value value,
2739 bool* result) {
2740 CHECK_ENV(env);
2741 CHECK_ARG(env, value);
2742 CHECK_ARG(env, result);
2743
2744 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2745
2746 *result = val->IsRegExp();
2747 return jsvm_clear_last_error(env);
2748 }
2749
OH_JSVM_GetArrayLength(JSVM_Env env,JSVM_Value value,uint32_t * result)2750 JSVM_Status JSVM_CDECL OH_JSVM_GetArrayLength(JSVM_Env env,
2751 JSVM_Value value,
2752 uint32_t* result) {
2753 JSVM_PREAMBLE(env);
2754 CHECK_ARG(env, value);
2755 CHECK_ARG(env, result);
2756
2757 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
2758 RETURN_STATUS_IF_FALSE(env, val->IsArray(), JSVM_ARRAY_EXPECTED);
2759
2760 v8::Local<v8::Array> arr = val.As<v8::Array>();
2761 *result = arr->Length();
2762
2763 return GET_RETURN_STATUS(env);
2764 }
2765
OH_JSVM_StrictEquals(JSVM_Env env,JSVM_Value lhs,JSVM_Value rhs,bool * result)2766 JSVM_Status JSVM_CDECL OH_JSVM_StrictEquals(JSVM_Env env,
2767 JSVM_Value lhs,
2768 JSVM_Value rhs,
2769 bool* result) {
2770 JSVM_PREAMBLE(env);
2771 CHECK_ARG(env, lhs);
2772 CHECK_ARG(env, rhs);
2773 CHECK_ARG(env, result);
2774
2775 v8::Local<v8::Value> a = v8impl::V8LocalValueFromJsValue(lhs);
2776 v8::Local<v8::Value> b = v8impl::V8LocalValueFromJsValue(rhs);
2777
2778 *result = a->StrictEquals(b);
2779 return GET_RETURN_STATUS(env);
2780 }
2781
OH_JSVM_Equals(JSVM_Env env,JSVM_Value lhs,JSVM_Value rhs,bool * result)2782 JSVM_Status JSVM_CDECL OH_JSVM_Equals(JSVM_Env env,
2783 JSVM_Value lhs,
2784 JSVM_Value rhs,
2785 bool* result)
2786 {
2787 JSVM_PREAMBLE(env);
2788 CHECK_ARG(env, lhs);
2789 CHECK_ARG(env, rhs);
2790 CHECK_ARG(env, result);
2791
2792 v8::Local<v8::Value> a = v8impl::V8LocalValueFromJsValue(lhs);
2793 v8::Local<v8::Value> b = v8impl::V8LocalValueFromJsValue(rhs);
2794 v8::Local<v8::Context> context = env->context();
2795
2796 *result = a->Equals(context, b).FromJust();
2797 return GET_RETURN_STATUS(env);
2798 }
2799
OH_JSVM_GetPrototype(JSVM_Env env,JSVM_Value object,JSVM_Value * result)2800 JSVM_Status JSVM_CDECL OH_JSVM_GetPrototype(JSVM_Env env,
2801 JSVM_Value object,
2802 JSVM_Value* result) {
2803 JSVM_PREAMBLE(env);
2804 CHECK_ARG(env, result);
2805
2806 v8::Local<v8::Context> context = env->context();
2807
2808 v8::Local<v8::Object> obj;
2809 CHECK_TO_OBJECT(env, context, obj, object);
2810
2811 v8::Local<v8::Value> val = obj->GetPrototype();
2812 *result = v8impl::JsValueFromV8LocalValue(val);
2813 return GET_RETURN_STATUS(env);
2814 }
2815
OH_JSVM_CreateObject(JSVM_Env env,JSVM_Value * result)2816 JSVM_Status JSVM_CDECL OH_JSVM_CreateObject(JSVM_Env env, JSVM_Value* result) {
2817 CHECK_ENV(env);
2818 CHECK_ARG(env, result);
2819
2820 *result = v8impl::JsValueFromV8LocalValue(v8::Object::New(env->isolate));
2821
2822 return jsvm_clear_last_error(env);
2823 }
2824
OH_JSVM_CreateArray(JSVM_Env env,JSVM_Value * result)2825 JSVM_Status JSVM_CDECL OH_JSVM_CreateArray(JSVM_Env env, JSVM_Value* result) {
2826 CHECK_ENV(env);
2827 CHECK_ARG(env, result);
2828
2829 *result = v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate));
2830
2831 return jsvm_clear_last_error(env);
2832 }
2833
OH_JSVM_CreateArrayWithLength(JSVM_Env env,size_t length,JSVM_Value * result)2834 JSVM_Status JSVM_CDECL OH_JSVM_CreateArrayWithLength(JSVM_Env env,
2835 size_t length,
2836 JSVM_Value* result) {
2837 CHECK_ENV(env);
2838 CHECK_ARG(env, result);
2839
2840 *result =
2841 v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate, length));
2842
2843 return jsvm_clear_last_error(env);
2844 }
2845
OH_JSVM_CreateStringLatin1(JSVM_Env env,const char * str,size_t length,JSVM_Value * result)2846 JSVM_Status JSVM_CDECL OH_JSVM_CreateStringLatin1(JSVM_Env env,
2847 const char* str,
2848 size_t length,
2849 JSVM_Value* result) {
2850 return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
2851 return v8::String::NewFromOneByte(isolate,
2852 reinterpret_cast<const uint8_t*>(str),
2853 v8::NewStringType::kNormal,
2854 length);
2855 });
2856 }
2857
OH_JSVM_CreateStringUtf8(JSVM_Env env,const char * str,size_t length,JSVM_Value * result)2858 JSVM_Status JSVM_CDECL OH_JSVM_CreateStringUtf8(JSVM_Env env,
2859 const char* str,
2860 size_t length,
2861 JSVM_Value* result) {
2862 return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
2863 return v8::String::NewFromUtf8(
2864 isolate, str, v8::NewStringType::kNormal, static_cast<int>(length));
2865 });
2866 }
2867
OH_JSVM_CreateStringUtf16(JSVM_Env env,const char16_t * str,size_t length,JSVM_Value * result)2868 JSVM_Status JSVM_CDECL OH_JSVM_CreateStringUtf16(JSVM_Env env,
2869 const char16_t* str,
2870 size_t length,
2871 JSVM_Value* result) {
2872 return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
2873 return v8::String::NewFromTwoByte(isolate,
2874 reinterpret_cast<const uint16_t*>(str),
2875 v8::NewStringType::kNormal,
2876 length);
2877 });
2878 }
2879
OH_JSVM_CreateDouble(JSVM_Env env,double value,JSVM_Value * result)2880 JSVM_Status JSVM_CDECL OH_JSVM_CreateDouble(JSVM_Env env,
2881 double value,
2882 JSVM_Value* result) {
2883 CHECK_ENV(env);
2884 CHECK_ARG(env, result);
2885
2886 *result =
2887 v8impl::JsValueFromV8LocalValue(v8::Number::New(env->isolate, value));
2888
2889 return jsvm_clear_last_error(env);
2890 }
2891
OH_JSVM_CreateInt32(JSVM_Env env,int32_t value,JSVM_Value * result)2892 JSVM_Status JSVM_CDECL OH_JSVM_CreateInt32(JSVM_Env env,
2893 int32_t value,
2894 JSVM_Value* result) {
2895 CHECK_ENV(env);
2896 CHECK_ARG(env, result);
2897
2898 *result =
2899 v8impl::JsValueFromV8LocalValue(v8::Integer::New(env->isolate, value));
2900
2901 return jsvm_clear_last_error(env);
2902 }
2903
OH_JSVM_CreateUint32(JSVM_Env env,uint32_t value,JSVM_Value * result)2904 JSVM_Status JSVM_CDECL OH_JSVM_CreateUint32(JSVM_Env env,
2905 uint32_t value,
2906 JSVM_Value* result) {
2907 CHECK_ENV(env);
2908 CHECK_ARG(env, result);
2909
2910 *result = v8impl::JsValueFromV8LocalValue(
2911 v8::Integer::NewFromUnsigned(env->isolate, value));
2912
2913 return jsvm_clear_last_error(env);
2914 }
2915
OH_JSVM_CreateInt64(JSVM_Env env,int64_t value,JSVM_Value * result)2916 JSVM_Status JSVM_CDECL OH_JSVM_CreateInt64(JSVM_Env env,
2917 int64_t value,
2918 JSVM_Value* result) {
2919 CHECK_ENV(env);
2920 CHECK_ARG(env, result);
2921
2922 *result = v8impl::JsValueFromV8LocalValue(
2923 v8::Number::New(env->isolate, static_cast<double>(value)));
2924
2925 return jsvm_clear_last_error(env);
2926 }
2927
OH_JSVM_CreateBigintInt64(JSVM_Env env,int64_t value,JSVM_Value * result)2928 JSVM_Status JSVM_CDECL OH_JSVM_CreateBigintInt64(JSVM_Env env,
2929 int64_t value,
2930 JSVM_Value* result) {
2931 CHECK_ENV(env);
2932 CHECK_ARG(env, result);
2933
2934 *result =
2935 v8impl::JsValueFromV8LocalValue(v8::BigInt::New(env->isolate, value));
2936
2937 return jsvm_clear_last_error(env);
2938 }
2939
OH_JSVM_CreateBigintUint64(JSVM_Env env,uint64_t value,JSVM_Value * result)2940 JSVM_Status JSVM_CDECL OH_JSVM_CreateBigintUint64(JSVM_Env env,
2941 uint64_t value,
2942 JSVM_Value* result) {
2943 CHECK_ENV(env);
2944 CHECK_ARG(env, result);
2945
2946 *result = v8impl::JsValueFromV8LocalValue(
2947 v8::BigInt::NewFromUnsigned(env->isolate, value));
2948
2949 return jsvm_clear_last_error(env);
2950 }
2951
OH_JSVM_CreateBigintWords(JSVM_Env env,int signBit,size_t wordCount,const uint64_t * words,JSVM_Value * result)2952 JSVM_Status JSVM_CDECL OH_JSVM_CreateBigintWords(JSVM_Env env,
2953 int signBit,
2954 size_t wordCount,
2955 const uint64_t* words,
2956 JSVM_Value* result) {
2957 JSVM_PREAMBLE(env);
2958 CHECK_ARG(env, words);
2959 CHECK_ARG(env, result);
2960
2961 v8::Local<v8::Context> context = env->context();
2962
2963 RETURN_STATUS_IF_FALSE(env, wordCount <= INT_MAX, JSVM_INVALID_ARG);
2964
2965 v8::MaybeLocal<v8::BigInt> b =
2966 v8::BigInt::NewFromWords(context, signBit, wordCount, words);
2967
2968 CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, b, JSVM_GENERIC_FAILURE);
2969
2970 *result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked());
2971 return GET_RETURN_STATUS(env);
2972 }
2973
OH_JSVM_GetBoolean(JSVM_Env env,bool value,JSVM_Value * result)2974 JSVM_Status JSVM_CDECL OH_JSVM_GetBoolean(JSVM_Env env,
2975 bool value,
2976 JSVM_Value* result) {
2977 CHECK_ENV(env);
2978 CHECK_ARG(env, result);
2979
2980 v8::Isolate* isolate = env->isolate;
2981
2982 if (value) {
2983 *result = v8impl::JsValueFromV8LocalValue(v8::True(isolate));
2984 } else {
2985 *result = v8impl::JsValueFromV8LocalValue(v8::False(isolate));
2986 }
2987
2988 return jsvm_clear_last_error(env);
2989 }
2990
OH_JSVM_CreateSymbol(JSVM_Env env,JSVM_Value description,JSVM_Value * result)2991 JSVM_Status JSVM_CDECL OH_JSVM_CreateSymbol(JSVM_Env env,
2992 JSVM_Value description,
2993 JSVM_Value* result) {
2994 CHECK_ENV(env);
2995 CHECK_ARG(env, result);
2996
2997 v8::Isolate* isolate = env->isolate;
2998
2999 if (description == nullptr) {
3000 *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate));
3001 } else {
3002 v8::Local<v8::Value> desc = v8impl::V8LocalValueFromJsValue(description);
3003 RETURN_STATUS_IF_FALSE(env, desc->IsString(), JSVM_STRING_EXPECTED);
3004
3005 *result = v8impl::JsValueFromV8LocalValue(
3006 v8::Symbol::New(isolate, desc.As<v8::String>()));
3007 }
3008
3009 return jsvm_clear_last_error(env);
3010 }
3011
OH_JSVM_SymbolFor(JSVM_Env env,const char * utf8description,size_t length,JSVM_Value * result)3012 JSVM_Status JSVM_CDECL OH_JSVM_SymbolFor(JSVM_Env env,
3013 const char* utf8description,
3014 size_t length,
3015 JSVM_Value* result) {
3016 CHECK_ENV(env);
3017 CHECK_ARG(env, result);
3018
3019 JSVM_Value js_description_string;
3020 STATUS_CALL(OH_JSVM_CreateStringUtf8(
3021 env, utf8description, length, &js_description_string));
3022 v8::Local<v8::String> description_string =
3023 v8impl::V8LocalValueFromJsValue(js_description_string).As<v8::String>();
3024
3025 *result = v8impl::JsValueFromV8LocalValue(
3026 v8::Symbol::For(env->isolate, description_string));
3027
3028 return jsvm_clear_last_error(env);
3029 }
3030
set_error_code(JSVM_Env env,v8::Local<v8::Value> error,JSVM_Value code,const char * code_cstring)3031 static inline JSVM_Status set_error_code(JSVM_Env env,
3032 v8::Local<v8::Value> error,
3033 JSVM_Value code,
3034 const char* code_cstring) {
3035 if ((code != nullptr) || (code_cstring != nullptr)) {
3036 v8::Local<v8::Context> context = env->context();
3037 v8::Local<v8::Object> err_object = error.As<v8::Object>();
3038
3039 v8::Local<v8::Value> code_value = v8impl::V8LocalValueFromJsValue(code);
3040 if (code != nullptr) {
3041 code_value = v8impl::V8LocalValueFromJsValue(code);
3042 RETURN_STATUS_IF_FALSE(env, code_value->IsString(), JSVM_STRING_EXPECTED);
3043 } else {
3044 CHECK_NEW_FROM_UTF8(env, code_value, code_cstring);
3045 }
3046
3047 v8::Local<v8::Name> code_key;
3048 CHECK_NEW_FROM_UTF8(env, code_key, "code");
3049
3050 v8::Maybe<bool> set_maybe = err_object->Set(context, code_key, code_value);
3051 RETURN_STATUS_IF_FALSE(
3052 env, set_maybe.FromMaybe(false), JSVM_GENERIC_FAILURE);
3053 }
3054 return JSVM_OK;
3055 }
3056
OH_JSVM_CreateError(JSVM_Env env,JSVM_Value code,JSVM_Value msg,JSVM_Value * result)3057 JSVM_Status JSVM_CDECL OH_JSVM_CreateError(JSVM_Env env,
3058 JSVM_Value code,
3059 JSVM_Value msg,
3060 JSVM_Value* result) {
3061 CHECK_ENV(env);
3062 CHECK_ARG(env, msg);
3063 CHECK_ARG(env, result);
3064
3065 v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
3066 RETURN_STATUS_IF_FALSE(env, message_value->IsString(), JSVM_STRING_EXPECTED);
3067
3068 v8::Local<v8::Value> error_obj =
3069 v8::Exception::Error(message_value.As<v8::String>());
3070 STATUS_CALL(set_error_code(env, error_obj, code, nullptr));
3071
3072 *result = v8impl::JsValueFromV8LocalValue(error_obj);
3073
3074 return jsvm_clear_last_error(env);
3075 }
3076
OH_JSVM_CreateTypeError(JSVM_Env env,JSVM_Value code,JSVM_Value msg,JSVM_Value * result)3077 JSVM_Status JSVM_CDECL OH_JSVM_CreateTypeError(JSVM_Env env,
3078 JSVM_Value code,
3079 JSVM_Value msg,
3080 JSVM_Value* result) {
3081 CHECK_ENV(env);
3082 CHECK_ARG(env, msg);
3083 CHECK_ARG(env, result);
3084
3085 v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
3086 RETURN_STATUS_IF_FALSE(env, message_value->IsString(), JSVM_STRING_EXPECTED);
3087
3088 v8::Local<v8::Value> error_obj =
3089 v8::Exception::TypeError(message_value.As<v8::String>());
3090 STATUS_CALL(set_error_code(env, error_obj, code, nullptr));
3091
3092 *result = v8impl::JsValueFromV8LocalValue(error_obj);
3093
3094 return jsvm_clear_last_error(env);
3095 }
3096
OH_JSVM_CreateRangeError(JSVM_Env env,JSVM_Value code,JSVM_Value msg,JSVM_Value * result)3097 JSVM_Status JSVM_CDECL OH_JSVM_CreateRangeError(JSVM_Env env,
3098 JSVM_Value code,
3099 JSVM_Value msg,
3100 JSVM_Value* result) {
3101 CHECK_ENV(env);
3102 CHECK_ARG(env, msg);
3103 CHECK_ARG(env, result);
3104
3105 v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
3106 RETURN_STATUS_IF_FALSE(env, message_value->IsString(), JSVM_STRING_EXPECTED);
3107
3108 v8::Local<v8::Value> error_obj =
3109 v8::Exception::RangeError(message_value.As<v8::String>());
3110 STATUS_CALL(set_error_code(env, error_obj, code, nullptr));
3111
3112 *result = v8impl::JsValueFromV8LocalValue(error_obj);
3113
3114 return jsvm_clear_last_error(env);
3115 }
3116
OH_JSVM_CreateSyntaxError(JSVM_Env env,JSVM_Value code,JSVM_Value msg,JSVM_Value * result)3117 JSVM_Status JSVM_CDECL OH_JSVM_CreateSyntaxError(JSVM_Env env,
3118 JSVM_Value code,
3119 JSVM_Value msg,
3120 JSVM_Value* result) {
3121 CHECK_ENV(env);
3122 CHECK_ARG(env, msg);
3123 CHECK_ARG(env, result);
3124
3125 v8::Local<v8::Value> message_value = v8impl::V8LocalValueFromJsValue(msg);
3126 RETURN_STATUS_IF_FALSE(env, message_value->IsString(), JSVM_STRING_EXPECTED);
3127
3128 v8::Local<v8::Value> error_obj =
3129 v8::Exception::SyntaxError(message_value.As<v8::String>());
3130 STATUS_CALL(set_error_code(env, error_obj, code, nullptr));
3131
3132 *result = v8impl::JsValueFromV8LocalValue(error_obj);
3133
3134 return jsvm_clear_last_error(env);
3135 }
3136
OH_JSVM_Typeof(JSVM_Env env,JSVM_Value value,JSVM_ValueType * result)3137 JSVM_Status JSVM_CDECL OH_JSVM_Typeof(JSVM_Env env,
3138 JSVM_Value value,
3139 JSVM_ValueType* result) {
3140 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3141 // JS exceptions.
3142 CHECK_ENV(env);
3143 CHECK_ARG(env, value);
3144 CHECK_ARG(env, result);
3145
3146 v8::Local<v8::Value> v = v8impl::V8LocalValueFromJsValue(value);
3147
3148 if (v->IsNumber()) {
3149 *result = JSVM_NUMBER;
3150 } else if (v->IsBigInt()) {
3151 *result = JSVM_BIGINT;
3152 } else if (v->IsString()) {
3153 *result = JSVM_STRING;
3154 } else if (v->IsFunction()) {
3155 // This test has to come before IsObject because IsFunction
3156 // implies IsObject
3157 *result = JSVM_FUNCTION;
3158 } else if (v->IsExternal()) {
3159 // This test has to come before IsObject because IsExternal
3160 // implies IsObject
3161 *result = JSVM_EXTERNAL;
3162 } else if (v->IsObject()) {
3163 *result = JSVM_OBJECT;
3164 } else if (v->IsBoolean()) {
3165 *result = JSVM_BOOLEAN;
3166 } else if (v->IsUndefined()) {
3167 *result = JSVM_UNDEFINED;
3168 } else if (v->IsSymbol()) {
3169 *result = JSVM_SYMBOL;
3170 } else if (v->IsNull()) {
3171 *result = JSVM_NULL;
3172 } else {
3173 // Should not get here unless V8 has added some new kind of value.
3174 return jsvm_set_last_error(env, JSVM_INVALID_ARG);
3175 }
3176
3177 return jsvm_clear_last_error(env);
3178 }
3179
OH_JSVM_GetUndefined(JSVM_Env env,JSVM_Value * result)3180 JSVM_Status JSVM_CDECL OH_JSVM_GetUndefined(JSVM_Env env, JSVM_Value* result) {
3181 CHECK_ENV(env);
3182 CHECK_ARG(env, result);
3183
3184 *result = v8impl::JsValueFromV8LocalValue(v8::Undefined(env->isolate));
3185
3186 return jsvm_clear_last_error(env);
3187 }
3188
OH_JSVM_GetNull(JSVM_Env env,JSVM_Value * result)3189 JSVM_Status JSVM_CDECL OH_JSVM_GetNull(JSVM_Env env, JSVM_Value* result) {
3190 CHECK_ENV(env);
3191 CHECK_ARG(env, result);
3192
3193 *result = v8impl::JsValueFromV8LocalValue(v8::Null(env->isolate));
3194
3195 return jsvm_clear_last_error(env);
3196 }
3197
3198 // Gets all callback info in a single call. (Ugly, but faster.)
OH_JSVM_GetCbInfo(JSVM_Env env,JSVM_CallbackInfo cbinfo,size_t * argc,JSVM_Value * argv,JSVM_Value * thisArg,void ** data)3199 JSVM_Status JSVM_CDECL OH_JSVM_GetCbInfo(
3200 JSVM_Env env, // [in] JSVM environment handle
3201 JSVM_CallbackInfo cbinfo, // [in] Opaque callback-info handle
3202 size_t* argc, // [in-out] Specifies the size of the provided argv array
3203 // and receives the actual count of args.
3204 JSVM_Value* argv, // [out] Array of values
3205 JSVM_Value* thisArg, // [out] Receives the JS 'this' arg for the call
3206 void** data) { // [out] Receives the data pointer for the callback.
3207 CHECK_ENV(env);
3208 CHECK_ARG(env, cbinfo);
3209
3210 v8impl::CallbackWrapper* info =
3211 reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
3212
3213 if (argv != nullptr) {
3214 CHECK_ARG(env, argc);
3215 info->Args(argv, *argc);
3216 }
3217 if (argc != nullptr) {
3218 *argc = info->ArgsLength();
3219 }
3220 if (thisArg != nullptr) {
3221 *thisArg = info->This();
3222 }
3223 if (data != nullptr) {
3224 *data = info->Data();
3225 }
3226
3227 return jsvm_clear_last_error(env);
3228 }
3229
OH_JSVM_GetNewTarget(JSVM_Env env,JSVM_CallbackInfo cbinfo,JSVM_Value * result)3230 JSVM_Status JSVM_CDECL OH_JSVM_GetNewTarget(JSVM_Env env,
3231 JSVM_CallbackInfo cbinfo,
3232 JSVM_Value* result) {
3233 CHECK_ENV(env);
3234 CHECK_ARG(env, cbinfo);
3235 CHECK_ARG(env, result);
3236
3237 v8impl::CallbackWrapper* info =
3238 reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
3239
3240 *result = info->GetNewTarget();
3241 return jsvm_clear_last_error(env);
3242 }
3243
OH_JSVM_CallFunction(JSVM_Env env,JSVM_Value recv,JSVM_Value func,size_t argc,const JSVM_Value * argv,JSVM_Value * result)3244 JSVM_Status JSVM_CDECL OH_JSVM_CallFunction(JSVM_Env env,
3245 JSVM_Value recv,
3246 JSVM_Value func,
3247 size_t argc,
3248 const JSVM_Value* argv,
3249 JSVM_Value* result) {
3250 JSVM_PREAMBLE(env);
3251 CHECK_ARG(env, recv);
3252 if (argc > 0) {
3253 CHECK_ARG(env, argv);
3254 }
3255
3256 v8::Local<v8::Context> context = env->context();
3257
3258 v8::Local<v8::Value> v8recv = v8impl::V8LocalValueFromJsValue(recv);
3259
3260 v8::Local<v8::Function> v8func;
3261 CHECK_TO_FUNCTION(env, v8func, func);
3262
3263 auto maybe = v8func->Call(
3264 context,
3265 v8recv,
3266 argc,
3267 reinterpret_cast<v8::Local<v8::Value>*>(const_cast<JSVM_Value*>(argv)));
3268
3269 if (try_catch.HasCaught()) {
3270 return jsvm_set_last_error(env, JSVM_PENDING_EXCEPTION);
3271 } else {
3272 if (result != nullptr) {
3273 CHECK_MAYBE_EMPTY(env, maybe, JSVM_GENERIC_FAILURE);
3274 *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
3275 }
3276 return jsvm_clear_last_error(env);
3277 }
3278 }
3279
OH_JSVM_GetGlobal(JSVM_Env env,JSVM_Value * result)3280 JSVM_Status JSVM_CDECL OH_JSVM_GetGlobal(JSVM_Env env, JSVM_Value* result) {
3281 CHECK_ENV(env);
3282 CHECK_ARG(env, result);
3283
3284 *result = v8impl::JsValueFromV8LocalValue(env->context()->Global());
3285
3286 return jsvm_clear_last_error(env);
3287 }
3288
OH_JSVM_Throw(JSVM_Env env,JSVM_Value error)3289 JSVM_Status JSVM_CDECL OH_JSVM_Throw(JSVM_Env env, JSVM_Value error) {
3290 JSVM_PREAMBLE(env);
3291 CHECK_ARG(env, error);
3292
3293 v8::Isolate* isolate = env->isolate;
3294
3295 isolate->ThrowException(v8impl::V8LocalValueFromJsValue(error));
3296 // any VM calls after this point and before returning
3297 // to the javascript invoker will fail
3298 return jsvm_clear_last_error(env);
3299 }
3300
OH_JSVM_ThrowError(JSVM_Env env,const char * code,const char * msg)3301 JSVM_Status JSVM_CDECL OH_JSVM_ThrowError(JSVM_Env env,
3302 const char* code,
3303 const char* msg) {
3304 JSVM_PREAMBLE(env);
3305
3306 v8::Isolate* isolate = env->isolate;
3307 v8::Local<v8::String> str;
3308 CHECK_NEW_FROM_UTF8(env, str, msg);
3309
3310 v8::Local<v8::Value> error_obj = v8::Exception::Error(str);
3311 STATUS_CALL(set_error_code(env, error_obj, nullptr, code));
3312
3313 isolate->ThrowException(error_obj);
3314 // any VM calls after this point and before returning
3315 // to the javascript invoker will fail
3316 return jsvm_clear_last_error(env);
3317 }
3318
OH_JSVM_ThrowTypeError(JSVM_Env env,const char * code,const char * msg)3319 JSVM_Status JSVM_CDECL OH_JSVM_ThrowTypeError(JSVM_Env env,
3320 const char* code,
3321 const char* msg) {
3322 JSVM_PREAMBLE(env);
3323
3324 v8::Isolate* isolate = env->isolate;
3325 v8::Local<v8::String> str;
3326 CHECK_NEW_FROM_UTF8(env, str, msg);
3327
3328 v8::Local<v8::Value> error_obj = v8::Exception::TypeError(str);
3329 STATUS_CALL(set_error_code(env, error_obj, nullptr, code));
3330
3331 isolate->ThrowException(error_obj);
3332 // any VM calls after this point and before returning
3333 // to the javascript invoker will fail
3334 return jsvm_clear_last_error(env);
3335 }
3336
OH_JSVM_ThrowRangeError(JSVM_Env env,const char * code,const char * msg)3337 JSVM_Status JSVM_CDECL OH_JSVM_ThrowRangeError(JSVM_Env env,
3338 const char* code,
3339 const char* msg) {
3340 JSVM_PREAMBLE(env);
3341
3342 v8::Isolate* isolate = env->isolate;
3343 v8::Local<v8::String> str;
3344 CHECK_NEW_FROM_UTF8(env, str, msg);
3345
3346 v8::Local<v8::Value> error_obj = v8::Exception::RangeError(str);
3347 STATUS_CALL(set_error_code(env, error_obj, nullptr, code));
3348
3349 isolate->ThrowException(error_obj);
3350 // any VM calls after this point and before returning
3351 // to the javascript invoker will fail
3352 return jsvm_clear_last_error(env);
3353 }
3354
OH_JSVM_ThrowSyntaxError(JSVM_Env env,const char * code,const char * msg)3355 JSVM_Status JSVM_CDECL OH_JSVM_ThrowSyntaxError(JSVM_Env env,
3356 const char* code,
3357 const char* msg) {
3358 JSVM_PREAMBLE(env);
3359
3360 v8::Isolate* isolate = env->isolate;
3361 v8::Local<v8::String> str;
3362 CHECK_NEW_FROM_UTF8(env, str, msg);
3363
3364 v8::Local<v8::Value> error_obj = v8::Exception::SyntaxError(str);
3365 STATUS_CALL(set_error_code(env, error_obj, nullptr, code));
3366
3367 isolate->ThrowException(error_obj);
3368 // any VM calls after this point and before returning
3369 // to the javascript invoker will fail
3370 return jsvm_clear_last_error(env);
3371 }
3372
OH_JSVM_IsError(JSVM_Env env,JSVM_Value value,bool * result)3373 JSVM_Status JSVM_CDECL OH_JSVM_IsError(JSVM_Env env,
3374 JSVM_Value value,
3375 bool* result) {
3376 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot
3377 // throw JS exceptions.
3378 CHECK_ENV(env);
3379 CHECK_ARG(env, value);
3380 CHECK_ARG(env, result);
3381
3382 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3383 *result = val->IsNativeError();
3384
3385 return jsvm_clear_last_error(env);
3386 }
3387
OH_JSVM_GetValueDouble(JSVM_Env env,JSVM_Value value,double * result)3388 JSVM_Status JSVM_CDECL OH_JSVM_GetValueDouble(JSVM_Env env,
3389 JSVM_Value value,
3390 double* result) {
3391 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3392 // JS exceptions.
3393 CHECK_ENV(env);
3394 CHECK_ARG(env, value);
3395 CHECK_ARG(env, result);
3396
3397 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3398 RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED);
3399
3400 *result = val.As<v8::Number>()->Value();
3401
3402 return jsvm_clear_last_error(env);
3403 }
3404
OH_JSVM_GetValueInt32(JSVM_Env env,JSVM_Value value,int32_t * result)3405 JSVM_Status JSVM_CDECL OH_JSVM_GetValueInt32(JSVM_Env env,
3406 JSVM_Value value,
3407 int32_t* result) {
3408 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3409 // JS exceptions.
3410 CHECK_ENV(env);
3411 CHECK_ARG(env, value);
3412 CHECK_ARG(env, result);
3413
3414 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3415
3416 if (val->IsInt32()) {
3417 *result = val.As<v8::Int32>()->Value();
3418 } else {
3419 RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED);
3420
3421 // Empty context: https://github.com/nodejs/node/issues/14379
3422 v8::Local<v8::Context> context;
3423 *result = val->Int32Value(context).FromJust();
3424 }
3425
3426 return jsvm_clear_last_error(env);
3427 }
3428
OH_JSVM_GetValueUint32(JSVM_Env env,JSVM_Value value,uint32_t * result)3429 JSVM_Status JSVM_CDECL OH_JSVM_GetValueUint32(JSVM_Env env,
3430 JSVM_Value value,
3431 uint32_t* result) {
3432 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3433 // JS exceptions.
3434 CHECK_ENV(env);
3435 CHECK_ARG(env, value);
3436 CHECK_ARG(env, result);
3437
3438 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3439
3440 if (val->IsUint32()) {
3441 *result = val.As<v8::Uint32>()->Value();
3442 } else {
3443 RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED);
3444
3445 // Empty context: https://github.com/nodejs/node/issues/14379
3446 v8::Local<v8::Context> context;
3447 *result = val->Uint32Value(context).FromJust();
3448 }
3449
3450 return jsvm_clear_last_error(env);
3451 }
3452
OH_JSVM_GetValueInt64(JSVM_Env env,JSVM_Value value,int64_t * result)3453 JSVM_Status JSVM_CDECL OH_JSVM_GetValueInt64(JSVM_Env env,
3454 JSVM_Value value,
3455 int64_t* result) {
3456 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3457 // JS exceptions.
3458 CHECK_ENV(env);
3459 CHECK_ARG(env, value);
3460 CHECK_ARG(env, result);
3461
3462 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3463
3464 // This is still a fast path very likely to be taken.
3465 if (val->IsInt32()) {
3466 *result = val.As<v8::Int32>()->Value();
3467 return jsvm_clear_last_error(env);
3468 }
3469
3470 RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED);
3471
3472 // v8::Value::IntegerValue() converts NaN, +Inf, and -Inf to INT64_MIN,
3473 // inconsistent with v8::Value::Int32Value() which converts those values to 0.
3474 // Special-case all non-finite values to match that behavior.
3475 double doubleValue = val.As<v8::Number>()->Value();
3476 if (std::isfinite(doubleValue)) {
3477 // Empty context: https://github.com/nodejs/node/issues/14379
3478 v8::Local<v8::Context> context;
3479 *result = val->IntegerValue(context).FromJust();
3480 } else {
3481 *result = 0;
3482 }
3483
3484 return jsvm_clear_last_error(env);
3485 }
3486
OH_JSVM_GetValueBigintInt64(JSVM_Env env,JSVM_Value value,int64_t * result,bool * lossless)3487 JSVM_Status JSVM_CDECL OH_JSVM_GetValueBigintInt64(JSVM_Env env,
3488 JSVM_Value value,
3489 int64_t* result,
3490 bool* lossless) {
3491 CHECK_ENV(env);
3492 CHECK_ARG(env, value);
3493 CHECK_ARG(env, result);
3494 CHECK_ARG(env, lossless);
3495
3496 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3497
3498 RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), JSVM_BIGINT_EXPECTED);
3499
3500 *result = val.As<v8::BigInt>()->Int64Value(lossless);
3501
3502 return jsvm_clear_last_error(env);
3503 }
3504
OH_JSVM_GetValueBigintUint64(JSVM_Env env,JSVM_Value value,uint64_t * result,bool * lossless)3505 JSVM_Status JSVM_CDECL OH_JSVM_GetValueBigintUint64(JSVM_Env env,
3506 JSVM_Value value,
3507 uint64_t* result,
3508 bool* lossless) {
3509 CHECK_ENV(env);
3510 CHECK_ARG(env, value);
3511 CHECK_ARG(env, result);
3512 CHECK_ARG(env, lossless);
3513
3514 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3515
3516 RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), JSVM_BIGINT_EXPECTED);
3517
3518 *result = val.As<v8::BigInt>()->Uint64Value(lossless);
3519
3520 return jsvm_clear_last_error(env);
3521 }
3522
OH_JSVM_GetValueBigintWords(JSVM_Env env,JSVM_Value value,int * signBit,size_t * wordCount,uint64_t * words)3523 JSVM_Status JSVM_CDECL OH_JSVM_GetValueBigintWords(JSVM_Env env,
3524 JSVM_Value value,
3525 int* signBit,
3526 size_t* wordCount,
3527 uint64_t* words) {
3528 CHECK_ENV(env);
3529 CHECK_ARG(env, value);
3530 CHECK_ARG(env, wordCount);
3531
3532 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3533
3534 RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), JSVM_BIGINT_EXPECTED);
3535
3536 v8::Local<v8::BigInt> big = val.As<v8::BigInt>();
3537
3538 int word_count_int = *wordCount;
3539
3540 if (signBit == nullptr && words == nullptr) {
3541 word_count_int = big->WordCount();
3542 } else {
3543 CHECK_ARG(env, signBit);
3544 CHECK_ARG(env, words);
3545 big->ToWordsArray(signBit, &word_count_int, words);
3546 }
3547
3548 *wordCount = word_count_int;
3549
3550 return jsvm_clear_last_error(env);
3551 }
3552
OH_JSVM_GetValueBool(JSVM_Env env,JSVM_Value value,bool * result)3553 JSVM_Status JSVM_CDECL OH_JSVM_GetValueBool(JSVM_Env env,
3554 JSVM_Value value,
3555 bool* result) {
3556 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3557 // JS exceptions.
3558 CHECK_ENV(env);
3559 CHECK_ARG(env, value);
3560 CHECK_ARG(env, result);
3561
3562 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3563 RETURN_STATUS_IF_FALSE(env, val->IsBoolean(), JSVM_BOOLEAN_EXPECTED);
3564
3565 *result = val.As<v8::Boolean>()->Value();
3566
3567 return jsvm_clear_last_error(env);
3568 }
3569
3570 // Copies a JavaScript string into a LATIN-1 string buffer. The result is the
3571 // number of bytes (excluding the null terminator) copied into buf.
3572 // A sufficient buffer size should be greater than the length of string,
3573 // reserving space for null terminator.
3574 // If bufsize is insufficient, the string will be truncated and null terminated.
3575 // If buf is NULL, this method returns the length of the string (in bytes)
3576 // via the result parameter.
3577 // The result argument is optional unless buf is NULL.
OH_JSVM_GetValueStringLatin1(JSVM_Env env,JSVM_Value value,char * buf,size_t bufsize,size_t * result)3578 JSVM_Status JSVM_CDECL OH_JSVM_GetValueStringLatin1(
3579 JSVM_Env env, JSVM_Value value, char* buf, size_t bufsize, size_t* result) {
3580 CHECK_ENV(env);
3581 CHECK_ARG(env, value);
3582
3583 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3584 RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED);
3585
3586 if (!buf) {
3587 CHECK_ARG(env, result);
3588 *result = val.As<v8::String>()->Length();
3589 } else if (bufsize != 0) {
3590 int copied =
3591 val.As<v8::String>()->WriteOneByte(env->isolate,
3592 reinterpret_cast<uint8_t*>(buf),
3593 0,
3594 bufsize - 1,
3595 v8::String::NO_NULL_TERMINATION);
3596
3597 buf[copied] = '\0';
3598 if (result != nullptr) {
3599 *result = copied;
3600 }
3601 } else if (result != nullptr) {
3602 *result = 0;
3603 }
3604
3605 return jsvm_clear_last_error(env);
3606 }
3607
3608 // Copies a JavaScript string into a UTF-8 string buffer. The result is the
3609 // number of bytes (excluding the null terminator) copied into buf.
3610 // A sufficient buffer size should be greater than the length of string,
3611 // reserving space for null terminator.
3612 // If bufsize is insufficient, the string will be truncated and null terminated.
3613 // If buf is NULL, this method returns the length of the string (in bytes)
3614 // via the result parameter.
3615 // The result argument is optional unless buf is NULL.
OH_JSVM_GetValueStringUtf8(JSVM_Env env,JSVM_Value value,char * buf,size_t bufsize,size_t * result)3616 JSVM_Status JSVM_CDECL OH_JSVM_GetValueStringUtf8(
3617 JSVM_Env env, JSVM_Value value, char* buf, size_t bufsize, size_t* result) {
3618 CHECK_ENV(env);
3619 CHECK_ARG(env, value);
3620
3621 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3622 RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED);
3623
3624 if (!buf) {
3625 CHECK_ARG(env, result);
3626 *result = val.As<v8::String>()->Utf8Length(env->isolate);
3627 } else if (bufsize != 0) {
3628 int copied = val.As<v8::String>()->WriteUtf8(
3629 env->isolate,
3630 buf,
3631 bufsize - 1,
3632 nullptr,
3633 v8::String::REPLACE_INVALID_UTF8 | v8::String::NO_NULL_TERMINATION);
3634
3635 buf[copied] = '\0';
3636 if (result != nullptr) {
3637 *result = copied;
3638 }
3639 } else if (result != nullptr) {
3640 *result = 0;
3641 }
3642
3643 return jsvm_clear_last_error(env);
3644 }
3645
3646 // Copies a JavaScript string into a UTF-16 string buffer. The result is the
3647 // number of 2-byte code units (excluding the null terminator) copied into buf.
3648 // A sufficient buffer size should be greater than the length of string,
3649 // reserving space for null terminator.
3650 // If bufsize is insufficient, the string will be truncated and null terminated.
3651 // If buf is NULL, this method returns the length of the string (in 2-byte
3652 // code units) via the result parameter.
3653 // The result argument is optional unless buf is NULL.
OH_JSVM_GetValueStringUtf16(JSVM_Env env,JSVM_Value value,char16_t * buf,size_t bufsize,size_t * result)3654 JSVM_Status JSVM_CDECL OH_JSVM_GetValueStringUtf16(JSVM_Env env,
3655 JSVM_Value value,
3656 char16_t* buf,
3657 size_t bufsize,
3658 size_t* result) {
3659 CHECK_ENV(env);
3660 CHECK_ARG(env, value);
3661
3662 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3663 RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED);
3664
3665 if (!buf) {
3666 CHECK_ARG(env, result);
3667 // V8 assumes UTF-16 length is the same as the number of characters.
3668 *result = val.As<v8::String>()->Length();
3669 } else if (bufsize != 0) {
3670 int copied = val.As<v8::String>()->Write(env->isolate,
3671 reinterpret_cast<uint16_t*>(buf),
3672 0,
3673 bufsize - 1,
3674 v8::String::NO_NULL_TERMINATION);
3675
3676 buf[copied] = '\0';
3677 if (result != nullptr) {
3678 *result = copied;
3679 }
3680 } else if (result != nullptr) {
3681 *result = 0;
3682 }
3683
3684 return jsvm_clear_last_error(env);
3685 }
3686
OH_JSVM_CoerceToBool(JSVM_Env env,JSVM_Value value,JSVM_Value * result)3687 JSVM_Status JSVM_CDECL OH_JSVM_CoerceToBool(JSVM_Env env,
3688 JSVM_Value value,
3689 JSVM_Value* result) {
3690 JSVM_PREAMBLE(env);
3691 CHECK_ARG(env, value);
3692 CHECK_ARG(env, result);
3693
3694 v8::Isolate* isolate = env->isolate;
3695 v8::Local<v8::Boolean> b =
3696 v8impl::V8LocalValueFromJsValue(value)->ToBoolean(isolate);
3697 *result = v8impl::JsValueFromV8LocalValue(b);
3698 return GET_RETURN_STATUS(env);
3699 }
3700
3701 #define GEN_COERCE_FUNCTION(UpperCaseName, MixedCaseName, LowerCaseName) \
3702 JSVM_Status JSVM_CDECL OH_JSVM_CoerceTo##MixedCaseName( \
3703 JSVM_Env env, JSVM_Value value, JSVM_Value* result) { \
3704 JSVM_PREAMBLE(env); \
3705 CHECK_ARG(env, value); \
3706 CHECK_ARG(env, result); \
3707 \
3708 v8::Local<v8::Context> context = env->context(); \
3709 v8::Local<v8::MixedCaseName> str; \
3710 \
3711 CHECK_TO_##UpperCaseName(env, context, str, value); \
3712 \
3713 *result = v8impl::JsValueFromV8LocalValue(str); \
3714 return GET_RETURN_STATUS(env); \
3715 }
3716
GEN_COERCE_FUNCTION(NUMBER,Number,number)3717 GEN_COERCE_FUNCTION(NUMBER, Number, number)
3718 GEN_COERCE_FUNCTION(OBJECT, Object, object)
3719 GEN_COERCE_FUNCTION(STRING, String, string)
3720 GEN_COERCE_FUNCTION(BIGINT, BigInt, bigint)
3721
3722 #undef GEN_COERCE_FUNCTION
3723
3724 JSVM_Status JSVM_CDECL OH_JSVM_Wrap(JSVM_Env env,
3725 JSVM_Value jsObject,
3726 void* nativeObject,
3727 JSVM_Finalize finalizeCb,
3728 void* finalizeHint,
3729 JSVM_Ref* result) {
3730 return v8impl::Wrap(
3731 env, jsObject, nativeObject, finalizeCb, finalizeHint, result);
3732 }
3733
OH_JSVM_Unwrap(JSVM_Env env,JSVM_Value obj,void ** result)3734 JSVM_Status JSVM_CDECL OH_JSVM_Unwrap(JSVM_Env env,
3735 JSVM_Value obj,
3736 void** result) {
3737 return v8impl::Unwrap(env, obj, result, v8impl::KeepWrap);
3738 }
3739
OH_JSVM_RemoveWrap(JSVM_Env env,JSVM_Value obj,void ** result)3740 JSVM_Status JSVM_CDECL OH_JSVM_RemoveWrap(JSVM_Env env,
3741 JSVM_Value obj,
3742 void** result) {
3743 return v8impl::Unwrap(env, obj, result, v8impl::RemoveWrap);
3744 }
3745
OH_JSVM_CreateExternal(JSVM_Env env,void * data,JSVM_Finalize finalizeCb,void * finalizeHint,JSVM_Value * result)3746 JSVM_Status JSVM_CDECL OH_JSVM_CreateExternal(JSVM_Env env,
3747 void* data,
3748 JSVM_Finalize finalizeCb,
3749 void* finalizeHint,
3750 JSVM_Value* result) {
3751 JSVM_PREAMBLE(env);
3752 CHECK_ARG(env, result);
3753
3754 v8::Isolate* isolate = env->isolate;
3755
3756 v8::Local<v8::Value> external_value = v8::External::New(isolate, data);
3757
3758 if (finalizeCb) {
3759 // The Reference object will delete itself after invoking the finalizer
3760 // callback.
3761 v8impl::Reference::New(env,
3762 external_value,
3763 0,
3764 v8impl::Ownership::kRuntime,
3765 finalizeCb,
3766 data,
3767 finalizeHint);
3768 }
3769
3770 *result = v8impl::JsValueFromV8LocalValue(external_value);
3771
3772 return jsvm_clear_last_error(env);
3773 }
3774
OH_JSVM_TypeTagObject(JSVM_Env env,JSVM_Value object,const JSVM_TypeTag * typeTag)3775 JSVM_Status JSVM_CDECL OH_JSVM_TypeTagObject(JSVM_Env env,
3776 JSVM_Value object,
3777 const JSVM_TypeTag* typeTag) {
3778 JSVM_PREAMBLE(env);
3779 v8::Local<v8::Context> context = env->context();
3780 v8::Local<v8::Object> obj;
3781 CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, obj, object);
3782 CHECK_ARG_WITH_PREAMBLE(env, typeTag);
3783
3784 auto key = JSVM_PRIVATE_KEY(env->isolate, type_tag);
3785 auto maybe_has = obj->HasPrivate(context, key);
3786 CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe_has, JSVM_GENERIC_FAILURE);
3787 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(
3788 env, !maybe_has.FromJust(), JSVM_INVALID_ARG);
3789
3790 auto tag = v8::BigInt::NewFromWords(
3791 context, 0, 2, reinterpret_cast<const uint64_t*>(typeTag));
3792 CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, tag, JSVM_GENERIC_FAILURE);
3793
3794 auto maybe_set = obj->SetPrivate(context, key, tag.ToLocalChecked());
3795 CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe_set, JSVM_GENERIC_FAILURE);
3796 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(
3797 env, maybe_set.FromJust(), JSVM_GENERIC_FAILURE);
3798
3799 return GET_RETURN_STATUS(env);
3800 }
3801
OH_JSVM_CheckObjectTypeTag(JSVM_Env env,JSVM_Value object,const JSVM_TypeTag * typeTag,bool * result)3802 JSVM_Status JSVM_CDECL OH_JSVM_CheckObjectTypeTag(JSVM_Env env,
3803 JSVM_Value object,
3804 const JSVM_TypeTag* typeTag,
3805 bool* result) {
3806 JSVM_PREAMBLE(env);
3807 v8::Local<v8::Context> context = env->context();
3808 v8::Local<v8::Object> obj;
3809 CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, obj, object);
3810 CHECK_ARG_WITH_PREAMBLE(env, typeTag);
3811 CHECK_ARG_WITH_PREAMBLE(env, result);
3812
3813 auto maybe_value =
3814 obj->GetPrivate(context, JSVM_PRIVATE_KEY(env->isolate, type_tag));
3815 CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe_value, JSVM_GENERIC_FAILURE);
3816 v8::Local<v8::Value> val = maybe_value.ToLocalChecked();
3817
3818 // We consider the type check to have failed unless we reach the line below
3819 // where we set whether the type check succeeded or not based on the
3820 // comparison of the two type tags.
3821 *result = false;
3822 if (val->IsBigInt()) {
3823 int sign;
3824 int size = 2;
3825 JSVM_TypeTag tag;
3826 val.As<v8::BigInt>()->ToWordsArray(
3827 &sign, &size, reinterpret_cast<uint64_t*>(&tag));
3828 if (sign == 0) {
3829 if (size == 2) {
3830 *result =
3831 (tag.lower == typeTag->lower && tag.upper == typeTag->upper);
3832 } else if (size == 1) {
3833 *result = (tag.lower == typeTag->lower && 0 == typeTag->upper);
3834 } else if (size == 0) {
3835 *result = (0 == typeTag->lower && 0 == typeTag->upper);
3836 }
3837 }
3838 }
3839
3840 return GET_RETURN_STATUS(env);
3841 }
3842
OH_JSVM_GetValueExternal(JSVM_Env env,JSVM_Value value,void ** result)3843 JSVM_Status JSVM_CDECL OH_JSVM_GetValueExternal(JSVM_Env env,
3844 JSVM_Value value,
3845 void** result) {
3846 CHECK_ENV(env);
3847 CHECK_ARG(env, value);
3848 CHECK_ARG(env, result);
3849
3850 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
3851 RETURN_STATUS_IF_FALSE(env, val->IsExternal(), JSVM_INVALID_ARG);
3852
3853 v8::Local<v8::External> external_value = val.As<v8::External>();
3854 *result = external_value->Value();
3855
3856 return jsvm_clear_last_error(env);
3857 }
3858
3859 // Set initialRefcount to 0 for a weak reference, >0 for a strong reference.
OH_JSVM_CreateReference(JSVM_Env env,JSVM_Value value,uint32_t initialRefcount,JSVM_Ref * result)3860 JSVM_Status JSVM_CDECL OH_JSVM_CreateReference(JSVM_Env env,
3861 JSVM_Value value,
3862 uint32_t initialRefcount,
3863 JSVM_Ref* result) {
3864 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3865 // JS exceptions.
3866 CHECK_ENV(env);
3867 CHECK_ARG(env, value);
3868 CHECK_ARG(env, result);
3869
3870 v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(value);
3871 v8impl::Reference* reference = v8impl::Reference::New(
3872 env, v8_value, initialRefcount, v8impl::Ownership::kUserland);
3873
3874 *result = reinterpret_cast<JSVM_Ref>(reference);
3875 return jsvm_clear_last_error(env);
3876 }
3877
3878 // Deletes a reference. The referenced value is released, and may be GC'd unless
3879 // there are other references to it.
OH_JSVM_DeleteReference(JSVM_Env env,JSVM_Ref ref)3880 JSVM_Status JSVM_CDECL OH_JSVM_DeleteReference(JSVM_Env env, JSVM_Ref ref) {
3881 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3882 // JS exceptions.
3883 CHECK_ENV(env);
3884 CHECK_ARG(env, ref);
3885
3886 reinterpret_cast<v8impl::Reference*>(ref)->Delete();
3887
3888 return jsvm_clear_last_error(env);
3889 }
3890
3891 // Increments the reference count, optionally returning the resulting count.
3892 // After this call the reference will be a strong reference because its
3893 // refcount is >0, and the referenced object is effectively "pinned".
3894 // Calling this when the refcount is 0 and the object is unavailable
3895 // results in an error.
OH_JSVM_ReferenceRef(JSVM_Env env,JSVM_Ref ref,uint32_t * result)3896 JSVM_Status JSVM_CDECL OH_JSVM_ReferenceRef(JSVM_Env env,
3897 JSVM_Ref ref,
3898 uint32_t* result) {
3899 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3900 // JS exceptions.
3901 CHECK_ENV(env);
3902 CHECK_ARG(env, ref);
3903
3904 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
3905 if (reference->HasDeletedByUser()) {
3906 return jsvm_set_last_error(env, JSVM_GENERIC_FAILURE);
3907 }
3908 uint32_t count = reference->Ref();
3909
3910 if (result != nullptr) {
3911 *result = count;
3912 }
3913
3914 return jsvm_clear_last_error(env);
3915 }
3916
3917 // Decrements the reference count, optionally returning the resulting count. If
3918 // the result is 0 the reference is now weak and the object may be GC'd at any
3919 // time if there are no other references. Calling this when the refcount is
3920 // already 0 results in an error.
OH_JSVM_ReferenceUnref(JSVM_Env env,JSVM_Ref ref,uint32_t * result)3921 JSVM_Status JSVM_CDECL OH_JSVM_ReferenceUnref(JSVM_Env env,
3922 JSVM_Ref ref,
3923 uint32_t* result) {
3924 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3925 // JS exceptions.
3926 CHECK_ENV(env);
3927 CHECK_ARG(env, ref);
3928
3929 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
3930
3931 if (reference->RefCount() == 0 || reference->HasDeletedByUser()) {
3932 return jsvm_set_last_error(env, JSVM_GENERIC_FAILURE);
3933 }
3934
3935 uint32_t count = reference->Unref();
3936
3937 if (result != nullptr) {
3938 *result = count;
3939 }
3940
3941 return jsvm_clear_last_error(env);
3942 }
3943
3944 // Attempts to get a referenced value. If the reference is weak, the value might
3945 // no longer be available, in that case the call is still successful but the
3946 // result is NULL.
OH_JSVM_GetReferenceValue(JSVM_Env env,JSVM_Ref ref,JSVM_Value * result)3947 JSVM_Status JSVM_CDECL OH_JSVM_GetReferenceValue(JSVM_Env env,
3948 JSVM_Ref ref,
3949 JSVM_Value* result) {
3950 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3951 // JS exceptions.
3952 CHECK_ENV(env);
3953 CHECK_ARG(env, ref);
3954 CHECK_ARG(env, result);
3955
3956 v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
3957 *result = v8impl::JsValueFromV8LocalValue(reference->Get());
3958
3959 return jsvm_clear_last_error(env);
3960 }
3961
OH_JSVM_OpenHandleScope(JSVM_Env env,JSVM_HandleScope * result)3962 JSVM_Status JSVM_CDECL OH_JSVM_OpenHandleScope(JSVM_Env env,
3963 JSVM_HandleScope* result) {
3964 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3965 // JS exceptions.
3966 CHECK_ENV(env);
3967 CHECK_ARG(env, result);
3968
3969 *result = v8impl::JsHandleScopeFromV8HandleScope(
3970 new v8impl::HandleScopeWrapper(env->isolate));
3971 env->open_handle_scopes++;
3972 return jsvm_clear_last_error(env);
3973 }
3974
OH_JSVM_CloseHandleScope(JSVM_Env env,JSVM_HandleScope scope)3975 JSVM_Status JSVM_CDECL OH_JSVM_CloseHandleScope(JSVM_Env env,
3976 JSVM_HandleScope scope) {
3977 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3978 // JS exceptions.
3979 CHECK_ENV(env);
3980 CHECK_ARG(env, scope);
3981 if (env->open_handle_scopes == 0) {
3982 return JSVM_HANDLE_SCOPE_MISMATCH;
3983 }
3984
3985 env->ReleaseJsvmData();
3986 env->open_handle_scopes--;
3987 delete v8impl::V8HandleScopeFromJsHandleScope(scope);
3988 return jsvm_clear_last_error(env);
3989 }
3990
OH_JSVM_OpenEscapableHandleScope(JSVM_Env env,JSVM_EscapableHandleScope * result)3991 JSVM_Status JSVM_CDECL OH_JSVM_OpenEscapableHandleScope(
3992 JSVM_Env env, JSVM_EscapableHandleScope* result) {
3993 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
3994 // JS exceptions.
3995 CHECK_ENV(env);
3996 CHECK_ARG(env, result);
3997
3998 *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope(
3999 new v8impl::EscapableHandleScopeWrapper(env->isolate));
4000 env->open_handle_scopes++;
4001 return jsvm_clear_last_error(env);
4002 }
4003
OH_JSVM_CloseEscapableHandleScope(JSVM_Env env,JSVM_EscapableHandleScope scope)4004 JSVM_Status JSVM_CDECL OH_JSVM_CloseEscapableHandleScope(
4005 JSVM_Env env, JSVM_EscapableHandleScope scope) {
4006 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
4007 // JS exceptions.
4008 CHECK_ENV(env);
4009 CHECK_ARG(env, scope);
4010 if (env->open_handle_scopes == 0) {
4011 return JSVM_HANDLE_SCOPE_MISMATCH;
4012 }
4013
4014 delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope);
4015 env->open_handle_scopes--;
4016 return jsvm_clear_last_error(env);
4017 }
4018
OH_JSVM_EscapeHandle(JSVM_Env env,JSVM_EscapableHandleScope scope,JSVM_Value escapee,JSVM_Value * result)4019 JSVM_Status JSVM_CDECL OH_JSVM_EscapeHandle(JSVM_Env env,
4020 JSVM_EscapableHandleScope scope,
4021 JSVM_Value escapee,
4022 JSVM_Value* result) {
4023 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
4024 // JS exceptions.
4025 CHECK_ENV(env);
4026 CHECK_ARG(env, scope);
4027 CHECK_ARG(env, escapee);
4028 CHECK_ARG(env, result);
4029
4030 v8impl::EscapableHandleScopeWrapper* s =
4031 v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope);
4032 if (!s->escape_called()) {
4033 *result = v8impl::JsValueFromV8LocalValue(
4034 s->Escape(v8impl::V8LocalValueFromJsValue(escapee)));
4035 return jsvm_clear_last_error(env);
4036 }
4037 return jsvm_set_last_error(env, JSVM_ESCAPE_CALLED_TWICE);
4038 }
4039
OH_JSVM_NewInstance(JSVM_Env env,JSVM_Value constructor,size_t argc,const JSVM_Value * argv,JSVM_Value * result)4040 JSVM_Status JSVM_CDECL OH_JSVM_NewInstance(JSVM_Env env,
4041 JSVM_Value constructor,
4042 size_t argc,
4043 const JSVM_Value* argv,
4044 JSVM_Value* result) {
4045 JSVM_PREAMBLE(env);
4046 CHECK_ARG(env, constructor);
4047 if (argc > 0) {
4048 CHECK_ARG(env, argv);
4049 }
4050 CHECK_ARG(env, result);
4051
4052 v8::Local<v8::Context> context = env->context();
4053
4054 v8::Local<v8::Function> ctor;
4055 CHECK_TO_FUNCTION(env, ctor, constructor);
4056
4057 auto maybe = ctor->NewInstance(
4058 context,
4059 argc,
4060 reinterpret_cast<v8::Local<v8::Value>*>(const_cast<JSVM_Value*>(argv)));
4061
4062 CHECK_MAYBE_EMPTY(env, maybe, JSVM_PENDING_EXCEPTION);
4063
4064 *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked());
4065 return GET_RETURN_STATUS(env);
4066 }
4067
OH_JSVM_Instanceof(JSVM_Env env,JSVM_Value object,JSVM_Value constructor,bool * result)4068 JSVM_Status JSVM_CDECL OH_JSVM_Instanceof(JSVM_Env env,
4069 JSVM_Value object,
4070 JSVM_Value constructor,
4071 bool* result) {
4072 JSVM_PREAMBLE(env);
4073 CHECK_ARG(env, object);
4074 CHECK_ARG(env, result);
4075
4076 *result = false;
4077
4078 v8::Local<v8::Object> ctor;
4079 v8::Local<v8::Context> context = env->context();
4080
4081 CHECK_TO_OBJECT(env, context, ctor, constructor);
4082
4083 if (!ctor->IsFunction()) {
4084 OH_JSVM_ThrowTypeError(
4085 env, "ERR_NAPI_CONS_FUNCTION", "Constructor must be a function");
4086
4087 return jsvm_set_last_error(env, JSVM_FUNCTION_EXPECTED);
4088 }
4089
4090 JSVM_Status status = JSVM_GENERIC_FAILURE;
4091
4092 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(object);
4093 auto maybe_result = val->InstanceOf(context, ctor);
4094 CHECK_MAYBE_NOTHING(env, maybe_result, status);
4095 *result = maybe_result.FromJust();
4096 return GET_RETURN_STATUS(env);
4097 }
4098
4099 // Methods to support catching exceptions
OH_JSVM_IsExceptionPending(JSVM_Env env,bool * result)4100 JSVM_Status JSVM_CDECL OH_JSVM_IsExceptionPending(JSVM_Env env, bool* result) {
4101 // JSVM_PREAMBLE is not used here: this function must execute when there is a
4102 // pending exception.
4103 CHECK_ENV(env);
4104 CHECK_ARG(env, result);
4105
4106 *result = !env->last_exception.IsEmpty();
4107 return jsvm_clear_last_error(env);
4108 }
4109
OH_JSVM_GetAndClearLastException(JSVM_Env env,JSVM_Value * result)4110 JSVM_Status JSVM_CDECL OH_JSVM_GetAndClearLastException(JSVM_Env env,
4111 JSVM_Value* result) {
4112 // JSVM_PREAMBLE is not used here: this function must execute when there is a
4113 // pending exception.
4114 CHECK_ENV(env);
4115 CHECK_ARG(env, result);
4116
4117 if (env->last_exception.IsEmpty()) {
4118 return OH_JSVM_GetUndefined(env, result);
4119 } else {
4120 *result = v8impl::JsValueFromV8LocalValue(
4121 v8::Local<v8::Value>::New(env->isolate, env->last_exception));
4122 env->last_exception.Reset();
4123 }
4124
4125 return jsvm_clear_last_error(env);
4126 }
4127
OH_JSVM_IsArraybuffer(JSVM_Env env,JSVM_Value value,bool * result)4128 JSVM_Status JSVM_CDECL OH_JSVM_IsArraybuffer(JSVM_Env env,
4129 JSVM_Value value,
4130 bool* result) {
4131 CHECK_ENV(env);
4132 CHECK_ARG(env, value);
4133 CHECK_ARG(env, result);
4134
4135 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4136 *result = val->IsArrayBuffer();
4137
4138 return jsvm_clear_last_error(env);
4139 }
4140
OH_JSVM_CreateArraybuffer(JSVM_Env env,size_t byteLength,void ** data,JSVM_Value * result)4141 JSVM_Status JSVM_CDECL OH_JSVM_CreateArraybuffer(JSVM_Env env,
4142 size_t byteLength,
4143 void** data,
4144 JSVM_Value* result) {
4145 JSVM_PREAMBLE(env);
4146 CHECK_ARG(env, result);
4147
4148 v8::Isolate* isolate = env->isolate;
4149 v8::Local<v8::ArrayBuffer> buffer =
4150 v8::ArrayBuffer::New(isolate, byteLength);
4151
4152 // Optionally return a pointer to the buffer's data, to avoid another call to
4153 // retrieve it.
4154 if (data != nullptr) {
4155 *data = buffer->Data();
4156 }
4157
4158 *result = v8impl::JsValueFromV8LocalValue(buffer);
4159 return GET_RETURN_STATUS(env);
4160 }
4161
OH_JSVM_AllocateArrayBufferBackingStoreData(size_t byteLength,JSVM_InitializedFlag initialized,void ** data)4162 JSVM_Status JSVM_CDECL OH_JSVM_AllocateArrayBufferBackingStoreData(size_t byteLength,
4163 JSVM_InitializedFlag initialized,
4164 void **data) {
4165 if (!data) {
4166 return JSVM_INVALID_ARG;
4167 }
4168 auto allocator = v8impl::GetOrCreateDefaultArrayBufferAllocator();
4169 *data = (initialized == JSVM_ZERO_INITIALIZED) ?
4170 allocator->Allocate(byteLength) :
4171 allocator->AllocateUninitialized(byteLength);
4172 return *data ? JSVM_OK : JSVM_GENERIC_FAILURE;
4173 }
4174
OH_JSVM_FreeArrayBufferBackingStoreData(void * data)4175 JSVM_Status JSVM_CDECL OH_JSVM_FreeArrayBufferBackingStoreData(void *data) {
4176 if (!data) {
4177 return JSVM_INVALID_ARG;
4178 }
4179 auto allocator = v8impl::GetOrCreateDefaultArrayBufferAllocator();
4180 allocator->Free(data, JSVM_AUTO_LENGTH);
4181 return JSVM_OK;
4182 }
4183
OH_JSVM_CreateArrayBufferFromBackingStoreData(JSVM_Env env,void * data,size_t byteLength,size_t offset,size_t slicedByteLength,JSVM_Value * result)4184 JSVM_Status JSVM_CDECL OH_JSVM_CreateArrayBufferFromBackingStoreData(JSVM_Env env,
4185 void *data,
4186 size_t byteLength,
4187 size_t offset,
4188 size_t slicedByteLength,
4189 JSVM_Value *result) {
4190 CHECK_ENV(env);
4191 JSVM_PREAMBLE(env);
4192 CHECK_ARG(env, data);
4193 CHECK_ARG(env, result);
4194 CHECK_ARG_NOT_ZERO(env, byteLength);
4195 CHECK_ARG_NOT_ZERO(env, slicedByteLength);
4196 void *dataPtr = static_cast<uint8_t*>(data) + offset;
4197 auto backingStoreSize = slicedByteLength;
4198 RETURN_STATUS_IF_FALSE(env, offset + slicedByteLength <= byteLength, JSVM_INVALID_ARG);
4199 auto backingStore = v8::ArrayBuffer::NewBackingStore(
4200 dataPtr, backingStoreSize, v8::BackingStore::EmptyDeleter, nullptr);
4201 v8::Local<v8::ArrayBuffer> arrayBuffer =
4202 v8::ArrayBuffer::New(env->isolate, std::move(backingStore));
4203 *result = v8impl::JsValueFromV8LocalValue(arrayBuffer);
4204 return jsvm_clear_last_error(env);
4205 }
4206
4207 JSVM_Status JSVM_CDECL
OH_JSVM_CreateExternalArraybuffer(JSVM_Env env,void * externalData,size_t byteLength,JSVM_Finalize finalizeCb,void * finalizeHint,JSVM_Value * result)4208 OH_JSVM_CreateExternalArraybuffer(JSVM_Env env,
4209 void* externalData,
4210 size_t byteLength,
4211 JSVM_Finalize finalizeCb,
4212 void* finalizeHint,
4213 JSVM_Value* result) {
4214 // The API contract here is that the cleanup function runs on the JS thread,
4215 // and is able to use JSVM_Env. Implementing that properly is hard, so use the
4216 // `Buffer` variant for easier implementation.
4217 JSVM_Value buffer;
4218 STATUS_CALL(OH_JSVM_CreateExternal_buffer(
4219 env, byteLength, externalData, finalizeCb, finalizeHint, &buffer));
4220 return OH_JSVM_GetTypedarrayInfo(
4221 env, buffer, nullptr, nullptr, nullptr, result, nullptr);
4222 }
4223
OH_JSVM_GetArraybufferInfo(JSVM_Env env,JSVM_Value arraybuffer,void ** data,size_t * byteLength)4224 JSVM_Status JSVM_CDECL OH_JSVM_GetArraybufferInfo(JSVM_Env env,
4225 JSVM_Value arraybuffer,
4226 void** data,
4227 size_t* byteLength) {
4228 CHECK_ENV(env);
4229 CHECK_ARG(env, arraybuffer);
4230
4231 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
4232 RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), JSVM_INVALID_ARG);
4233
4234 v8::Local<v8::ArrayBuffer> ab = value.As<v8::ArrayBuffer>();
4235
4236 if (data != nullptr) {
4237 *data = ab->Data();
4238 }
4239
4240 if (byteLength != nullptr) {
4241 *byteLength = ab->ByteLength();
4242 }
4243
4244 return jsvm_clear_last_error(env);
4245 }
4246
OH_JSVM_IsTypedarray(JSVM_Env env,JSVM_Value value,bool * result)4247 JSVM_Status JSVM_CDECL OH_JSVM_IsTypedarray(JSVM_Env env,
4248 JSVM_Value value,
4249 bool* result) {
4250 CHECK_ENV(env);
4251 CHECK_ARG(env, value);
4252 CHECK_ARG(env, result);
4253
4254 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4255 *result = val->IsTypedArray();
4256
4257 return jsvm_clear_last_error(env);
4258 }
4259
OH_JSVM_CreateTypedarray(JSVM_Env env,JSVM_TypedarrayType type,size_t length,JSVM_Value arraybuffer,size_t byteOffset,JSVM_Value * result)4260 JSVM_Status JSVM_CDECL OH_JSVM_CreateTypedarray(JSVM_Env env,
4261 JSVM_TypedarrayType type,
4262 size_t length,
4263 JSVM_Value arraybuffer,
4264 size_t byteOffset,
4265 JSVM_Value* result) {
4266 JSVM_PREAMBLE(env);
4267 CHECK_ARG(env, arraybuffer);
4268 CHECK_ARG(env, result);
4269
4270 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
4271 RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), JSVM_INVALID_ARG);
4272
4273 v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
4274 v8::Local<v8::TypedArray> typedArray;
4275
4276 switch (type) {
4277 case JSVM_INT8_ARRAY:
4278 CREATE_TYPED_ARRAY(
4279 env, Int8Array, 1, buffer, byteOffset, length, typedArray);
4280 break;
4281 case JSVM_UINT8_ARRAY:
4282 CREATE_TYPED_ARRAY(
4283 env, Uint8Array, 1, buffer, byteOffset, length, typedArray);
4284 break;
4285 case JSVM_UINT8_CLAMPED_ARRAY:
4286 CREATE_TYPED_ARRAY(
4287 env, Uint8ClampedArray, 1, buffer, byteOffset, length, typedArray);
4288 break;
4289 case JSVM_INT16_ARRAY:
4290 CREATE_TYPED_ARRAY(
4291 env, Int16Array, 2, buffer, byteOffset, length, typedArray);
4292 break;
4293 case JSVM_UINT16_ARRAY:
4294 CREATE_TYPED_ARRAY(
4295 env, Uint16Array, 2, buffer, byteOffset, length, typedArray);
4296 break;
4297 case JSVM_INT32_ARRAY:
4298 CREATE_TYPED_ARRAY(
4299 env, Int32Array, 4, buffer, byteOffset, length, typedArray);
4300 break;
4301 case JSVM_UINT32_ARRAY:
4302 CREATE_TYPED_ARRAY(
4303 env, Uint32Array, 4, buffer, byteOffset, length, typedArray);
4304 break;
4305 case JSVM_FLOAT32_ARRAY:
4306 CREATE_TYPED_ARRAY(
4307 env, Float32Array, 4, buffer, byteOffset, length, typedArray);
4308 break;
4309 case JSVM_FLOAT64_ARRAY:
4310 CREATE_TYPED_ARRAY(
4311 env, Float64Array, 8, buffer, byteOffset, length, typedArray);
4312 break;
4313 case JSVM_BIGINT64_ARRAY:
4314 CREATE_TYPED_ARRAY(
4315 env, BigInt64Array, 8, buffer, byteOffset, length, typedArray);
4316 break;
4317 case JSVM_BIGUINT64_ARRAY:
4318 CREATE_TYPED_ARRAY(
4319 env, BigUint64Array, 8, buffer, byteOffset, length, typedArray);
4320 break;
4321 default:
4322 return jsvm_set_last_error(env, JSVM_INVALID_ARG);
4323 }
4324
4325 *result = v8impl::JsValueFromV8LocalValue(typedArray);
4326 return GET_RETURN_STATUS(env);
4327 }
4328
OH_JSVM_GetTypedarrayInfo(JSVM_Env env,JSVM_Value typedarray,JSVM_TypedarrayType * type,size_t * length,void ** data,JSVM_Value * arraybuffer,size_t * byteOffset)4329 JSVM_Status JSVM_CDECL OH_JSVM_GetTypedarrayInfo(JSVM_Env env,
4330 JSVM_Value typedarray,
4331 JSVM_TypedarrayType* type,
4332 size_t* length,
4333 void** data,
4334 JSVM_Value* arraybuffer,
4335 size_t* byteOffset) {
4336 CHECK_ENV(env);
4337 CHECK_ARG(env, typedarray);
4338
4339 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(typedarray);
4340 RETURN_STATUS_IF_FALSE(env, value->IsTypedArray(), JSVM_INVALID_ARG);
4341
4342 v8::Local<v8::TypedArray> array = value.As<v8::TypedArray>();
4343
4344 if (type != nullptr) {
4345 if (value->IsInt8Array()) {
4346 *type = JSVM_INT8_ARRAY;
4347 } else if (value->IsUint8Array()) {
4348 *type = JSVM_UINT8_ARRAY;
4349 } else if (value->IsUint8ClampedArray()) {
4350 *type = JSVM_UINT8_CLAMPED_ARRAY;
4351 } else if (value->IsInt16Array()) {
4352 *type = JSVM_INT16_ARRAY;
4353 } else if (value->IsUint16Array()) {
4354 *type = JSVM_UINT16_ARRAY;
4355 } else if (value->IsInt32Array()) {
4356 *type = JSVM_INT32_ARRAY;
4357 } else if (value->IsUint32Array()) {
4358 *type = JSVM_UINT32_ARRAY;
4359 } else if (value->IsFloat32Array()) {
4360 *type = JSVM_FLOAT32_ARRAY;
4361 } else if (value->IsFloat64Array()) {
4362 *type = JSVM_FLOAT64_ARRAY;
4363 } else if (value->IsBigInt64Array()) {
4364 *type = JSVM_BIGINT64_ARRAY;
4365 } else if (value->IsBigUint64Array()) {
4366 *type = JSVM_BIGUINT64_ARRAY;
4367 }
4368 }
4369
4370 if (length != nullptr) {
4371 *length = array->Length();
4372 }
4373
4374 v8::Local<v8::ArrayBuffer> buffer;
4375 if (data != nullptr || arraybuffer != nullptr) {
4376 // Calling Buffer() may have the side effect of allocating the buffer,
4377 // so only do this when it’s needed.
4378 buffer = array->Buffer();
4379 }
4380
4381 if (data != nullptr) {
4382 *data = static_cast<uint8_t*>(buffer->Data()) + array->ByteOffset();
4383 }
4384
4385 if (arraybuffer != nullptr) {
4386 *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer);
4387 }
4388
4389 if (byteOffset != nullptr) {
4390 *byteOffset = array->ByteOffset();
4391 }
4392
4393 return jsvm_clear_last_error(env);
4394 }
4395
OH_JSVM_CreateDataview(JSVM_Env env,size_t byteLength,JSVM_Value arraybuffer,size_t byteOffset,JSVM_Value * result)4396 JSVM_Status JSVM_CDECL OH_JSVM_CreateDataview(JSVM_Env env,
4397 size_t byteLength,
4398 JSVM_Value arraybuffer,
4399 size_t byteOffset,
4400 JSVM_Value* result) {
4401 JSVM_PREAMBLE(env);
4402 CHECK_ARG(env, arraybuffer);
4403 CHECK_ARG(env, result);
4404
4405 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
4406 RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), JSVM_INVALID_ARG);
4407
4408 v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
4409 if (byteLength + byteOffset > buffer->ByteLength()) {
4410 OH_JSVM_ThrowRangeError(env,
4411 "ERR_JSVM_INVALID_DATAVIEW_ARGS",
4412 "byteOffset + byteLength should be less than or "
4413 "equal to the size in bytes of the array passed in");
4414 return jsvm_set_last_error(env, JSVM_PENDING_EXCEPTION);
4415 }
4416 v8::Local<v8::DataView> DataView =
4417 v8::DataView::New(buffer, byteOffset, byteLength);
4418
4419 *result = v8impl::JsValueFromV8LocalValue(DataView);
4420 return GET_RETURN_STATUS(env);
4421 }
4422
OH_JSVM_IsDataview(JSVM_Env env,JSVM_Value value,bool * result)4423 JSVM_Status JSVM_CDECL OH_JSVM_IsDataview(JSVM_Env env,
4424 JSVM_Value value,
4425 bool* result) {
4426 CHECK_ENV(env);
4427 CHECK_ARG(env, value);
4428 CHECK_ARG(env, result);
4429
4430 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4431 *result = val->IsDataView();
4432
4433 return jsvm_clear_last_error(env);
4434 }
4435
OH_JSVM_GetDataviewInfo(JSVM_Env env,JSVM_Value dataview,size_t * byteLength,void ** data,JSVM_Value * arraybuffer,size_t * byteOffset)4436 JSVM_Status JSVM_CDECL OH_JSVM_GetDataviewInfo(JSVM_Env env,
4437 JSVM_Value dataview,
4438 size_t* byteLength,
4439 void** data,
4440 JSVM_Value* arraybuffer,
4441 size_t* byteOffset) {
4442 CHECK_ENV(env);
4443 CHECK_ARG(env, dataview);
4444
4445 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(dataview);
4446 RETURN_STATUS_IF_FALSE(env, value->IsDataView(), JSVM_INVALID_ARG);
4447
4448 v8::Local<v8::DataView> array = value.As<v8::DataView>();
4449
4450 if (byteLength != nullptr) {
4451 *byteLength = array->ByteLength();
4452 }
4453
4454 v8::Local<v8::ArrayBuffer> buffer;
4455 if (data != nullptr || arraybuffer != nullptr) {
4456 // Calling Buffer() may have the side effect of allocating the buffer,
4457 // so only do this when it’s needed.
4458 buffer = array->Buffer();
4459 }
4460
4461 if (data != nullptr) {
4462 *data = static_cast<uint8_t*>(buffer->Data()) + array->ByteOffset();
4463 }
4464
4465 if (arraybuffer != nullptr) {
4466 *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer);
4467 }
4468
4469 if (byteOffset != nullptr) {
4470 *byteOffset = array->ByteOffset();
4471 }
4472
4473 return jsvm_clear_last_error(env);
4474 }
4475
OH_JSVM_GetVersion(JSVM_Env env,uint32_t * result)4476 JSVM_Status JSVM_CDECL OH_JSVM_GetVersion(JSVM_Env env, uint32_t* result) {
4477 CHECK_ENV(env);
4478 CHECK_ARG(env, result);
4479 *result = NAPI_VERSION;
4480 return jsvm_clear_last_error(env);
4481 }
4482
OH_JSVM_CreatePromise(JSVM_Env env,JSVM_Deferred * deferred,JSVM_Value * promise)4483 JSVM_Status JSVM_CDECL OH_JSVM_CreatePromise(JSVM_Env env,
4484 JSVM_Deferred* deferred,
4485 JSVM_Value* promise) {
4486 JSVM_PREAMBLE(env);
4487 CHECK_ARG(env, deferred);
4488 CHECK_ARG(env, promise);
4489
4490 auto maybe = v8::Promise::Resolver::New(env->context());
4491 CHECK_MAYBE_EMPTY(env, maybe, JSVM_GENERIC_FAILURE);
4492
4493 auto v8_resolver = maybe.ToLocalChecked();
4494 auto v8_deferred = new v8impl::Persistent<v8::Value>();
4495 v8_deferred->Reset(env->isolate, v8_resolver);
4496
4497 *deferred = v8impl::JsDeferredFromNodePersistent(v8_deferred);
4498 *promise = v8impl::JsValueFromV8LocalValue(v8_resolver->GetPromise());
4499 return GET_RETURN_STATUS(env);
4500 }
4501
OH_JSVM_ResolveDeferred(JSVM_Env env,JSVM_Deferred deferred,JSVM_Value resolution)4502 JSVM_Status JSVM_CDECL OH_JSVM_ResolveDeferred(JSVM_Env env,
4503 JSVM_Deferred deferred,
4504 JSVM_Value resolution) {
4505 return v8impl::ConcludeDeferred(env, deferred, resolution, true);
4506 }
4507
OH_JSVM_RejectDeferred(JSVM_Env env,JSVM_Deferred deferred,JSVM_Value resolution)4508 JSVM_Status JSVM_CDECL OH_JSVM_RejectDeferred(JSVM_Env env,
4509 JSVM_Deferred deferred,
4510 JSVM_Value resolution) {
4511 return v8impl::ConcludeDeferred(env, deferred, resolution, false);
4512 }
4513
OH_JSVM_IsPromise(JSVM_Env env,JSVM_Value value,bool * is_promise)4514 JSVM_Status JSVM_CDECL OH_JSVM_IsPromise(JSVM_Env env,
4515 JSVM_Value value,
4516 bool* is_promise) {
4517 CHECK_ENV(env);
4518 CHECK_ARG(env, value);
4519 CHECK_ARG(env, is_promise);
4520
4521 *is_promise = v8impl::V8LocalValueFromJsValue(value)->IsPromise();
4522
4523 return jsvm_clear_last_error(env);
4524 }
4525
OH_JSVM_CreateDate(JSVM_Env env,double time,JSVM_Value * result)4526 JSVM_Status JSVM_CDECL OH_JSVM_CreateDate(JSVM_Env env,
4527 double time,
4528 JSVM_Value* result) {
4529 JSVM_PREAMBLE(env);
4530 CHECK_ARG(env, result);
4531
4532 v8::MaybeLocal<v8::Value> maybe_date = v8::Date::New(env->context(), time);
4533 CHECK_MAYBE_EMPTY(env, maybe_date, JSVM_GENERIC_FAILURE);
4534
4535 *result = v8impl::JsValueFromV8LocalValue(maybe_date.ToLocalChecked());
4536
4537 return GET_RETURN_STATUS(env);
4538 }
4539
OH_JSVM_IsDate(JSVM_Env env,JSVM_Value value,bool * isDate)4540 JSVM_Status JSVM_CDECL OH_JSVM_IsDate(JSVM_Env env,
4541 JSVM_Value value,
4542 bool* isDate) {
4543 CHECK_ENV(env);
4544 CHECK_ARG(env, value);
4545 CHECK_ARG(env, isDate);
4546
4547 *isDate = v8impl::V8LocalValueFromJsValue(value)->IsDate();
4548
4549 return jsvm_clear_last_error(env);
4550 }
4551
OH_JSVM_GetDateValue(JSVM_Env env,JSVM_Value value,double * result)4552 JSVM_Status JSVM_CDECL OH_JSVM_GetDateValue(JSVM_Env env,
4553 JSVM_Value value,
4554 double* result) {
4555 JSVM_PREAMBLE(env);
4556 CHECK_ARG(env, value);
4557 CHECK_ARG(env, result);
4558
4559 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4560 RETURN_STATUS_IF_FALSE(env, val->IsDate(), JSVM_DATE_EXPECTED);
4561
4562 v8::Local<v8::Date> date = val.As<v8::Date>();
4563 *result = date->ValueOf();
4564
4565 return GET_RETURN_STATUS(env);
4566 }
4567
OH_JSVM_AddFinalizer(JSVM_Env env,JSVM_Value jsObject,void * finalizeData,JSVM_Finalize finalizeCb,void * finalizeHint,JSVM_Ref * result)4568 JSVM_Status JSVM_CDECL OH_JSVM_AddFinalizer(JSVM_Env env,
4569 JSVM_Value jsObject,
4570 void* finalizeData,
4571 JSVM_Finalize finalizeCb,
4572 void* finalizeHint,
4573 JSVM_Ref* result) {
4574 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
4575 // JS exceptions.
4576 CHECK_ENV(env);
4577 CHECK_ARG(env, jsObject);
4578 CHECK_ARG(env, finalizeCb);
4579
4580 v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(jsObject);
4581 RETURN_STATUS_IF_FALSE(env, v8_value->IsObject(), JSVM_INVALID_ARG);
4582
4583 // Create a self-deleting reference if the optional out-param result is not
4584 // set.
4585 v8impl::Ownership ownership = result == nullptr
4586 ? v8impl::Ownership::kRuntime
4587 : v8impl::Ownership::kUserland;
4588 v8impl::Reference* reference = v8impl::Reference::New(
4589 env, v8_value, 0, ownership, finalizeCb, finalizeData, finalizeHint);
4590
4591 if (result != nullptr) {
4592 *result = reinterpret_cast<JSVM_Ref>(reference);
4593 }
4594 return jsvm_clear_last_error(env);
4595 }
4596
OH_JSVM_AdjustExternalMemory(JSVM_Env env,int64_t changeInBytes,int64_t * adjustedValue)4597 JSVM_Status JSVM_CDECL OH_JSVM_AdjustExternalMemory(JSVM_Env env,
4598 int64_t changeInBytes,
4599 int64_t* adjustedValue) {
4600 CHECK_ENV(env);
4601 CHECK_ARG(env, adjustedValue);
4602
4603 *adjustedValue =
4604 env->isolate->AdjustAmountOfExternalAllocatedMemory(changeInBytes);
4605
4606 return jsvm_clear_last_error(env);
4607 }
4608
OH_JSVM_SetInstanceData(JSVM_Env env,void * data,JSVM_Finalize finalizeCb,void * finalizeHint)4609 JSVM_Status JSVM_CDECL OH_JSVM_SetInstanceData(JSVM_Env env,
4610 void* data,
4611 JSVM_Finalize finalizeCb,
4612 void* finalizeHint) {
4613 CHECK_ENV(env);
4614
4615 v8impl::RefBase* old_data = static_cast<v8impl::RefBase*>(env->instance_data);
4616 if (old_data != nullptr) {
4617 // Our contract so far has been to not finalize any old data there may be.
4618 // So we simply delete it.
4619 delete old_data;
4620 }
4621
4622 env->instance_data = v8impl::RefBase::New(
4623 env, 0, v8impl::Ownership::kRuntime, finalizeCb, data, finalizeHint);
4624
4625 return jsvm_clear_last_error(env);
4626 }
4627
OH_JSVM_GetInstanceData(JSVM_Env env,void ** data)4628 JSVM_Status JSVM_CDECL OH_JSVM_GetInstanceData(JSVM_Env env, void** data) {
4629 CHECK_ENV(env);
4630 CHECK_ARG(env, data);
4631
4632 v8impl::RefBase* idata = static_cast<v8impl::RefBase*>(env->instance_data);
4633
4634 *data = (idata == nullptr ? nullptr : idata->Data());
4635
4636 return jsvm_clear_last_error(env);
4637 }
4638
OH_JSVM_DetachArraybuffer(JSVM_Env env,JSVM_Value arraybuffer)4639 JSVM_Status JSVM_CDECL OH_JSVM_DetachArraybuffer(JSVM_Env env,
4640 JSVM_Value arraybuffer) {
4641 CHECK_ENV(env);
4642 CHECK_ARG(env, arraybuffer);
4643
4644 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
4645 RETURN_STATUS_IF_FALSE(
4646 env, value->IsArrayBuffer() || value->IsSharedArrayBuffer(), JSVM_ARRAYBUFFER_EXPECTED);
4647
4648 v8::Local<v8::ArrayBuffer> it = value.As<v8::ArrayBuffer>();
4649 RETURN_STATUS_IF_FALSE(
4650 env, it->IsDetachable(), JSVM_DETACHABLE_ARRAYBUFFER_EXPECTED);
4651
4652 it->Detach();
4653
4654 return jsvm_clear_last_error(env);
4655 }
4656
OH_JSVM_IsDetachedArraybuffer(JSVM_Env env,JSVM_Value arraybuffer,bool * result)4657 JSVM_Status JSVM_CDECL OH_JSVM_IsDetachedArraybuffer(JSVM_Env env,
4658 JSVM_Value arraybuffer,
4659 bool* result) {
4660 CHECK_ENV(env);
4661 CHECK_ARG(env, arraybuffer);
4662 CHECK_ARG(env, result);
4663
4664 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(arraybuffer);
4665
4666 *result =
4667 value->IsArrayBuffer() && value.As<v8::ArrayBuffer>()->WasDetached();
4668
4669 return jsvm_clear_last_error(env);
4670 }
4671
4672 JSVM_Status JSVM_CDECL
OH_JSVM_DefineClassWithPropertyHandler(JSVM_Env env,const char * utf8name,size_t length,JSVM_Callback constructor,size_t propertyCount,const JSVM_PropertyDescriptor * properties,JSVM_PropertyHandlerCfg propertyHandlerCfg,JSVM_Callback callAsFunctionCallback,JSVM_Value * result)4673 OH_JSVM_DefineClassWithPropertyHandler(JSVM_Env env,
4674 const char* utf8name,
4675 size_t length,
4676 JSVM_Callback constructor,
4677 size_t propertyCount,
4678 const JSVM_PropertyDescriptor* properties,
4679 JSVM_PropertyHandlerCfg propertyHandlerCfg,
4680 JSVM_Callback callAsFunctionCallback,
4681 JSVM_Value* result) {
4682 JSVM_PREAMBLE(env);
4683 CHECK_ARG(env, result);
4684 CHECK_ARG(env, constructor);
4685 CHECK_ARG(env, constructor->callback);
4686 CHECK_ARG(env, propertyHandlerCfg);
4687
4688 if (propertyCount > 0) {
4689 CHECK_ARG(env, properties);
4690 }
4691
4692 v8::Isolate* isolate = env->isolate;
4693 v8::EscapableHandleScope scope(isolate);
4694 v8::Local<v8::FunctionTemplate> tpl;
4695 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
4696 env, constructor, &tpl));
4697
4698 v8::Local<v8::String> name_string;
4699 CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length);
4700 tpl->SetClassName(name_string);
4701
4702 size_t static_property_count = 0;
4703 for (size_t i = 0; i < propertyCount; i++) {
4704 const JSVM_PropertyDescriptor* p = properties + i;
4705
4706 if ((p->attributes & JSVM_STATIC) != 0) { // attributes
4707 // Static properties are handled separately below.
4708 static_property_count++;
4709 continue;
4710 }
4711
4712 v8::Local<v8::Name> property_name;
4713 STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name));
4714 v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p);
4715
4716 // This code is similar to that in OH_JSVM_DefineProperties(); the
4717 // difference is it applies to a template instead of an object,
4718 // and preferred PropertyAttribute for lack of PropertyDescriptor
4719 // support on ObjectTemplate.
4720 if (p->getter != nullptr || p->setter != nullptr) {
4721 v8::Local<v8::FunctionTemplate> getter_tpl;
4722 v8::Local<v8::FunctionTemplate> setter_tpl;
4723 if (p->getter != nullptr) {
4724 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
4725 env, p->getter, &getter_tpl));
4726 }
4727 if (p->setter != nullptr) {
4728 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
4729 env, p->setter, &setter_tpl));
4730 }
4731
4732 tpl->PrototypeTemplate()->SetAccessorProperty(property_name,
4733 getter_tpl,
4734 setter_tpl,
4735 attributes,
4736 v8::AccessControl::DEFAULT);
4737 } else if (p->method != nullptr) {
4738 v8::Local<v8::FunctionTemplate> t;
4739 if (p->attributes & JSVM_NO_RECEIVER_CHECK) {
4740 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
4741 env, p->method, &t));
4742 } else {
4743 STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(
4744 env, p->method, &t, v8::Signature::New(isolate, tpl)));
4745 }
4746
4747 tpl->PrototypeTemplate()->Set(property_name, t, attributes);
4748 } else {
4749 v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value);
4750 tpl->PrototypeTemplate()->Set(property_name, value, attributes);
4751 }
4752 }
4753
4754 /* register property handler for instance object */
4755 v8impl::JSVM_PropertyHandlerCfgStruct* propertyHandleCfg = v8impl::CreatePropertyCfg(env, propertyHandlerCfg);
4756 if (propertyHandleCfg == nullptr) {
4757 return JSVM_Status::JSVM_GENERIC_FAILURE;
4758 }
4759 v8::Local<v8::Value> cbdata = v8impl::CallbackBundle::New(env, propertyHandleCfg);
4760
4761 // register named property handler
4762 v8::NamedPropertyHandlerConfiguration namedPropertyHandler;
4763 if (propertyHandlerCfg->genericNamedPropertyGetterCallback) {
4764 namedPropertyHandler.getter = v8impl::PropertyCallbackWrapper<v8::Value>::NameGetterInvoke;
4765 }
4766 if (propertyHandlerCfg->genericNamedPropertySetterCallback) {
4767 namedPropertyHandler.setter = v8impl::PropertyCallbackWrapper<v8::Value>::NameSetterInvoke;
4768 }
4769 if (propertyHandlerCfg->genericNamedPropertyDeleterCallback) {
4770 namedPropertyHandler.deleter = v8impl::PropertyCallbackWrapper<v8::Boolean>::NameDeleterInvoke;
4771 }
4772 if (propertyHandlerCfg->genericNamedPropertyEnumeratorCallback) {
4773 namedPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper<v8::Array>::NameEnumeratorInvoke;
4774 }
4775 namedPropertyHandler.data = cbdata;
4776 tpl->InstanceTemplate()->SetHandler(namedPropertyHandler);
4777
4778 // register indexed property handle
4779 v8::IndexedPropertyHandlerConfiguration indexPropertyHandler;
4780 if (propertyHandlerCfg->genericIndexedPropertyGetterCallback) {
4781 indexPropertyHandler.getter = v8impl::PropertyCallbackWrapper<v8::Value>::IndexGetterInvoke;
4782 }
4783 if (propertyHandlerCfg->genericIndexedPropertySetterCallback) {
4784 indexPropertyHandler.setter = v8impl::PropertyCallbackWrapper<v8::Value>::IndexSetterInvoke;
4785 }
4786 if (propertyHandlerCfg->genericIndexedPropertyDeleterCallback) {
4787 indexPropertyHandler.deleter = v8impl::PropertyCallbackWrapper<v8::Boolean>::IndexDeleterInvoke;
4788 }
4789 if (propertyHandlerCfg->genericIndexedPropertyEnumeratorCallback) {
4790 indexPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper<v8::Array>::IndexEnumeratorInvoke;
4791 }
4792 indexPropertyHandler.data = cbdata;
4793 tpl->InstanceTemplate()->SetHandler(indexPropertyHandler);
4794
4795 // register call as function
4796 if (callAsFunctionCallback && callAsFunctionCallback->callback) {
4797 v8::Local<v8::Value> funcCbdata = v8impl::CallbackBundle::New(env, callAsFunctionCallback);
4798 tpl->InstanceTemplate()->SetCallAsFunctionHandler(v8impl::FunctionCallbackWrapper::Invoke, funcCbdata);
4799 }
4800
4801 v8::Local<v8::Context> context = env->context();
4802 *result = v8impl::JsValueFromV8LocalValue(
4803 scope.Escape(tpl->GetFunction(context).ToLocalChecked()));
4804
4805 v8impl::Reference::New(env, v8impl::V8LocalValueFromJsValue(*result), 0, v8impl::Ownership::kRuntime,
4806 v8impl::CfgFinalizedCallback, propertyHandleCfg, nullptr);
4807
4808 if (static_property_count > 0) {
4809 std::vector<JSVM_PropertyDescriptor> static_descriptors;
4810 static_descriptors.reserve(static_property_count);
4811
4812 for (size_t i = 0; i < propertyCount; i++) {
4813 const JSVM_PropertyDescriptor* p = properties + i;
4814 if ((p->attributes & JSVM_STATIC) != 0) {
4815 static_descriptors.push_back(*p);
4816 }
4817 }
4818
4819 STATUS_CALL(OH_JSVM_DefineProperties(
4820 env, *result, static_descriptors.size(), static_descriptors.data()));
4821 }
4822
4823 return GET_RETURN_STATUS(env);
4824 }
4825
OH_JSVM_IsLocked(JSVM_Env env,bool * isLocked)4826 JSVM_Status JSVM_CDECL OH_JSVM_IsLocked(JSVM_Env env,
4827 bool* isLocked) {
4828 CHECK_ENV(env);
4829 CHECK_ARG(env, isLocked);
4830
4831 *isLocked = v8::Locker::IsLocked(env->isolate);
4832
4833 return jsvm_clear_last_error(env);
4834 }
4835
OH_JSVM_AcquireLock(JSVM_Env env)4836 JSVM_Status JSVM_CDECL OH_JSVM_AcquireLock(JSVM_Env env) {
4837 CHECK_ENV(env);
4838
4839 bool isLocked = v8::Locker::IsLocked(env->isolate);
4840 if (!isLocked) {
4841 env->locker = new v8::Locker(env->isolate);
4842 }
4843
4844 return jsvm_clear_last_error(env);
4845 }
4846
OH_JSVM_ReleaseLock(JSVM_Env env)4847 JSVM_Status JSVM_CDECL OH_JSVM_ReleaseLock(JSVM_Env env) {
4848 CHECK_ENV(env);
4849
4850 bool isLocked = v8::Locker::IsLocked(env->isolate);
4851 if (isLocked && env->locker != nullptr) {
4852 delete env->locker;
4853 env->locker = nullptr;
4854 }
4855
4856 return jsvm_clear_last_error(env);
4857 }
4858
OH_JSVM_IsCallable(JSVM_Env env,JSVM_Value value,bool * isCallable)4859 JSVM_Status JSVM_CDECL OH_JSVM_IsCallable(JSVM_Env env,
4860 JSVM_Value value,
4861 bool* isCallable) {
4862 CHECK_ENV(env);
4863 CHECK_ARG(env, value);
4864 CHECK_ARG(env, isCallable);
4865
4866 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4867
4868 *isCallable = val->IsFunction();
4869 return jsvm_clear_last_error(env);
4870 }
4871
OH_JSVM_IsUndefined(JSVM_Env env,JSVM_Value value,bool * isUndefined)4872 JSVM_Status JSVM_CDECL OH_JSVM_IsUndefined(JSVM_Env env,
4873 JSVM_Value value,
4874 bool* isUndefined) {
4875 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
4876 // calls here cannot throw JS exceptions.
4877 CHECK_ENV(env);
4878 CHECK_ARG(env, value);
4879 CHECK_ARG(env, isUndefined);
4880
4881 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4882 *isUndefined = val->IsUndefined();
4883
4884 return jsvm_clear_last_error(env);
4885 }
4886
OH_JSVM_IsNull(JSVM_Env env,JSVM_Value value,bool * isNull)4887 JSVM_Status JSVM_CDECL OH_JSVM_IsNull(JSVM_Env env,
4888 JSVM_Value value,
4889 bool* isNull) {
4890 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
4891 // calls here cannot throw JS exceptions.
4892 CHECK_ENV(env);
4893 CHECK_ARG(env, value);
4894 CHECK_ARG(env, isNull);
4895
4896 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4897 *isNull = val->IsNull();
4898
4899 return jsvm_clear_last_error(env);
4900 }
4901
OH_JSVM_IsNullOrUndefined(JSVM_Env env,JSVM_Value value,bool * isNullOrUndefined)4902 JSVM_Status JSVM_CDECL OH_JSVM_IsNullOrUndefined(JSVM_Env env,
4903 JSVM_Value value,
4904 bool* isNullOrUndefined) {
4905 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
4906 // calls here cannot throw JS exceptions.
4907 CHECK_ENV(env);
4908 CHECK_ARG(env, value);
4909 CHECK_ARG(env, isNullOrUndefined);
4910
4911 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4912 *isNullOrUndefined = val->IsNullOrUndefined();
4913
4914 return jsvm_clear_last_error(env);
4915 }
4916
OH_JSVM_IsBoolean(JSVM_Env env,JSVM_Value value,bool * isBoolean)4917 JSVM_Status JSVM_CDECL OH_JSVM_IsBoolean(JSVM_Env env,
4918 JSVM_Value value,
4919 bool* isBoolean) {
4920 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
4921 // calls here cannot throw JS exceptions.
4922 CHECK_ENV(env);
4923 CHECK_ARG(env, value);
4924 CHECK_ARG(env, isBoolean);
4925
4926 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4927 *isBoolean = val->IsBoolean();
4928
4929 return jsvm_clear_last_error(env);
4930 }
4931
OH_JSVM_IsNumber(JSVM_Env env,JSVM_Value value,bool * isNumber)4932 JSVM_Status JSVM_CDECL OH_JSVM_IsNumber(JSVM_Env env,
4933 JSVM_Value value,
4934 bool* isNumber) {
4935 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
4936 // calls here cannot throw JS exceptions.
4937 CHECK_ENV(env);
4938 CHECK_ARG(env, value);
4939 CHECK_ARG(env, isNumber);
4940
4941 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4942 *isNumber = val->IsNumber();
4943
4944 return jsvm_clear_last_error(env);
4945 }
4946
OH_JSVM_IsString(JSVM_Env env,JSVM_Value value,bool * isString)4947 JSVM_Status JSVM_CDECL OH_JSVM_IsString(JSVM_Env env,
4948 JSVM_Value value,
4949 bool* isString) {
4950 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
4951 // calls here cannot throw JS exceptions.
4952 CHECK_ENV(env);
4953 CHECK_ARG(env, value);
4954 CHECK_ARG(env, isString);
4955
4956 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4957 *isString = val->IsString();
4958
4959 return jsvm_clear_last_error(env);
4960 }
4961
OH_JSVM_IsSymbol(JSVM_Env env,JSVM_Value value,bool * isSymbol)4962 JSVM_Status JSVM_CDECL OH_JSVM_IsSymbol(JSVM_Env env,
4963 JSVM_Value value,
4964 bool* isSymbol) {
4965 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
4966 // calls here cannot throw JS exceptions.
4967 CHECK_ENV(env);
4968 CHECK_ARG(env, value);
4969 CHECK_ARG(env, isSymbol);
4970
4971 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4972 *isSymbol = val->IsSymbol();
4973
4974 return jsvm_clear_last_error(env);
4975 }
4976
OH_JSVM_IsFunction(JSVM_Env env,JSVM_Value value,bool * isFunction)4977 JSVM_Status JSVM_CDECL OH_JSVM_IsFunction(JSVM_Env env,
4978 JSVM_Value value,
4979 bool* isFunction) {
4980 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
4981 // calls here cannot throw JS exceptions.
4982 CHECK_ENV(env);
4983 CHECK_ARG(env, value);
4984 CHECK_ARG(env, isFunction);
4985
4986 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
4987 *isFunction = val->IsFunction();
4988
4989 return jsvm_clear_last_error(env);
4990 }
4991
OH_JSVM_IsObject(JSVM_Env env,JSVM_Value value,bool * isObject)4992 JSVM_Status JSVM_CDECL OH_JSVM_IsObject(JSVM_Env env,
4993 JSVM_Value value,
4994 bool* isObject) {
4995 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
4996 // calls here cannot throw JS exceptions.
4997 CHECK_ENV(env);
4998 CHECK_ARG(env, value);
4999 CHECK_ARG(env, isObject);
5000
5001 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
5002 *isObject = val->IsObject();
5003
5004 return jsvm_clear_last_error(env);
5005 }
5006
OH_JSVM_IsBigInt(JSVM_Env env,JSVM_Value value,bool * isBigInt)5007 JSVM_Status JSVM_CDECL OH_JSVM_IsBigInt(JSVM_Env env,
5008 JSVM_Value value,
5009 bool* isBigInt) {
5010 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
5011 // calls here cannot throw JS exceptions.
5012 CHECK_ENV(env);
5013 CHECK_ARG(env, value);
5014 CHECK_ARG(env, isBigInt);
5015
5016 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
5017 *isBigInt = val->IsBigInt();
5018
5019 return jsvm_clear_last_error(env);
5020 }
5021
OH_JSVM_CreateSet(JSVM_Env env,JSVM_Value * result)5022 JSVM_Status JSVM_CDECL OH_JSVM_CreateSet(JSVM_Env env,
5023 JSVM_Value* result) {
5024 CHECK_ENV(env);
5025 CHECK_ARG(env, result);
5026
5027 *result = v8impl::JsValueFromV8LocalValue(v8::Set::New(env->isolate));
5028
5029 return jsvm_clear_last_error(env);
5030 }
5031
OH_JSVM_IsSet(JSVM_Env env,JSVM_Value value,bool * isSet)5032 JSVM_Status JSVM_CDECL OH_JSVM_IsSet(JSVM_Env env,
5033 JSVM_Value value,
5034 bool* isSet) {
5035 CHECK_ENV(env);
5036 CHECK_ARG(env, value);
5037 CHECK_ARG(env, isSet);
5038
5039 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
5040 *isSet = val->IsSet();
5041
5042 return jsvm_clear_last_error(env);
5043 }
5044
OH_JSVM_ObjectGetPrototypeOf(JSVM_Env env,JSVM_Value object,JSVM_Value * result)5045 JSVM_Status JSVM_CDECL OH_JSVM_ObjectGetPrototypeOf(JSVM_Env env,
5046 JSVM_Value object,
5047 JSVM_Value* result) {
5048 JSVM_PREAMBLE(env);
5049 CHECK_ARG(env, result);
5050
5051 v8::Local<v8::Context> context = env->context();
5052
5053 v8::Local<v8::Object> obj;
5054 CHECK_TO_OBJECT(env, context, obj, object);
5055
5056 v8::Local<v8::Value> val = obj->GetPrototypeV2();
5057 *result = v8impl::JsValueFromV8LocalValue(val);
5058 return GET_RETURN_STATUS(env);
5059 }
5060
OH_JSVM_ObjectSetPrototypeOf(JSVM_Env env,JSVM_Value object,JSVM_Value prototype)5061 JSVM_Status JSVM_CDECL OH_JSVM_ObjectSetPrototypeOf(JSVM_Env env,
5062 JSVM_Value object,
5063 JSVM_Value prototype) {
5064 JSVM_PREAMBLE(env);
5065 CHECK_ARG(env, prototype);
5066
5067 v8::Local<v8::Context> context = env->context();
5068
5069 v8::Local<v8::Object> obj;
5070 CHECK_TO_OBJECT(env, context, obj, object);
5071
5072 v8::Local<v8::Value> type = v8impl::V8LocalValueFromJsValue(prototype);
5073 RETURN_STATUS_IF_FALSE(env, type->IsObject(), JSVM_INVALID_ARG);
5074 v8::Maybe<bool> set_maybe = obj->SetPrototypeV2(context, type);
5075
5076 RETURN_STATUS_IF_FALSE(env, set_maybe.FromMaybe(false), JSVM_GENERIC_FAILURE);
5077 return GET_RETURN_STATUS(env);
5078 }
5079
OH_JSVM_RetainScript(JSVM_Env env,JSVM_Script script)5080 JSVM_Status JSVM_CDECL OH_JSVM_RetainScript(JSVM_Env env, JSVM_Script script) {
5081 CHECK_ENV(env);
5082 auto jsvmData = reinterpret_cast<JSVM_Data__ *>(script);
5083
5084 RETURN_STATUS_IF_FALSE(env, jsvmData && !jsvmData->isGlobal, JSVM_INVALID_ARG);
5085
5086 jsvmData->taggedPointer = v8::Global<v8::Script>(
5087 env->isolate, jsvmData->ToV8Local<v8::Script>(env->isolate));
5088
5089 jsvmData->isGlobal = true;
5090 return jsvm_clear_last_error(env);
5091 }
5092
OH_JSVM_ReleaseScript(JSVM_Env env,JSVM_Script script)5093 JSVM_Status JSVM_CDECL OH_JSVM_ReleaseScript(JSVM_Env env, JSVM_Script script) {
5094 CHECK_ENV(env);
5095 auto jsvmData = reinterpret_cast<JSVM_Data__ *>(script);
5096
5097 RETURN_STATUS_IF_FALSE(env, jsvmData && jsvmData->isGlobal, JSVM_INVALID_ARG);
5098
5099 std::get<v8::Global<v8::Script>>(jsvmData->taggedPointer).Reset();
5100 delete jsvmData;
5101 return jsvm_clear_last_error(env);
5102 }
5103
FindAvailablePort()5104 int FindAvailablePort() {
5105 constexpr int startPort = 9229;
5106 constexpr int endPort = 9999;
5107 constexpr int invalidPort = -1;
5108 int sockfd = -1;
5109
5110 for (auto port = startPort; port <= endPort; ++port) {
5111 sockfd = socket(AF_INET, SOCK_STREAM, 0);
5112 if (sockfd < 0) {
5113 continue;
5114 }
5115 struct sockaddr_in addr;
5116 addr.sin_family = AF_INET;
5117 addr.sin_addr.s_addr = htonl(INADDR_ANY);
5118 addr.sin_port = htons(port);
5119
5120 if (bind(sockfd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
5121 close(sockfd);
5122 if (errno == EADDRINUSE) {
5123 continue;
5124 } else {
5125 break;
5126 }
5127 }
5128 close(sockfd);
5129 return port;
5130 }
5131 return invalidPort;
5132 }
5133
OH_JSVM_OpenInspectorWithName(JSVM_Env env,int pid,const char * name)5134 JSVM_Status JSVM_CDECL OH_JSVM_OpenInspectorWithName(JSVM_Env env,
5135 int pid,
5136 const char* name) {
5137 JSVM_PREAMBLE(env);
5138 RETURN_STATUS_IF_FALSE(env, !name || strlen(name) < SIZE_MAX, JSVM_INVALID_ARG);
5139 RETURN_STATUS_IF_FALSE(env, pid >= 0, JSVM_INVALID_ARG);
5140 std::string path(name ? name : "jsvm");
5141 auto port = FindAvailablePort();
5142 auto hostPort =
5143 std::make_shared<node::ExclusiveAccess<node::HostPort>>("localhost", port, pid);
5144 env->inspector_agent()->Start(path, hostPort, true, false);
5145 return GET_RETURN_STATUS(env);
5146 }
5147
OH_JSVM_IsConstructor(JSVM_Env env,JSVM_Value value,bool * isConstructor)5148 JSVM_Status JSVM_CDECL OH_JSVM_IsConstructor(JSVM_Env env,
5149 JSVM_Value value,
5150 bool* isConstructor)
5151 {
5152 CHECK_ENV(env);
5153 CHECK_ARG(env, value);
5154 CHECK_ARG(env, isConstructor);
5155
5156 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
5157 if (!val->IsObject()) {
5158 *isConstructor = false;
5159 return jsvm_clear_last_error(env);
5160 }
5161 v8::Local<v8::Object> obj = val.As<v8::Object>();
5162 *isConstructor = obj->IsConstructor();
5163
5164 return jsvm_clear_last_error(env);
5165 }
5166
OH_JSVM_CreateRegExp(JSVM_Env env,JSVM_Value value,JSVM_RegExpFlags flags,JSVM_Value * result)5167 JSVM_Status JSVM_CDECL OH_JSVM_CreateRegExp(JSVM_Env env,
5168 JSVM_Value value,
5169 JSVM_RegExpFlags flags,
5170 JSVM_Value* result)
5171 {
5172 JSVM_PREAMBLE(env);
5173 CHECK_ARG(env, value);
5174 CHECK_ARG(env, result);
5175
5176 v8::Local<v8::Value> pattern = v8impl::V8LocalValueFromJsValue(value);
5177 RETURN_STATUS_IF_FALSE(env, pattern->IsString(), JSVM_STRING_EXPECTED);
5178 v8::Local<v8::Context> context = env->context();
5179 v8::MaybeLocal<v8::RegExp> regExp = v8::RegExp::New(context, pattern.As<v8::String>(),
5180 static_cast<v8::RegExp::Flags>(flags));
5181 CHECK_MAYBE_EMPTY(env, regExp, JSVM_GENERIC_FAILURE);
5182 *result = v8impl::JsValueFromV8LocalValue(regExp.ToLocalChecked());
5183
5184 return GET_RETURN_STATUS(env);
5185 }
5186
OH_JSVM_CreateMap(JSVM_Env env,JSVM_Value * result)5187 JSVM_Status JSVM_CDECL OH_JSVM_CreateMap(JSVM_Env env, JSVM_Value* result)
5188 {
5189 CHECK_ENV(env);
5190 CHECK_ARG(env, result);
5191
5192 *result = v8impl::JsValueFromV8LocalValue(v8::Map::New(env->isolate));
5193
5194 return jsvm_clear_last_error(env);
5195 }
5196
OH_JSVM_IsMap(JSVM_Env env,JSVM_Value value,bool * isMap)5197 JSVM_Status JSVM_CDECL OH_JSVM_IsMap(JSVM_Env env,
5198 JSVM_Value value,
5199 bool* isMap)
5200 {
5201 CHECK_ENV(env);
5202 CHECK_ARG(env, value);
5203 CHECK_ARG(env, isMap);
5204
5205 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
5206
5207 *isMap = val->IsMap();
5208 return jsvm_clear_last_error(env);
5209 }
5210
OH_JSVM_CompileWasmModule(JSVM_Env env,const uint8_t * wasmBytecode,size_t wasmBytecodeLength,const uint8_t * cacheData,size_t cacheDataLength,bool * cacheRejected,JSVM_Value * wasmModule)5211 JSVM_Status JSVM_CDECL OH_JSVM_CompileWasmModule(JSVM_Env env,
5212 const uint8_t *wasmBytecode,
5213 size_t wasmBytecodeLength,
5214 const uint8_t *cacheData,
5215 size_t cacheDataLength,
5216 bool *cacheRejected,
5217 JSVM_Value *wasmModule) {
5218 JSVM_PREAMBLE(env);
5219 CHECK_ARG(env, wasmBytecode);
5220 RETURN_STATUS_IF_FALSE(env, wasmBytecodeLength > 0, JSVM_INVALID_ARG);
5221 v8::MaybeLocal<v8::WasmModuleObject> maybe_module;
5222 if (cacheData == nullptr) {
5223 maybe_module = v8::WasmModuleObject::Compile(env->isolate, {wasmBytecode, wasmBytecodeLength});
5224 } else {
5225 RETURN_STATUS_IF_FALSE(env, cacheDataLength > 0, JSVM_INVALID_ARG);
5226 bool rejected;
5227 maybe_module = v8::WasmModuleObject::DeserializeOrCompile(
5228 env->isolate, {wasmBytecode, wasmBytecodeLength}, {cacheData, cacheDataLength}, rejected);
5229 if (cacheRejected != nullptr) {
5230 *cacheRejected = rejected;
5231 }
5232 }
5233 // To avoid the status code caused by exception being override, check exception once v8 API finished
5234 if (try_catch.HasCaught()) {
5235 return jsvm_set_last_error(env, JSVM_PENDING_EXCEPTION);
5236 }
5237 CHECK_MAYBE_EMPTY(env, maybe_module, JSVM_GENERIC_FAILURE);
5238 *wasmModule = v8impl::JsValueFromV8LocalValue(maybe_module.ToLocalChecked());
5239 return jsvm_clear_last_error(env);
5240 }
5241
OH_JSVM_CompileWasmFunction(JSVM_Env env,JSVM_Value wasmModule,uint32_t functionIndex,JSVM_WasmOptLevel optLevel)5242 JSVM_Status JSVM_CDECL OH_JSVM_CompileWasmFunction(JSVM_Env env,
5243 JSVM_Value wasmModule,
5244 uint32_t functionIndex,
5245 JSVM_WasmOptLevel optLevel) {
5246 JSVM_PREAMBLE(env);
5247 CHECK_ARG(env, wasmModule);
5248 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(wasmModule);
5249 RETURN_STATUS_IF_FALSE(env, val->IsWasmModuleObject(), JSVM_INVALID_ARG);
5250
5251 v8::Local<v8::WasmModuleObject> v8WasmModule = val.As<v8::WasmModuleObject>();
5252 v8::WasmExecutionTier tier = v8::WasmExecutionTier::kNone;
5253 if (optLevel == JSVM_WASM_OPT_BASELINE) {
5254 // v8 liftoff has bug, keep BASELINE same as HIGH.
5255 tier = v8::WasmExecutionTier::kTurbofan;
5256 } else if (optLevel == JSVM_WASM_OPT_HIGH) {
5257 tier = v8::WasmExecutionTier::kTurbofan;
5258 } else {
5259 // Unsupported optLevel
5260 return jsvm_set_last_error(env, JSVM_INVALID_ARG);
5261 }
5262 bool compileSuccess = v8WasmModule->CompileFunction(env->isolate, functionIndex, tier);
5263 // To avoid the status code caused by exception being override, check exception once v8 API finished
5264 if (try_catch.HasCaught()) {
5265 return jsvm_set_last_error(env, JSVM_PENDING_EXCEPTION);
5266 }
5267 RETURN_STATUS_IF_FALSE(env, compileSuccess, JSVM_GENERIC_FAILURE);
5268 return jsvm_clear_last_error(env);
5269 }
5270
OH_JSVM_IsWasmModuleObject(JSVM_Env env,JSVM_Value value,bool * result)5271 JSVM_Status JSVM_CDECL OH_JSVM_IsWasmModuleObject(JSVM_Env env,
5272 JSVM_Value value,
5273 bool* result) {
5274 // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8
5275 // calls here cannot throw JS exceptions.
5276 CHECK_ENV(env);
5277 CHECK_ARG(env, value);
5278 CHECK_ARG(env, result);
5279
5280 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
5281 *result = val->IsWasmModuleObject();
5282
5283 return jsvm_clear_last_error(env);
5284 }
5285
OH_JSVM_CreateWasmCache(JSVM_Env env,JSVM_Value wasmModule,const uint8_t ** data,size_t * length)5286 JSVM_Status JSVM_CDECL OH_JSVM_CreateWasmCache(JSVM_Env env,
5287 JSVM_Value wasmModule,
5288 const uint8_t** data,
5289 size_t* length) {
5290 JSVM_PREAMBLE(env);
5291 CHECK_ARG(env, wasmModule);
5292 CHECK_ARG(env, data);
5293 CHECK_ARG(env, length);
5294
5295 v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(wasmModule);
5296 RETURN_STATUS_IF_FALSE(env, val->IsWasmModuleObject(), JSVM_INVALID_ARG);
5297
5298 v8::Local<v8::WasmModuleObject> v8WasmModule = val.As<v8::WasmModuleObject>();
5299 v8::CompiledWasmModule compiledWasmModule = v8WasmModule->GetCompiledModule();
5300 v8::OwnedBuffer serialized_bytes = compiledWasmModule.Serialize();
5301 // To avoid the status code caused by exception being override, check exception once v8 API finished
5302 if (try_catch.HasCaught()) {
5303 return jsvm_set_last_error(env, JSVM_PENDING_EXCEPTION);
5304 }
5305 // If buffer size is 0, create wasm cache failed.
5306 RETURN_STATUS_IF_FALSE(env, serialized_bytes.size > 0, JSVM_GENERIC_FAILURE);
5307 *data = serialized_bytes.buffer.get();
5308 *length = serialized_bytes.size;
5309 // Release the ownership of buffer, OH_JSVM_ReleaseCache must be called explicitly to release the buffer
5310 serialized_bytes.buffer.release();
5311
5312 return GET_RETURN_STATUS(env);
5313 }
5314
OH_JSVM_ReleaseCache(JSVM_Env env,const uint8_t * cacheData,JSVM_CacheType cacheType)5315 JSVM_Status JSVM_CDECL OH_JSVM_ReleaseCache(JSVM_Env env,
5316 const uint8_t* cacheData,
5317 JSVM_CacheType cacheType) {
5318 CHECK_ENV(env);
5319 CHECK_ARG(env, cacheData);
5320 if (cacheType == JSVM_CACHE_TYPE_JS) {
5321 // The release behavior MUST match the memory allocation of OH_JSVM_CreateCodeCache.
5322 delete[] cacheData;
5323 } else if (cacheType == JSVM_CACHE_TYPE_WASM) {
5324 // The release behavior MUST match the memory allocation of OH_JSVM_CreateWasmCache.
5325 delete[] cacheData;
5326 } else {
5327 // Unsupported cacheType
5328 return jsvm_set_last_error(env, JSVM_INVALID_ARG);
5329 }
5330 return jsvm_clear_last_error(env);
5331 }
5332