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