• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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