/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include // INT_MAX #include #include #include #include #include "v8-debug.h" #include "v8-internal.h" #include "v8-local-handle.h" #include "v8-primitive.h" #include "v8-statistics.h" #include "v8-version-string.h" #define JSVM_EXPERIMENTAL #include "js_native_api_v8.h" #include "jsvm.h" #include "jsvm_env.h" #include "jsvm_log.h" #include "jsvm_reference.h" #include "jsvm_util.h" #include "libplatform/libplatform.h" #include "libplatform/v8-tracing.h" #include "platform/platform.h" #include "sourcemap.h" #ifdef V8_USE_PERFETTO #error Unsupported Perfetto. #endif // V8_USE_PERFETTO #define TRACE_DISABLED_BY_DEFAULT(name) "disabled-by-default-" name namespace v8impl { namespace { enum IsolateDataSlot { K_ISOLATE_DATA = 0, K_ISOLATE_SNAPSHOT_CREATOR_SLOT = 1, K_ISOLATE_HANDLER_POOL_SLOT = 2, }; // Always compare the final element of IsolateDataSlot with v8 limit. static_assert(K_ISOLATE_HANDLER_POOL_SLOT < v8::internal::Internals::kNumIsolateDataSlots); struct GCHandlerWrapper { GCHandlerWrapper(JSVM_GCType gcType, JSVM_HandlerForGC handler, void* userData) : gcType(gcType), handler(handler), userData(userData) {} JSVM_GCType gcType; JSVM_HandlerForGC handler; void* userData; }; using GCHandlerWrappers = std::list; struct IsolateHandlerPool { JSVM_HandlerForOOMError handlerForOOMError = nullptr; JSVM_HandlerForFatalError handlerForFatalError = nullptr; JSVM_HandlerForPromiseReject handlerForPromiseReject = nullptr; GCHandlerWrappers handlerWrappersBeforeGC; GCHandlerWrappers handlerWrappersAfterGC; ~IsolateHandlerPool() { for (auto* handler : handlerWrappersBeforeGC) { delete handler; } handlerWrappersBeforeGC.clear(); for (auto* handler : handlerWrappersAfterGC) { delete handler; } handlerWrappersAfterGC.clear(); } }; static IsolateHandlerPool* GetIsolateHandlerPool(v8::Isolate* isolate) { auto pool = isolate->GetData(v8impl::K_ISOLATE_HANDLER_POOL_SLOT); return reinterpret_cast(pool); } static IsolateHandlerPool* GetOrCreateIsolateHandlerPool(v8::Isolate* isolate) { auto* pool = isolate->GetData(v8impl::K_ISOLATE_HANDLER_POOL_SLOT); if (pool != nullptr) { return reinterpret_cast(pool); } auto* createdPool = new v8impl::IsolateHandlerPool(); isolate->SetData(v8impl::K_ISOLATE_HANDLER_POOL_SLOT, createdPool); return createdPool; } enum ContextEmbedderIndex { K_CONTEXT_ENV_INDEX = 1, }; static JSVM_Env GetEnvByContext(v8::Local context) { auto env = context->GetAlignedPointerFromEmbedderData(v8impl::K_CONTEXT_ENV_INDEX); return reinterpret_cast(env); } struct IsolateData { IsolateData(v8::StartupData* blob) : blob(blob) {} ~IsolateData() { delete blob; } v8::StartupData* blob; v8::Eternal typeTagKey; v8::Eternal wrapperKey; }; static void CreateIsolateData(v8::Isolate* isolate, v8::StartupData* blob) { auto data = new IsolateData(blob); v8::Isolate::Scope isolateScope(isolate); v8::HandleScope handleScope(isolate); if (blob) { // NOTE: The order of getting the data must be consistent with the order of // adding data in OH_JSVM_CreateSnapshot. auto wrapperKey = isolate->GetDataFromSnapshotOnce(0); auto typeTagKey = isolate->GetDataFromSnapshotOnce(1); data->wrapperKey.Set(isolate, wrapperKey.ToLocalChecked()); data->typeTagKey.Set(isolate, typeTagKey.ToLocalChecked()); } else { data->wrapperKey.Set(isolate, v8::Private::New(isolate)); data->typeTagKey.Set(isolate, v8::Private::New(isolate)); } isolate->SetData(v8impl::K_ISOLATE_DATA, data); } static IsolateData* GetIsolateData(v8::Isolate* isolate) { auto data = isolate->GetData(v8impl::K_ISOLATE_DATA); return reinterpret_cast(data); } static void SetIsolateSnapshotCreator(v8::Isolate* isolate, v8::SnapshotCreator* creator) { isolate->SetData(v8impl::K_ISOLATE_SNAPSHOT_CREATOR_SLOT, creator); } static v8::SnapshotCreator* GetIsolateSnapshotCreator(v8::Isolate* isolate) { auto data = isolate->GetData(v8impl::K_ISOLATE_SNAPSHOT_CREATOR_SLOT); return reinterpret_cast(data); } static void SetContextEnv(v8::Local context, JSVM_Env env) { context->SetAlignedPointerInEmbedderData(K_CONTEXT_ENV_INDEX, env); } static JSVM_Env GetContextEnv(v8::Local context) { auto data = context->GetAlignedPointerFromEmbedderData(K_CONTEXT_ENV_INDEX); return reinterpret_cast(data); } class OutputStream : public v8::OutputStream { public: OutputStream(JSVM_OutputStream stream, void* data, int chunkSize = 65536) : stream(stream), streamData(data), chunkSize(chunkSize) {} int GetChunkSize() override { return chunkSize; } void EndOfStream() override { stream(nullptr, 0, streamData); } WriteResult WriteAsciiChunk(char* data, const int size) override { return stream(data, size, streamData) ? kContinue : kAbort; } private: JSVM_OutputStream stream; void* streamData; int chunkSize; }; static std::unique_ptr g_platform = v8::platform::NewDefaultPlatform(); static std::vector externalReferenceRegistry; static std::unordered_map sourceMapUrlMap; static std::unique_ptr defaultArrayBufferAllocator; static std::unique_ptr g_trace_stream; constexpr uint32_t TRACE_CATEGORY_COUNT = 7; static constexpr const char* INTERNAL_TRACE_CATEGORIES[] = { "v8", TRACE_DISABLED_BY_DEFAULT("v8.compile"), "v8.execute", TRACE_DISABLED_BY_DEFAULT("v8.runtime"), TRACE_DISABLED_BY_DEFAULT("v8.stack_trace"), "v8.wasm", TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"), }; constexpr uint32_t DEFAULT_CATEGORY_COUNT = 4; static constexpr JSVM_TraceCategory DEFAULT_CATAGORIES[] = { JSVM_TRACE_VM, JSVM_TRACE_EXECUTE, JSVM_TRACE_COMPILE, JSVM_TRACE_RUNTIME }; static v8::ArrayBuffer::Allocator* GetOrCreateDefaultArrayBufferAllocator() { if (!defaultArrayBufferAllocator) { defaultArrayBufferAllocator.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator()); } return defaultArrayBufferAllocator.get(); } static void SetFileToSourceMapMapping(std::string&& file, std::string&& sourceMapUrl) { auto it = sourceMapUrlMap.find(file); if (it == sourceMapUrlMap.end()) { sourceMapUrlMap.emplace(file, sourceMapUrl); return; } auto&& prevSourceMapUrl = it->second; CHECK(prevSourceMapUrl == sourceMapUrl); } static std::string GetSourceMapFromFileName(std::string&& file) { auto it = sourceMapUrlMap.find(file); if (it != sourceMapUrlMap.end()) { return it->second; } return ""; } template JSVM_Status NewString(JSVM_Env env, const CCharType* str, size_t length, JSVM_Value* result, StringMaker stringMaker) { CHECK_NEW_STRING_ARGS(env, str, length, result); auto isolate = env->isolate; auto strMaybe = stringMaker(isolate); CHECK_MAYBE_EMPTY(env, strMaybe, JSVM_GENERIC_FAILURE); *result = v8impl::JsValueFromV8LocalValue(strMaybe.ToLocalChecked()); return ClearLastError(env); } template JSVM_Status NewExternalString(JSVM_Env env, CharType* str, size_t length, JSVM_Finalize finalizeCallback, void* finalizeHint, JSVM_Value* result, bool* copied, CreateAPI create_api, StringMaker string_maker) { CHECK_NEW_STRING_ARGS(env, str, length, result); JSVM_Status status; #if defined(V8_ENABLE_SANDBOX) status = create_api(env, str, length, result); if (status == JSVM_OK) { if (copied != nullptr) { *copied = true; } if (finalizeCallback) { env->CallFinalizer(finalizeCallback, static_cast(str), finalizeHint); } } #else status = NewString(env, str, length, result, string_maker); if (status == JSVM_OK && copied != nullptr) { *copied = false; } #endif // V8_ENABLE_SANDBOX return status; } inline JSVM_Status V8NameFromPropertyDescriptor(JSVM_Env env, const JSVM_PropertyDescriptor* p, v8::Local* result) { if (p->utf8name != nullptr) { CHECK_NEW_FROM_UTF8(env, *result, p->utf8name); } else { v8::Local propertyValue = v8impl::V8LocalValueFromJsValue(p->name); RETURN_STATUS_IF_FALSE(env, propertyValue->IsName(), JSVM_NAME_EXPECTED); *result = propertyValue.As(); } return JSVM_OK; } // convert from jsvm-api property attributes to v8::PropertyAttribute v8::PropertyAttribute V8PropertyAttributesFromDescriptor(const JSVM_PropertyDescriptor* descriptor) { unsigned int attributeFlags = v8::PropertyAttribute::None; // The JSVM_WRITABLE attribute is ignored for accessor descriptors, but // V8 would throw `TypeError`s on assignment with nonexistence of a setter. if ((descriptor->getter == nullptr && descriptor->setter == nullptr) && (descriptor->attributes & JSVM_WRITABLE) == 0) { attributeFlags |= v8::PropertyAttribute::ReadOnly; } if ((descriptor->attributes & JSVM_ENUMERABLE) == 0) { attributeFlags |= v8::PropertyAttribute::DontEnum; } if ((descriptor->attributes & JSVM_CONFIGURABLE) == 0) { attributeFlags |= v8::PropertyAttribute::DontDelete; } return static_cast(attributeFlags); } template inline v8::Maybe ConcludeDeferred(JSVM_Env env, JSVM_Deferred deferred, JSVM_Value result) { v8::Local context = env->context(); v8impl::Persistent* deferredRef = PersistentFromJsDeferred(deferred); v8::Local v8Deferred = v8::Local::New(env->isolate, *deferredRef); auto resolver = v8Deferred.As(); v8::Maybe success = isResolved ? resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) : resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result)); delete deferredRef; return success; } enum UnwrapAction { KEEP_WRAP, REMOVE_WRAP }; JSVM_Status Unwrap(JSVM_Env env, JSVM_Value jsObject, void** result, UnwrapAction action) { JSVM_PREAMBLE(env); CHECK_ARG(env, jsObject); if (action == KEEP_WRAP) { CHECK_ARG(env, result); } v8::Local context = env->context(); v8::Local value = v8impl::V8LocalValueFromJsValue(jsObject); RETURN_STATUS_IF_FALSE(env, value->IsObject(), JSVM_INVALID_ARG); v8::Local obj = value.As(); auto val = obj->GetPrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper)).ToLocalChecked(); RETURN_STATUS_IF_FALSE(env, val->IsExternal(), JSVM_INVALID_ARG); RuntimeReference* reference = static_cast(val.As()->Value()); if (result) { *result = reference->GetData(); } if (action == REMOVE_WRAP) { CHECK(obj->DeletePrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper)).FromJust()); v8impl::RuntimeReference::DeleteReference(reference); } return GET_RETURN_STATUS(env); } //=== Function JSVM_Callback wrapper ================================= // Use this data structure to associate callback data with each N-API function // exposed to JavaScript. The structure is stored in a v8::External which gets // passed into our callback wrapper. This reduces the performance impact of // calling through N-API. // Ref: benchmark/misc/function_call // Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072 class CallbackBundle { public: // Creates an object to be made available to the static function callback // wrapper, used to retrieve the native callback function and data pointer. static inline v8::Local New(JSVM_Env env, JSVM_Callback cb) { return v8::External::New(env->isolate, cb); } static inline v8::Local New(JSVM_Env env, v8impl::JSVM_PropertyHandlerCfgStruct* cb) { return v8::External::New(env->isolate, cb); } }; // Base class extended by classes that wrap V8 function and property callback // info. class CallbackWrapper { public: inline CallbackWrapper(JSVM_Value thisArg, size_t argsLength, void* data) : receiver(thisArg), argsLength(argsLength), data(data) {} virtual JSVM_Value GetNewTarget() = 0; virtual void GetArgs(JSVM_Value* buffer, size_t bufferLength) = 0; virtual void SetReturnValue(JSVM_Value value) = 0; JSVM_Value This() { return receiver; } size_t ArgsLength() { return argsLength; } void* Data() { return data; } protected: const JSVM_Value receiver; const size_t argsLength; void* data; }; class CallbackWrapperBase : public CallbackWrapper { public: inline CallbackWrapperBase(const v8::FunctionCallbackInfo& cbinfo, const size_t argsLength) : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()), argsLength, nullptr), cbinfo(cbinfo) { cb = static_cast(cbinfo.Data().As()->Value()); data = cb->data; } protected: inline const v8::FunctionCallbackInfo& GetCbInfo() { return cbinfo; } void InvokeCallback() { JSVM_CallbackInfo cbinfoWrapper = reinterpret_cast(static_cast(this)); // All other pointers we need are stored in `_bundle` auto context = cbinfo.GetIsolate()->GetCurrentContext(); auto env = v8impl::GetContextEnv(context); auto func = cb->callback; JSVM_Value result = nullptr; bool exceptionOccurred = false; env->CallIntoModule([&](JSVM_Env env) { result = func(env, cbinfoWrapper); }, [&](JSVM_Env env, v8::Local value) { exceptionOccurred = true; if (env->IsTerminatedOrTerminating()) { return; } env->isolate->ThrowException(value); }); if (!exceptionOccurred && (result != nullptr)) { this->SetReturnValue(result); } } private: const v8::FunctionCallbackInfo& cbinfo; JSVM_Callback cb; }; class FunctionCallbackWrapper : public CallbackWrapperBase { public: static void Invoke(const v8::FunctionCallbackInfo& info) { FunctionCallbackWrapper cbwrapper(info); cbwrapper.InvokeCallback(); } static inline JSVM_Status NewFunction(JSVM_Env env, JSVM_Callback cb, v8::Local* result) { v8::Local cbdata = v8impl::CallbackBundle::New(env, cb); RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), JSVM_GENERIC_FAILURE); v8::MaybeLocal maybeFunction = v8::Function::New(env->context(), Invoke, cbdata); CHECK_MAYBE_EMPTY(env, maybeFunction, JSVM_GENERIC_FAILURE); *result = maybeFunction.ToLocalChecked(); return ClearLastError(env); } static inline JSVM_Status NewTemplate(JSVM_Env env, JSVM_Callback cb, v8::Local* result, v8::Local sig = v8::Local()) { v8::Local cbdata = v8impl::CallbackBundle::New(env, cb); RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), JSVM_GENERIC_FAILURE); *result = v8::FunctionTemplate::New(env->isolate, Invoke, cbdata, sig); return ClearLastError(env); } explicit FunctionCallbackWrapper(const v8::FunctionCallbackInfo& cbinfo) : CallbackWrapperBase(cbinfo, cbinfo.Length()) {} JSVM_Value GetNewTarget() override { if (GetCbInfo().IsConstructCall()) { return v8impl::JsValueFromV8LocalValue(GetCbInfo().NewTarget()); } else { return nullptr; } } /*virtual*/ void GetArgs(JSVM_Value* buffer, size_t bufferLength) override { size_t i = 0; size_t min = std::min(bufferLength, argsLength); const auto& info = GetCbInfo(); for (; i < min; i += 1) { buffer[i] = v8impl::JsValueFromV8LocalValue(info[i]); } if (i < bufferLength) { JSVM_Value undefined = v8impl::JsValueFromV8LocalValue(v8::Undefined(info.GetIsolate())); for (; i < bufferLength; i += 1) { buffer[i] = undefined; } } } /*virtual*/ void SetReturnValue(JSVM_Value value) override { v8::Local val = v8impl::V8LocalValueFromJsValue(value); GetCbInfo().GetReturnValue().Set(val); } }; template class PropertyCallbackWrapperBase : public CallbackWrapper { public: inline PropertyCallbackWrapperBase(uint32_t index, v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& cbinfo, const size_t argsLength) : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()), argsLength, nullptr), cbinfo(cbinfo), property(property), value(value), index(index) { propertyHandler = static_cast(cbinfo.Data().template As()->Value()); } JSVM_Value GetNewTarget() override { return nullptr; } void GetArgs(JSVM_Value* buffer, size_t bufferlength) override {} protected: void NameSetterInvokeCallback() { auto context = cbinfo.GetIsolate()->GetCurrentContext(); auto env = v8impl::GetContextEnv(context); auto setterCb = propertyHandler->namedSetterCallback; JSVM_Value innerData = nullptr; if (propertyHandler->namedPropertyData != nullptr) { v8impl::UserReference* reference = reinterpret_cast(propertyHandler->namedPropertyData); innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); } bool exceptionOccurred = false; JSVM_Value result = nullptr; JSVM_Value name = JsValueFromV8LocalValue(property); JSVM_Value v8Value = JsValueFromV8LocalValue(value); JSVM_Value thisArg = this->This(); env->CallIntoModule( [&](JSVM_Env env) { if (setterCb) { result = setterCb(env, name, v8Value, thisArg, innerData); } }, [&](JSVM_Env env, v8::Local v8Value) { exceptionOccurred = true; if (env->IsTerminatedOrTerminating()) { return; } env->isolate->ThrowException(v8Value); }); if (!exceptionOccurred && (result != nullptr)) { this->SetReturnValue(result); } } void NameGetterInvokeCallback() { auto context = cbinfo.GetIsolate()->GetCurrentContext(); auto env = v8impl::GetContextEnv(context); auto getterCb = propertyHandler->namedGetterCallback; JSVM_Value innerData = nullptr; if (propertyHandler->namedPropertyData != nullptr) { v8impl::UserReference* reference = reinterpret_cast(propertyHandler->namedPropertyData); innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); } bool exceptionOccurred = false; JSVM_Value result = nullptr; JSVM_Value name = JsValueFromV8LocalValue(property); JSVM_Value thisArg = this->This(); env->CallIntoModule( [&](JSVM_Env env) { if (getterCb) { result = getterCb(env, name, thisArg, innerData); } }, [&](JSVM_Env env, v8::Local v8Value) { exceptionOccurred = true; if (env->IsTerminatedOrTerminating()) { return; } env->isolate->ThrowException(v8Value); }); if (!exceptionOccurred && (result != nullptr)) { this->SetReturnValue(result); } } void NameDeleterInvokeCallback() { auto context = cbinfo.GetIsolate()->GetCurrentContext(); auto env = v8impl::GetContextEnv(context); auto deleterCb = propertyHandler->nameDeleterCallback; JSVM_Value innerData = nullptr; if (propertyHandler->namedPropertyData != nullptr) { v8impl::UserReference* reference = reinterpret_cast(propertyHandler->namedPropertyData); innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); } bool exceptionOccurred = false; JSVM_Value result = nullptr; JSVM_Value name = JsValueFromV8LocalValue(property); JSVM_Value thisArg = this->This(); env->CallIntoModule( [&](JSVM_Env env) { if (deleterCb) { result = deleterCb(env, name, thisArg, innerData); } }, [&](JSVM_Env env, v8::Local v8Value) { exceptionOccurred = true; if (env->IsTerminatedOrTerminating()) { return; } env->isolate->ThrowException(v8Value); }); if (!exceptionOccurred && (result != nullptr)) { if (v8impl::V8LocalValueFromJsValue(result)->IsBoolean()) { this->SetReturnValue(result); } } } void NameEnumeratorInvokeCallback() { auto context = cbinfo.GetIsolate()->GetCurrentContext(); auto env = v8impl::GetContextEnv(context); auto enumeratorCb = propertyHandler->namedEnumeratorCallback; JSVM_Value innerData = nullptr; if (propertyHandler->namedPropertyData != nullptr) { v8impl::UserReference* reference = reinterpret_cast(propertyHandler->namedPropertyData); innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); } bool exceptionOccurred = false; JSVM_Value result = nullptr; JSVM_Value thisArg = this->This(); env->CallIntoModule( [&](JSVM_Env env) { if (enumeratorCb) { result = enumeratorCb(env, thisArg, innerData); } }, [&](JSVM_Env env, v8::Local v8Value) { exceptionOccurred = true; if (env->IsTerminatedOrTerminating()) { return; } env->isolate->ThrowException(v8Value); }); if (!exceptionOccurred && (result != nullptr)) { if (v8impl::V8LocalValueFromJsValue(result)->IsArray()) { this->SetReturnValue(result); } } } void IndexSetterInvokeCallback() { auto context = cbinfo.GetIsolate()->GetCurrentContext(); auto env = v8impl::GetContextEnv(context); auto indexSetterCb = propertyHandler->indexedSetterCallback; JSVM_Value innerData = nullptr; if (propertyHandler->indexedPropertyData != nullptr) { v8impl::UserReference* reference = reinterpret_cast(propertyHandler->indexedPropertyData); innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); } bool exceptionOccurred = false; JSVM_Value result = nullptr; JSVM_Value v8Index = JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, index)); JSVM_Value v8Value = JsValueFromV8LocalValue(value); JSVM_Value thisArg = this->This(); env->CallIntoModule( [&](JSVM_Env env) { if (indexSetterCb) { result = indexSetterCb(env, v8Index, v8Value, thisArg, innerData); } }, [&](JSVM_Env env, v8::Local v8Value) { exceptionOccurred = true; if (env->IsTerminatedOrTerminating()) { return; } env->isolate->ThrowException(v8Value); }); if (!exceptionOccurred && (result != nullptr)) { this->SetReturnValue(result); } } void IndexGetterInvokeCallback() { auto context = cbinfo.GetIsolate()->GetCurrentContext(); auto env = v8impl::GetContextEnv(context); auto indexGetterCb = propertyHandler->indexedGetterCallback; JSVM_Value innerData = nullptr; if (propertyHandler->indexedPropertyData != nullptr) { v8impl::UserReference* reference = reinterpret_cast(propertyHandler->indexedPropertyData); innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); } JSVM_Value result = nullptr; bool exceptionOccurred = false; JSVM_Value v8Index = JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, index)); JSVM_Value thisArg = this->This(); env->CallIntoModule( [&](JSVM_Env env) { if (indexGetterCb) { result = indexGetterCb(env, v8Index, thisArg, innerData); } }, [&](JSVM_Env env, v8::Local v8Value) { exceptionOccurred = true; if (env->IsTerminatedOrTerminating()) { return; } env->isolate->ThrowException(v8Value); }); if (!exceptionOccurred && (result != nullptr)) { this->SetReturnValue(result); } } void IndexDeleterInvokeCallback() { auto context = cbinfo.GetIsolate()->GetCurrentContext(); auto env = v8impl::GetContextEnv(context); auto indexDeleterCb = propertyHandler->indexedDeleterCallback; JSVM_Value innerData = nullptr; if (propertyHandler->indexedPropertyData != nullptr) { v8impl::UserReference* reference = reinterpret_cast(propertyHandler->indexedPropertyData); innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); } bool exceptionOccurred = false; JSVM_Value result = nullptr; JSVM_Value v8Index = JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, index)); JSVM_Value thisArg = this->This(); env->CallIntoModule( [&](JSVM_Env env) { if (indexDeleterCb) { result = indexDeleterCb(env, v8Index, thisArg, innerData); } }, [&](JSVM_Env env, v8::Local v8Value) { exceptionOccurred = true; if (env->IsTerminatedOrTerminating()) { return; } env->isolate->ThrowException(v8Value); }); if (!exceptionOccurred && (result != nullptr)) { if (v8impl::V8LocalValueFromJsValue(result)->IsBoolean()) { this->SetReturnValue(result); } } } void IndexEnumeratorInvokeCallback() { auto context = cbinfo.GetIsolate()->GetCurrentContext(); auto env = v8impl::GetContextEnv(context); auto enumeratorCb = propertyHandler->indexedEnumeratorCallback; JSVM_Value innerData = nullptr; if (propertyHandler->indexedPropertyData != nullptr) { v8impl::UserReference* reference = reinterpret_cast(propertyHandler->indexedPropertyData); innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); } bool exceptionOccurred = false; JSVM_Value result = nullptr; JSVM_Value thisArg = this->This(); env->CallIntoModule( [&](JSVM_Env env) { if (enumeratorCb) { result = enumeratorCb(env, thisArg, innerData); } }, [&](JSVM_Env env, v8::Local v8Value) { exceptionOccurred = true; if (env->IsTerminatedOrTerminating()) { return; } env->isolate->ThrowException(v8Value); }); if (!exceptionOccurred && (result != nullptr)) { if (v8impl::V8LocalValueFromJsValue(result)->IsArray()) { this->SetReturnValue(result); } } } const v8::PropertyCallbackInfo& cbinfo; JSVM_PropertyHandlerCfgStruct* propertyHandler; v8::Local property; v8::Local value; uint32_t index; }; template class PropertyCallbackWrapper : public PropertyCallbackWrapperBase { public: static void NameSetterInvoke(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& info) { PropertyCallbackWrapper propertyCbWrapper(property, value, info); propertyCbWrapper.NameSetterInvokeCallback(); } static void NameGetterInvoke(v8::Local property, const v8::PropertyCallbackInfo& info) { PropertyCallbackWrapper propertyCbWrapper(property, v8::Local(), info); propertyCbWrapper.NameGetterInvokeCallback(); } static void NameDeleterInvoke(v8::Local property, const v8::PropertyCallbackInfo& info) { PropertyCallbackWrapper propertyCbWrapper(property, v8::Local(), info); propertyCbWrapper.NameDeleterInvokeCallback(); } static void NameEnumeratorInvoke(const v8::PropertyCallbackInfo& info) { PropertyCallbackWrapper propertyCbWrapper(v8::Local(), v8::Local(), info); propertyCbWrapper.NameEnumeratorInvokeCallback(); } static void IndexSetterInvoke(uint32_t index, v8::Local value, const v8::PropertyCallbackInfo& info) { PropertyCallbackWrapper propertyCbWrapper(index, value, info); propertyCbWrapper.IndexSetterInvokeCallback(); } static void IndexGetterInvoke(uint32_t index, const v8::PropertyCallbackInfo& info) { PropertyCallbackWrapper propertyCbWrapper(index, v8::Local(), info); propertyCbWrapper.IndexGetterInvokeCallback(); } static void IndexDeleterInvoke(uint32_t index, const v8::PropertyCallbackInfo& info) { PropertyCallbackWrapper propertyCbWrapper(index, v8::Local(), info); propertyCbWrapper.IndexDeleterInvokeCallback(); } static void IndexEnumeratorInvoke(const v8::PropertyCallbackInfo& info) { PropertyCallbackWrapper propertyCbWrapper(0, v8::Local(), info); propertyCbWrapper.IndexEnumeratorInvokeCallback(); } explicit PropertyCallbackWrapper(v8::Local name, v8::Local value, const v8::PropertyCallbackInfo& cbinfo) : PropertyCallbackWrapperBase(0, name, value, cbinfo, 0), cbinfo(cbinfo) {} explicit PropertyCallbackWrapper(uint32_t index, v8::Local value, const v8::PropertyCallbackInfo& cbinfo) : PropertyCallbackWrapperBase(index, v8::Local(), value, cbinfo, 0), cbinfo(cbinfo) {} /*virtual*/ void SetReturnValue(JSVM_Value value) override { v8::Local val = v8impl::V8LocalValueFromJsValue(value).As(); cbinfo.GetReturnValue().Set(val); } protected: const v8::PropertyCallbackInfo& cbinfo; }; JSVM_Status Wrap(JSVM_Env env, JSVM_Value jsObject, void* nativeObject, JSVM_Finalize finalizeCb, void* finalizeHint, JSVM_Ref* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, jsObject); v8::Local context = env->context(); v8::Local value = v8impl::V8LocalValueFromJsValue(jsObject); RETURN_STATUS_IF_FALSE(env, value->IsObject(), JSVM_INVALID_ARG); v8::Local obj = value.As(); // If we've already wrapped this object, we error out. RETURN_STATUS_IF_FALSE(env, !obj->HasPrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper)).FromJust(), JSVM_INVALID_ARG); auto reference = v8impl::RuntimeReference::New(env, obj, finalizeCb, nativeObject, finalizeHint); if (result != nullptr) { auto* userRef = v8impl::UserReference::New(env, obj, 0); *result = reinterpret_cast(userRef); } CHECK(obj->SetPrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper), v8::External::New(env->isolate, reference)) .FromJust()); return GET_RETURN_STATUS(env); } } // end of anonymous namespace } // end of namespace v8impl v8::Platform* JSVM_Env__::platform() { return v8impl::g_platform.get(); } JSVM_Status OH_JSVM_Init(const JSVM_InitOptions* options) { static std::atomic initialized(false); if (initialized.load()) { return JSVM_GENERIC_FAILURE; } initialized.store(true); OHOS_API_CALL(platform::ohos::WriteHisysevent()); OHOS_API_CALL(platform::ohos::ReportKeyThread(platform::ohos::ThreadRole::IMPORTANT_DISPLAY)); v8::V8::InitializePlatform(v8impl::g_platform.get()); OHOS_API_CALL(platform::ohos::SetSecurityMode()); if (options && options->argc && options->argv) { v8::V8::SetFlagsFromCommandLine(options->argc, options->argv, options->removeFlags); } v8::V8::Initialize(); const auto cb = v8impl::FunctionCallbackWrapper::Invoke; v8impl::externalReferenceRegistry.push_back((intptr_t)cb); if (auto p = options ? options->externalReferences : nullptr) { for (; *p != 0; p++) { v8impl::externalReferenceRegistry.push_back(*p); } } v8impl::externalReferenceRegistry.push_back(0); return JSVM_OK; } JSVM_Status OH_JSVM_GetVM(JSVM_Env env, JSVM_VM* result) { *result = reinterpret_cast(env->isolate); return JSVM_OK; } JSVM_Status OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, JSVM_VM* result) { OHOS_API_CALL(platform::ohos::ReportKeyThread(platform::ohos::ThreadRole::USER_INTERACT)); v8::Isolate::CreateParams createParams; auto externalReferences = v8impl::externalReferenceRegistry.data(); createParams.external_references = externalReferences; v8::StartupData* snapshotBlob = nullptr; if (options && options->snapshotBlobData) { snapshotBlob = new v8::StartupData(); snapshotBlob->data = options->snapshotBlobData; snapshotBlob->raw_size = options->snapshotBlobSize; if (!snapshotBlob->IsValid()) { delete snapshotBlob; return JSVM_INVALID_ARG; } createParams.snapshot_blob = snapshotBlob; } v8::Isolate* isolate; if (options && options->isForSnapshotting) { isolate = v8::Isolate::Allocate(); auto creator = new v8::SnapshotCreator(isolate, externalReferences); v8impl::SetIsolateSnapshotCreator(isolate, creator); } else { createParams.array_buffer_allocator = v8impl::GetOrCreateDefaultArrayBufferAllocator(); isolate = v8::Isolate::New(createParams); } v8impl::CreateIsolateData(isolate, snapshotBlob); *result = reinterpret_cast(isolate); // Create nullptr placeholder isolate->SetData(v8impl::K_ISOLATE_HANDLER_POOL_SLOT, nullptr); return JSVM_OK; } JSVM_Status OH_JSVM_DestroyVM(JSVM_VM vm) { if (!vm) { return JSVM_INVALID_ARG; } auto isolate = reinterpret_cast(vm); auto creator = v8impl::GetIsolateSnapshotCreator(isolate); auto data = v8impl::GetIsolateData(isolate); auto* handlerPool = v8impl::GetIsolateHandlerPool(isolate); if (creator != nullptr) { delete creator; } else { isolate->Dispose(); } if (data != nullptr) { delete data; } if (handlerPool != nullptr) { delete handlerPool; } return JSVM_OK; } JSVM_Status OH_JSVM_OpenVMScope(JSVM_VM vm, JSVM_VMScope* result) { auto isolate = reinterpret_cast(vm); auto scope = new v8::Isolate::Scope(isolate); *result = reinterpret_cast(scope); return JSVM_OK; } JSVM_Status OH_JSVM_CloseVMScope(JSVM_VM vm, JSVM_VMScope scope) { auto v8scope = reinterpret_cast(scope); delete v8scope; return JSVM_OK; } JSVM_Status OH_JSVM_CreateEnv(JSVM_VM vm, size_t propertyCount, const JSVM_PropertyDescriptor* properties, JSVM_Env* result) { auto isolate = reinterpret_cast(vm); auto env = new JSVM_Env__(isolate, JSVM_API_VERSION); v8::HandleScope handleScope(isolate); auto globalTemplate = v8::ObjectTemplate::New(isolate); for (size_t i = 0; i < propertyCount; i++) { const JSVM_PropertyDescriptor* p = properties + i; if ((p->attributes & JSVM_STATIC) != 0) { // Ignore static properties. continue; } v8::Local propertyName = v8::String::NewFromUtf8(isolate, p->utf8name, v8::NewStringType::kInternalized).ToLocalChecked(); v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); if (p->getter != nullptr || p->setter != nullptr) { v8::Local getterTpl; v8::Local setterTpl; if (p->getter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->getter, &getterTpl)); } if (p->setter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->setter, &setterTpl)); } globalTemplate->SetAccessorProperty(propertyName, getterTpl, setterTpl, attributes); } else if (p->method != nullptr) { v8::Local methodTpl; STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &methodTpl)); globalTemplate->Set(propertyName, methodTpl, attributes); } else { v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); globalTemplate->Set(propertyName, value, attributes); } } v8::Local context = v8::Context::New(isolate, nullptr, globalTemplate); env->contextPersistent.Reset(isolate, context); v8impl::SetContextEnv(context, env); *result = env; // The error code is set in constructor function, just return JSVM_OK here. return JSVM_OK; } JSVM_EXTERN JSVM_Status OH_JSVM_CreateEnvFromSnapshot(JSVM_VM vm, size_t index, JSVM_Env* result) { auto isolate = reinterpret_cast(vm); v8::HandleScope handleScope(isolate); auto maybe = v8::Context::FromSnapshot(isolate, index); if (maybe.IsEmpty()) { *result = nullptr; return JSVM_GENERIC_FAILURE; } auto env = new JSVM_Env__(isolate, JSVM_API_VERSION); auto context = maybe.ToLocalChecked(); env->contextPersistent.Reset(isolate, context); v8impl::SetContextEnv(context, env); *result = env; return JSVM_OK; } JSVM_Status OH_JSVM_DestroyEnv(JSVM_Env env) { env->DeleteMe(); return JSVM_OK; } JSVM_Status OH_JSVM_OpenEnvScope(JSVM_Env env, JSVM_EnvScope* result) { auto v8scope = new v8::Context::Scope(env->context()); *result = reinterpret_cast(v8scope); return ClearLastError(env); } JSVM_Status OH_JSVM_CloseEnvScope(JSVM_Env env, JSVM_EnvScope scope) { auto v8scope = reinterpret_cast(scope); delete v8scope; return ClearLastError(env); } JSVM_Status OH_JSVM_CompileScript(JSVM_Env env, JSVM_Value script, const uint8_t* cachedData, size_t cachedDataLength, bool eagerCompile, bool* cacheRejected, JSVM_Script* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, script); CHECK_ARG(env, result); v8::Local v8Script = v8impl::V8LocalValueFromJsValue(script); RETURN_STATUS_IF_FALSE(env, v8Script->IsString(), JSVM_STRING_EXPECTED); v8::Local context = env->context(); v8::ScriptCompiler::CachedData* cache = cachedData ? new v8::ScriptCompiler::CachedData(cachedData, cachedDataLength) : nullptr; v8::ScriptCompiler::Source scriptSource(v8Script.As(), cache); auto option = cache ? v8::ScriptCompiler::kConsumeCodeCache : (eagerCompile ? v8::ScriptCompiler::kEagerCompile : v8::ScriptCompiler::kNoCompileOptions); auto maybeScript = v8::ScriptCompiler::Compile(context, &scriptSource, option); if (cache && cacheRejected) { *cacheRejected = cache->rejected; } CHECK_MAYBE_EMPTY(env, maybeScript, JSVM_GENERIC_FAILURE); v8::Local compiledScript = maybeScript.ToLocalChecked(); *result = reinterpret_cast(env->NewJsvmData(compiledScript)); return GET_RETURN_STATUS(env); } v8::ScriptOrigin CreateScriptOrigin(v8::Isolate* isolate, v8::Local resourceName, v8::ScriptType type) { const int kOptionsLength = 2; const uint32_t kOptionsMagicConstant = 0xF1F2F3F0; auto options = v8::PrimitiveArray::New(isolate, kOptionsLength); options->Set(isolate, 0, v8::Uint32::New(isolate, kOptionsMagicConstant)); options->Set(isolate, 1, resourceName); return v8::ScriptOrigin(isolate, resourceName, 0, 0, false, -1, v8::Local(), false, false, type == v8::ScriptType::kModule, options); } v8::MaybeLocal PrepareStackTraceCallback(v8::Local context, v8::Local error, v8::Local trace) { auto* isolate = context->GetIsolate(); v8::TryCatch tryCatch(isolate); v8::Local moduleName = v8::String::NewFromUtf8(isolate, "sourcemap").ToLocalChecked(); v8::Local moduleSourceString = v8::String::NewFromUtf8(isolate, SourceMapRunner.c_str()).ToLocalChecked(); v8::ScriptOrigin moduleOrigin = CreateScriptOrigin(isolate, moduleName, v8::ScriptType::kClassic); v8::Local moduleContext = v8::Context::New(isolate); v8::ScriptCompiler::Source moduleSource(moduleSourceString, moduleOrigin); auto script = v8::Script::Compile(moduleContext, moduleSourceString, &moduleOrigin).ToLocalChecked(); auto result = script->Run(moduleContext).ToLocalChecked(); auto resultFunc = v8::Local::Cast(result); v8::Local element = trace->Get(context, 0).ToLocalChecked(); std::string fileName = ""; if (element->IsObject()) { auto obj = element->ToObject(context); auto getFileName = v8::String::NewFromUtf8(isolate, "getFileName", v8::NewStringType::kNormal); auto function = obj.ToLocalChecked()->Get(context, getFileName.ToLocalChecked()).ToLocalChecked(); auto lineNumberFunction = v8::Local::Cast(function); auto fileNameObj = lineNumberFunction->Call(context, obj.ToLocalChecked(), 0, {}); fileName = std::string(*v8::String::Utf8Value(isolate, fileNameObj.ToLocalChecked())); } auto&& sourceMapUrl = (!fileName.empty()) ? v8impl::GetSourceMapFromFileName(std::move(fileName)) : ""; std::ifstream sourceMapfile(sourceMapUrl); std::string content = ""; if (sourceMapfile.good()) { std::stringstream buffer; buffer << sourceMapfile.rdbuf(); content = buffer.str(); } return ParseSourceMap(isolate, moduleContext, error, trace, resultFunc, content); } JSVM_Status OH_JSVM_CompileScriptWithOrigin(JSVM_Env env, JSVM_Value script, const uint8_t* cachedData, size_t cachedDataLength, bool eagerCompile, bool* cacheRejected, JSVM_ScriptOrigin* origin, JSVM_Script* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, script); CHECK_ARG(env, result); CHECK_NOT_NULL(origin->resourceName); v8::Local v8Script = v8impl::V8LocalValueFromJsValue(script); RETURN_STATUS_IF_FALSE(env, v8Script->IsString(), JSVM_STRING_EXPECTED); v8::Local context = env->context(); auto* isolate = context->GetIsolate(); if (origin->sourceMapUrl) { v8impl::SetFileToSourceMapMapping(origin->resourceName, origin->sourceMapUrl); isolate->SetPrepareStackTraceCallback(PrepareStackTraceCallback); } auto sourceMapUrl = !origin->sourceMapUrl ? v8::Local() : v8::String::NewFromUtf8(isolate, origin->sourceMapUrl).ToLocalChecked().As(); auto resourceName = v8::String::NewFromUtf8(isolate, origin->resourceName).ToLocalChecked(); v8::ScriptOrigin scriptOrigin(isolate, resourceName, origin->resourceLineOffset, origin->resourceColumnOffset, false, -1, sourceMapUrl); v8::ScriptCompiler::CachedData* cache = cachedData ? new v8::ScriptCompiler::CachedData(cachedData, cachedDataLength) : nullptr; v8::ScriptCompiler::Source scriptSource(v8Script.As(), scriptOrigin, cache); auto option = cache ? v8::ScriptCompiler::kConsumeCodeCache : (eagerCompile ? v8::ScriptCompiler::kEagerCompile : v8::ScriptCompiler::kNoCompileOptions); auto maybeScript = v8::ScriptCompiler::Compile(context, &scriptSource, option); if (cache && cacheRejected) { *cacheRejected = cache->rejected; } CHECK_MAYBE_EMPTY(env, maybeScript, JSVM_GENERIC_FAILURE); v8::Local compiledScript = maybeScript.ToLocalChecked(); *result = reinterpret_cast(env->NewJsvmData(compiledScript)); return GET_RETURN_STATUS(env); } class CompileOptionResolver { public: CompileOptionResolver(size_t length, JSVM_CompileOptions options[], v8::Isolate* isolate) { for (size_t i = 0; i < length; i++) { switch (options[i].id) { case JSVM_COMPILE_MODE: { SetOption(options[i].content.num); break; } case JSVM_COMPILE_CODE_CACHE: { auto cache = static_cast(options[i].content.ptr); cachedData = cache->cache ? new v8::ScriptCompiler::CachedData(cache->cache, cache->length) : nullptr; break; } case JSVM_COMPILE_SCRIPT_ORIGIN: { jsvmOrigin = static_cast(options[i].content.ptr); break; } case JSVM_COMPILE_COMPILE_PROFILE: { profile = static_cast(options[i].content.ptr); break; } case JSVM_COMPILE_ENABLE_SOURCE_MAP: { enableSourceMap = options[i].content.boolean; break; } default: { continue; } } } auto sourceString = jsvmOrigin ? jsvmOrigin->resourceName : "script_" + std::to_string(compileCount++); auto sourceMapPtr = jsvmOrigin && jsvmOrigin->sourceMapUrl ? jsvmOrigin->sourceMapUrl : nullptr; auto sourceMapUrl = (jsvmOrigin && jsvmOrigin->sourceMapUrl) ? v8::String::NewFromUtf8(isolate, jsvmOrigin->sourceMapUrl).ToLocalChecked().As() : v8::Local(); auto resourceName = v8::String::NewFromUtf8(isolate, sourceString.c_str()).ToLocalChecked(); v8Origin = new v8::ScriptOrigin(isolate, resourceName, jsvmOrigin ? jsvmOrigin->resourceLineOffset : 0, jsvmOrigin ? jsvmOrigin->resourceColumnOffset : 0, false, -1, sourceMapUrl); if (enableSourceMap && sourceMapPtr) { v8impl::SetFileToSourceMapMapping(jsvmOrigin->resourceName, sourceMapPtr); isolate->SetPrepareStackTraceCallback(PrepareStackTraceCallback); } if (v8Option == v8::ScriptCompiler::kConsumeCodeCache && !cachedData) { hasInvalidOption = true; } } ~CompileOptionResolver() { delete v8Origin; v8Origin = nullptr; } v8::ScriptCompiler::CompileOptions v8Option = v8::ScriptCompiler::kNoCompileOptions; v8::ScriptCompiler::CachedData* cachedData = nullptr; v8::ScriptOrigin* v8Origin = nullptr; JSVM_CompileProfile* profile = nullptr; JSVM_ScriptOrigin* jsvmOrigin = nullptr; bool enableSourceMap = false; static size_t compileCount; bool hasInvalidOption = false; private: v8::ScriptCompiler::CompileOptions jsvmToOptions[] = { v8::ScriptCompiler::kNoCompileOptions, v8::ScriptCompiler::kConsumeCodeCache, v8::ScriptCompiler::kEagerCompile, v8::ScriptCompiler::kProduceCompileHints, v8::ScriptCompiler::kConsumeCompileHints}; void SetOption(int contentNum) { if (contentNum >= JSVM_COMPILE_MODE_DEFAULT && contentNum <= JSVM_COMPILE_MODE_CONSUME_COMPILE_PROFILE) { v8Option = jsvmToOptions[contentNum]; } else { hasInvalidOption = true; } } }; size_t CompileOptionResolver::compileCount = 0; JSVM_Status OH_JSVM_CompileScriptWithOptions(JSVM_Env env, JSVM_Value script, size_t optionCount, JSVM_CompileOptions options[], JSVM_Script* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, script); CHECK_ARG(env, result); v8::Local context = env->context(); auto* isolate = context->GetIsolate(); CompileOptionResolver optionResolver(optionCount, options, isolate); RETURN_STATUS_IF_FALSE(env, !optionResolver.hasInvalidOption, JSVM_INVALID_ARG); v8::Local v8Script = v8impl::V8LocalValueFromJsValue(script); RETURN_STATUS_IF_FALSE(env, v8Script->IsString(), JSVM_STRING_EXPECTED); v8::ScriptCompiler::Source scriptSource(v8Script.As(), *optionResolver.v8Origin, optionResolver.cachedData); auto maybeScript = v8::ScriptCompiler::Compile(context, &scriptSource, optionResolver.v8Option); CHECK_MAYBE_EMPTY(env, maybeScript, JSVM_GENERIC_FAILURE); v8::Local compiledScript = maybeScript.ToLocalChecked(); *result = reinterpret_cast(env->NewJsvmData(compiledScript)); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_CreateCodeCache(JSVM_Env env, JSVM_Script script, const uint8_t** data, size_t* length) { CHECK_ENV(env); CHECK_ARG(env, script); CHECK_ARG(env, data); CHECK_ARG(env, length); auto jsvmData = reinterpret_cast(script); auto v8script = jsvmData->ToV8Local(env->isolate); v8::ScriptCompiler::CachedData* cache = v8::ScriptCompiler::CreateCodeCache(v8script->GetUnboundScript()); if (cache == nullptr) { return SetLastError(env, JSVM_GENERIC_FAILURE); } *data = cache->data; *length = cache->length; cache->buffer_policy = v8::ScriptCompiler::CachedData::BufferNotOwned; delete cache; return ClearLastError(env); } JSVM_Status OH_JSVM_RunScript(JSVM_Env env, JSVM_Script script, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, script); CHECK_ARG(env, result); auto jsvmData = reinterpret_cast(script); auto v8script = jsvmData->ToV8Local(env->isolate); auto scriptResult = v8script->Run(env->context()); CHECK_MAYBE_EMPTY(env, scriptResult, JSVM_GENERIC_FAILURE); *result = v8impl::JsValueFromV8LocalValue(scriptResult.ToLocalChecked()); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_JsonParse(JSVM_Env env, JSVM_Value json_string, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, json_string); v8::Local val = v8impl::V8LocalValueFromJsValue(json_string); RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED); auto maybe = v8::JSON::Parse(env->context(), val.As()); CHECK_MAYBE_EMPTY(env, maybe, JSVM_GENERIC_FAILURE); *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_JsonStringify(JSVM_Env env, JSVM_Value json_object, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, json_object); v8::Local val = v8impl::V8LocalValueFromJsValue(json_object); auto maybe = v8::JSON::Stringify(env->context(), val); CHECK_MAYBE_EMPTY(env, maybe, JSVM_GENERIC_FAILURE); *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_CreateSnapshot(JSVM_VM vm, size_t contextCount, const JSVM_Env* contexts, const char** blobData, size_t* blobSize) { auto isolate = reinterpret_cast(vm); auto creator = v8impl::GetIsolateSnapshotCreator(isolate); if (creator == nullptr) { return JSVM_GENERIC_FAILURE; } { v8::HandleScope scope(isolate); v8::Local defaultContext = v8::Context::New(isolate); creator->SetDefaultContext(defaultContext); // NOTE: The order of the added data must be consistent with the order of // getting data in v8impl::CreateIsolateData. creator->AddData(JSVM_PRIVATE_KEY(isolate, wrapper)); creator->AddData(JSVM_PRIVATE_KEY(isolate, typeTag)); for (size_t i = 0; i < contextCount; i++) { auto ctx = contexts[i]->context(); creator->AddData(ctx, ctx); creator->AddContext(ctx); } } auto blob = creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kKeep); *blobData = blob.data; *blobSize = blob.raw_size; return JSVM_OK; } JSVM_EXTERN JSVM_Status OH_JSVM_GetVMInfo(JSVM_VMInfo* result) { result->apiVersion = 1; result->engine = "v8"; result->version = V8_VERSION_STRING; result->cachedDataVersionTag = v8::ScriptCompiler::CachedDataVersionTag(); return JSVM_OK; } JSVM_EXTERN JSVM_Status OH_JSVM_MemoryPressureNotification(JSVM_Env env, JSVM_MemoryPressureLevel level) { CHECK_ENV(env); env->isolate->MemoryPressureNotification(v8::MemoryPressureLevel(level)); return ClearLastError(env); } JSVM_EXTERN JSVM_Status OH_JSVM_GetHeapStatistics(JSVM_VM vm, JSVM_HeapStatistics* result) { auto isolate = reinterpret_cast(vm); v8::HeapStatistics stats; isolate->GetHeapStatistics(&stats); result->totalHeapSize = stats.total_heap_size(); result->totalHeapSizeExecutable = stats.total_heap_size_executable(); result->totalPhysicalSize = stats.total_physical_size(); result->totalAvailableSize = stats.total_available_size(); result->usedHeapSize = stats.used_heap_size(); result->heapSizeLimit = stats.heap_size_limit(); result->mallocedMemory = stats.malloced_memory(); result->externalMemory = stats.external_memory(); result->peakMallocedMemory = stats.peak_malloced_memory(); result->numberOfNativeContexts = stats.number_of_native_contexts(); result->numberOfDetachedContexts = stats.number_of_detached_contexts(); result->totalGlobalHandlesSize = stats.total_global_handles_size(); result->usedGlobalHandlesSize = stats.used_global_handles_size(); return JSVM_OK; } JSVM_EXTERN JSVM_Status OH_JSVM_StartCpuProfiler(JSVM_VM vm, JSVM_CpuProfiler* result) { auto isolate = reinterpret_cast(vm); auto profiler = v8::CpuProfiler::New(isolate); v8::HandleScope scope(isolate); v8::CpuProfilingOptions options; profiler->Start(v8::String::Empty(isolate), std::move(options)); *result = reinterpret_cast(profiler); return JSVM_OK; } JSVM_EXTERN JSVM_Status OH_JSVM_StopCpuProfiler(JSVM_VM vm, JSVM_CpuProfiler profiler, JSVM_OutputStream stream, void* streamData) { CHECK_ARG_WITHOUT_ENV(stream); auto isolate = reinterpret_cast(vm); auto v8profiler = reinterpret_cast(profiler); v8::HandleScope scope(isolate); auto profile = v8profiler->StopProfiling(v8::String::Empty(isolate)); v8impl::OutputStream os(stream, streamData); profile->Serialize(&os); return JSVM_OK; } JSVM_EXTERN JSVM_Status OH_JSVM_TakeHeapSnapshot(JSVM_VM vm, JSVM_OutputStream stream, void* streamData) { CHECK_ARG_WITHOUT_ENV(stream); auto isolate = reinterpret_cast(vm); auto profiler = isolate->GetHeapProfiler(); auto snapshot = profiler->TakeHeapSnapshot(); v8impl::OutputStream os(stream, streamData); snapshot->Serialize(&os); return JSVM_OK; } JSVM_EXTERN JSVM_Status OH_JSVM_OpenInspector(JSVM_Env env, const char* host, uint16_t port) { JSVM_PREAMBLE(env); CHECK_ARG(env, host); std::string inspectorPath; std::string hostName(host); auto agent = env->GetInspectorAgent(); if (!agent->Start(inspectorPath, hostName, port)) { LOG(Error) << "Open Inspector failed: Please check the internet permisson."; return SetLastError(env, JSVM_GENERIC_FAILURE); } return GET_RETURN_STATUS(env); } JSVM_EXTERN JSVM_Status OH_JSVM_CloseInspector(JSVM_Env env) { JSVM_PREAMBLE(env); auto agent = env->GetInspectorAgent(); if (!agent->IsActive()) { return JSVM_GENERIC_FAILURE; } agent->Stop(); return GET_RETURN_STATUS(env); } JSVM_EXTERN JSVM_Status OH_JSVM_WaitForDebugger(JSVM_Env env, bool breakNextLine) { JSVM_PREAMBLE(env); auto* agent = env->GetInspectorAgent(); if (!agent->IsActive()) { return JSVM_GENERIC_FAILURE; } agent->WaitForConnect(); if (breakNextLine) { agent->PauseOnNextJavascriptStatement("Break on debugger attached"); } return GET_RETURN_STATUS(env); } JSVM_EXTERN JSVM_Status OH_JSVM_PumpMessageLoop(JSVM_VM vm, bool* result) { auto isolate = reinterpret_cast(vm); *result = v8::platform::PumpMessageLoop(v8impl::g_platform.get(), isolate); return JSVM_OK; } JSVM_EXTERN JSVM_Status OH_JSVM_PerformMicrotaskCheckpoint(JSVM_VM vm) { auto isolate = reinterpret_cast(vm); isolate->PerformMicrotaskCheckpoint(); return JSVM_OK; } // Warning: Keep in-sync with JSVM_Status enum static const char* errorMessages[] = { nullptr, "Invalid argument", "An object was expected", "A string was expected", "A string or symbol was expected", "A function was expected", "A number was expected", "A boolean was expected", "An array was expected", "Unknown failure", "An exception is pending", "The async work item was cancelled", "OH_JSVM_EscapeHandle already called on scope", "Invalid handle scope usage", "Invalid callback scope usage", "Thread-safe function queue is full", "Thread-safe function handle is closing", "A bigint was expected", "A date was expected", "An arraybuffer was expected", "A detachable arraybuffer was expected", "Main thread would deadlock", "External buffers are not allowed", "Cannot run JavaScript", "Invalid type", }; JSVM_Status OH_JSVM_GetLastErrorInfo(JSVM_Env env, const JSVM_ExtendedErrorInfo** result) { CHECK_ENV(env); CHECK_ARG(env, result); // The value of the constant below must be updated to reference the last // message in the `JSVM_Status` enum each time a new error message is added. // We don't have a jsvm_status_last as this would result in an ABI // change each time a message was added. const int lastStatus = JSVM_INVALID_TYPE; static_assert(jsvm::ArraySize(errorMessages) == lastStatus + 1, "Count of error messages must match count of error values"); CHECK_LE(env->lastError.errorCode, lastStatus); // Wait until someone requests the last error information to fetch the error // message string env->lastError.errorMessage = errorMessages[env->lastError.errorCode]; if (env->lastError.errorCode == JSVM_OK) { ClearLastError(env); } *result = &(env->lastError); return JSVM_OK; } JSVM_Status OH_JSVM_CreateFunction(JSVM_Env env, const char* utf8name, size_t length, JSVM_Callback cb, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); CHECK_ARG(env, cb); v8::Local returnValue; v8::EscapableHandleScope scope(env->isolate); v8::Local fn; STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(env, cb, &fn)); returnValue = scope.Escape(fn); if (utf8name != nullptr) { v8::Local nameString; CHECK_NEW_FROM_UTF8_LEN(env, nameString, utf8name, length); returnValue->SetName(nameString); } *result = v8impl::JsValueFromV8LocalValue(returnValue); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_CreateFunctionWithScript(JSVM_Env env, const char* funcName, size_t length, size_t argc, const JSVM_Value* argv, JSVM_Value script, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, script); CHECK_ARG(env, result); if (argc > 0) { CHECK_ARG(env, argv); for (size_t i = 0; i < argc; i++) { RETURN_STATUS_IF_FALSE(env, v8impl::V8LocalValueFromJsValue(argv[i])->IsString(), JSVM_STRING_EXPECTED); } } v8::Local v8Script = v8impl::V8LocalValueFromJsValue(script); RETURN_STATUS_IF_FALSE(env, v8Script->IsString(), JSVM_STRING_EXPECTED); v8::ScriptCompiler::Source scriptSource(v8Script.As()); v8::Local context = env->context(); v8::MaybeLocal maybeFunc = v8::ScriptCompiler::CompileFunction(context, &scriptSource, argc, reinterpret_cast*>(const_cast(argv))); CHECK_MAYBE_EMPTY(env, maybeFunc, JSVM_GENERIC_FAILURE); v8::Local func = maybeFunc.ToLocalChecked(); if (funcName != nullptr) { v8::Local funcNameString; CHECK_NEW_FROM_UTF8_LEN(env, funcNameString, funcName, length); func->SetName(funcNameString); } *result = v8impl::JsValueFromV8LocalValue(func); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_DefineClass(JSVM_Env env, const char* utf8name, size_t length, JSVM_Callback constructor, size_t propertyCount, const JSVM_PropertyDescriptor* properties, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); CHECK_ARG(env, constructor); if (propertyCount > 0) { CHECK_ARG(env, properties); } v8::Isolate* isolate = env->isolate; v8::EscapableHandleScope scope(isolate); v8::Local tpl; STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, constructor, &tpl)); v8::Local nameString; CHECK_NEW_FROM_UTF8_LEN(env, nameString, utf8name, length); tpl->SetClassName(nameString); size_t staticPropertyCount = 0; for (size_t i = 0; i < propertyCount; i++) { const JSVM_PropertyDescriptor* p = properties + i; if ((p->attributes & JSVM_STATIC) != 0) { // Static properties are handled separately below. staticPropertyCount++; continue; } v8::Local propertyName; STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &propertyName)); v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); // This code is similar to that in OH_JSVM_DefineProperties(); the // difference is it applies to a template instead of an object, // and preferred PropertyAttribute for lack of PropertyDescriptor // support on ObjectTemplate. if (p->getter != nullptr || p->setter != nullptr) { v8::Local getterTpl; v8::Local setterTpl; if (p->getter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->getter, &getterTpl)); } if (p->setter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->setter, &setterTpl)); } tpl->PrototypeTemplate()->SetAccessorProperty(propertyName, getterTpl, setterTpl, attributes, v8::AccessControl::DEFAULT); } else if (p->method != nullptr) { v8::Local t; if (p->attributes & JSVM_NO_RECEIVER_CHECK) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &t)); } else { STATUS_CALL( v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &t, v8::Signature::New(isolate, tpl))); } tpl->PrototypeTemplate()->Set(propertyName, t, attributes); } else { v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); tpl->PrototypeTemplate()->Set(propertyName, value, attributes); } } v8::Local context = env->context(); *result = v8impl::JsValueFromV8LocalValue(scope.Escape(tpl->GetFunction(context).ToLocalChecked())); if (staticPropertyCount > 0) { std::vector staticDescriptors; staticDescriptors.reserve(staticPropertyCount); for (size_t i = 0; i < propertyCount; i++) { const JSVM_PropertyDescriptor* p = properties + i; if ((p->attributes & JSVM_STATIC) != 0) { staticDescriptors.push_back(*p); } } STATUS_CALL(OH_JSVM_DefineProperties(env, *result, staticDescriptors.size(), staticDescriptors.data())); } return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_GetPropertyNames(JSVM_Env env, JSVM_Value object, JSVM_Value* result) { return OH_JSVM_GetAllPropertyNames(env, object, JSVM_KEY_INCLUDE_PROTOTYPES, static_cast(JSVM_KEY_ENUMERABLE | JSVM_KEY_SKIP_SYMBOLS), JSVM_KEY_NUMBERS_TO_STRINGS, result); } JSVM_Status OH_JSVM_GetAllPropertyNames(JSVM_Env env, JSVM_Value object, JSVM_KeyCollectionMode keyMode, JSVM_KeyFilter keyFilter, JSVM_KeyConversion keyConversion, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::PropertyFilter filter = v8::PropertyFilter::ALL_PROPERTIES; if (keyFilter & JSVM_KEY_WRITABLE) { filter = static_cast(filter | v8::PropertyFilter::ONLY_WRITABLE); } if (keyFilter & JSVM_KEY_ENUMERABLE) { filter = static_cast(filter | v8::PropertyFilter::ONLY_ENUMERABLE); } if (keyFilter & JSVM_KEY_CONFIGURABLE) { filter = static_cast(filter | v8::PropertyFilter::ONLY_CONFIGURABLE); } if (keyFilter & JSVM_KEY_SKIP_STRINGS) { filter = static_cast(filter | v8::PropertyFilter::SKIP_STRINGS); } if (keyFilter & JSVM_KEY_SKIP_SYMBOLS) { filter = static_cast(filter | v8::PropertyFilter::SKIP_SYMBOLS); } v8::KeyCollectionMode collectionMode; v8::KeyConversionMode conversionMode; switch (keyMode) { case JSVM_KEY_INCLUDE_PROTOTYPES: collectionMode = v8::KeyCollectionMode::kIncludePrototypes; break; case JSVM_KEY_OWN_ONLY: collectionMode = v8::KeyCollectionMode::kOwnOnly; break; default: return SetLastError(env, JSVM_INVALID_ARG); } switch (keyConversion) { case JSVM_KEY_KEEP_NUMBERS: conversionMode = v8::KeyConversionMode::kKeepNumbers; break; case JSVM_KEY_NUMBERS_TO_STRINGS: conversionMode = v8::KeyConversionMode::kConvertToString; break; default: return SetLastError(env, JSVM_INVALID_ARG); } v8::MaybeLocal maybeAllPropertynames = obj->GetPropertyNames(context, collectionMode, filter, v8::IndexFilter::kIncludeIndices, conversionMode); CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybeAllPropertynames, JSVM_GENERIC_FAILURE); *result = v8impl::JsValueFromV8LocalValue(maybeAllPropertynames.ToLocalChecked()); return ClearLastError(env); } JSVM_Status OH_JSVM_SetProperty(JSVM_Env env, JSVM_Value object, JSVM_Value key, JSVM_Value value) { JSVM_PREAMBLE(env); CHECK_ARG(env, key); CHECK_ARG(env, value); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Local k = v8impl::V8LocalValueFromJsValue(key); v8::Local val = v8impl::V8LocalValueFromJsValue(value); v8::Maybe setMaybe = obj->Set(context, k, val); RETURN_STATUS_IF_FALSE(env, setMaybe.FromMaybe(false), JSVM_GENERIC_FAILURE); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_HasProperty(JSVM_Env env, JSVM_Value object, JSVM_Value key, bool* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); CHECK_ARG(env, key); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Local k = v8impl::V8LocalValueFromJsValue(key); v8::Maybe hasMaybe = obj->Has(context, k); CHECK_MAYBE_NOTHING(env, hasMaybe, JSVM_GENERIC_FAILURE); *result = hasMaybe.FromMaybe(false); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_GetProperty(JSVM_Env env, JSVM_Value object, JSVM_Value key, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, key); CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local k = v8impl::V8LocalValueFromJsValue(key); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); auto getMaybe = obj->Get(context, k); CHECK_MAYBE_EMPTY(env, getMaybe, JSVM_GENERIC_FAILURE); v8::Local val = getMaybe.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(val); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_DeleteProperty(JSVM_Env env, JSVM_Value object, JSVM_Value key, bool* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, key); v8::Local context = env->context(); v8::Local k = v8impl::V8LocalValueFromJsValue(key); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Maybe deleteMaybe = obj->Delete(context, k); CHECK_MAYBE_NOTHING(env, deleteMaybe, JSVM_GENERIC_FAILURE); if (result != nullptr) *result = deleteMaybe.FromMaybe(false); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_HasOwnProperty(JSVM_Env env, JSVM_Value object, JSVM_Value key, bool* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, key); CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Local k = v8impl::V8LocalValueFromJsValue(key); RETURN_STATUS_IF_FALSE(env, k->IsName(), JSVM_NAME_EXPECTED); v8::Maybe hasMaybe = obj->HasOwnProperty(context, k.As()); CHECK_MAYBE_NOTHING(env, hasMaybe, JSVM_GENERIC_FAILURE); *result = hasMaybe.FromMaybe(false); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_SetNamedProperty(JSVM_Env env, JSVM_Value object, const char* utf8name, JSVM_Value value) { JSVM_PREAMBLE(env); CHECK_ARG(env, value); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Local key; CHECK_NEW_FROM_UTF8(env, key, utf8name); v8::Local val = v8impl::V8LocalValueFromJsValue(value); v8::Maybe setMaybe = obj->Set(context, key, val); RETURN_STATUS_IF_FALSE(env, setMaybe.FromMaybe(false), JSVM_GENERIC_FAILURE); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_HasNamedProperty(JSVM_Env env, JSVM_Value object, const char* utf8name, bool* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Local key; CHECK_NEW_FROM_UTF8(env, key, utf8name); v8::Maybe hasMaybe = obj->Has(context, key); CHECK_MAYBE_NOTHING(env, hasMaybe, JSVM_GENERIC_FAILURE); *result = hasMaybe.FromMaybe(false); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_GetNamedProperty(JSVM_Env env, JSVM_Value object, const char* utf8name, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local key; CHECK_NEW_FROM_UTF8(env, key, utf8name); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); auto getMaybe = obj->Get(context, key); CHECK_MAYBE_EMPTY(env, getMaybe, JSVM_GENERIC_FAILURE); v8::Local val = getMaybe.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(val); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_SetElement(JSVM_Env env, JSVM_Value object, uint32_t index, JSVM_Value value) { JSVM_PREAMBLE(env); CHECK_ARG(env, value); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Local val = v8impl::V8LocalValueFromJsValue(value); auto setMaybe = obj->Set(context, index, val); RETURN_STATUS_IF_FALSE(env, setMaybe.FromMaybe(false), JSVM_GENERIC_FAILURE); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_HasElement(JSVM_Env env, JSVM_Value object, uint32_t index, bool* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Maybe hasMaybe = obj->Has(context, index); CHECK_MAYBE_NOTHING(env, hasMaybe, JSVM_GENERIC_FAILURE); *result = hasMaybe.FromMaybe(false); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_GetElement(JSVM_Env env, JSVM_Value object, uint32_t index, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); auto getMaybe = obj->Get(context, index); CHECK_MAYBE_EMPTY(env, getMaybe, JSVM_GENERIC_FAILURE); *result = v8impl::JsValueFromV8LocalValue(getMaybe.ToLocalChecked()); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_DeleteElement(JSVM_Env env, JSVM_Value object, uint32_t index, bool* result) { JSVM_PREAMBLE(env); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Maybe deleteMaybe = obj->Delete(context, index); CHECK_MAYBE_NOTHING(env, deleteMaybe, JSVM_GENERIC_FAILURE); if (result != nullptr) *result = deleteMaybe.FromMaybe(false); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_DefineProperties(JSVM_Env env, JSVM_Value object, size_t propertyCount, const JSVM_PropertyDescriptor* properties) { JSVM_PREAMBLE(env); if (propertyCount > 0) { CHECK_ARG(env, properties); } v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); for (size_t i = 0; i < propertyCount; i++) { const JSVM_PropertyDescriptor* p = &properties[i]; v8::Local propertyName; STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &propertyName)); if (p->getter != nullptr || p->setter != nullptr) { v8::Local localGetter; v8::Local localSetter; if (p->getter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(env, p->getter, &localGetter)); } if (p->setter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(env, p->setter, &localSetter)); } v8::PropertyDescriptor descriptor(localGetter, localSetter); descriptor.set_enumerable((p->attributes & JSVM_ENUMERABLE) != 0); descriptor.set_configurable((p->attributes & JSVM_CONFIGURABLE) != 0); auto defineMaybe = obj->DefineProperty(context, propertyName, descriptor); if (!defineMaybe.FromMaybe(false)) { return SetLastError(env, JSVM_INVALID_ARG); } } else if (p->method != nullptr) { v8::Local method; STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(env, p->method, &method)); v8::PropertyDescriptor descriptor(method, (p->attributes & JSVM_WRITABLE) != 0); descriptor.set_enumerable((p->attributes & JSVM_ENUMERABLE) != 0); descriptor.set_configurable((p->attributes & JSVM_CONFIGURABLE) != 0); auto defineMaybe = obj->DefineProperty(context, propertyName, descriptor); if (!defineMaybe.FromMaybe(false)) { return SetLastError(env, JSVM_GENERIC_FAILURE); } } else { v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); bool definedSuccessfully = false; if ((p->attributes & JSVM_ENUMERABLE) && (p->attributes & JSVM_WRITABLE) && (p->attributes & JSVM_CONFIGURABLE)) { // Use a fast path for this type of data property. auto defineMaybe = obj->CreateDataProperty(context, propertyName, value); definedSuccessfully = defineMaybe.FromMaybe(false); } else { v8::PropertyDescriptor descriptor(value, (p->attributes & JSVM_WRITABLE) != 0); descriptor.set_enumerable((p->attributes & JSVM_ENUMERABLE) != 0); descriptor.set_configurable((p->attributes & JSVM_CONFIGURABLE) != 0); auto defineMaybe = obj->DefineProperty(context, propertyName, descriptor); definedSuccessfully = defineMaybe.FromMaybe(false); } if (!definedSuccessfully) { return SetLastError(env, JSVM_INVALID_ARG); } } } return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_ObjectFreeze(JSVM_Env env, JSVM_Value object) { JSVM_PREAMBLE(env); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Maybe setFrozen = obj->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen); RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, setFrozen.FromMaybe(false), JSVM_GENERIC_FAILURE); return ClearLastError(env); } JSVM_Status OH_JSVM_ObjectSeal(JSVM_Env env, JSVM_Value object) { JSVM_PREAMBLE(env); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Maybe setSealed = obj->SetIntegrityLevel(context, v8::IntegrityLevel::kSealed); RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, setSealed.FromMaybe(false), JSVM_GENERIC_FAILURE); return ClearLastError(env); } JSVM_Status OH_JSVM_IsArray(JSVM_Env env, JSVM_Value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsArray(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsRegExp(JSVM_Env env, JSVM_Value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsRegExp(); return ClearLastError(env); } JSVM_Status OH_JSVM_GetArrayLength(JSVM_Env env, JSVM_Value value, uint32_t* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsArray(), JSVM_ARRAY_EXPECTED); v8::Local arr = val.As(); *result = arr->Length(); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_StrictEquals(JSVM_Env env, JSVM_Value lhs, JSVM_Value rhs, bool* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, lhs); CHECK_ARG(env, rhs); CHECK_ARG(env, result); v8::Local a = v8impl::V8LocalValueFromJsValue(lhs); v8::Local b = v8impl::V8LocalValueFromJsValue(rhs); *result = a->StrictEquals(b); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_Equals(JSVM_Env env, JSVM_Value lhs, JSVM_Value rhs, bool* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, lhs); CHECK_ARG(env, rhs); CHECK_ARG(env, result); v8::Local a = v8impl::V8LocalValueFromJsValue(lhs); v8::Local b = v8impl::V8LocalValueFromJsValue(rhs); v8::Local context = env->context(); *result = a->Equals(context, b).FromJust(); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_GetPrototype(JSVM_Env env, JSVM_Value object, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Local val = obj->GetPrototype(); *result = v8impl::JsValueFromV8LocalValue(val); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_CreateObject(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Object::New(env->isolate)); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateArray(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate)); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateArrayWithLength(JSVM_Env env, size_t length, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate, length)); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateStringLatin1(JSVM_Env env, const char* str, size_t length, JSVM_Value* result) { return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { return v8::String::NewFromOneByte(isolate, reinterpret_cast(str), v8::NewStringType::kNormal, length); }); } JSVM_Status OH_JSVM_CreateStringUtf8(JSVM_Env env, const char* str, size_t length, JSVM_Value* result) { return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { return v8::String::NewFromUtf8(isolate, str, v8::NewStringType::kNormal, static_cast(length)); }); } JSVM_Status OH_JSVM_CreateStringUtf16(JSVM_Env env, const char16_t* str, size_t length, JSVM_Value* result) { return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { return v8::String::NewFromTwoByte(isolate, reinterpret_cast(str), v8::NewStringType::kNormal, length); }); } JSVM_Status OH_JSVM_CreateDouble(JSVM_Env env, double value, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Number::New(env->isolate, value)); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateInt32(JSVM_Env env, int32_t value, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Integer::New(env->isolate, value)); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateUint32(JSVM_Env env, uint32_t value, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, value)); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateInt64(JSVM_Env env, int64_t value, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Number::New(env->isolate, static_cast(value))); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateBigintInt64(JSVM_Env env, int64_t value, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::BigInt::New(env->isolate, value)); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateBigintUint64(JSVM_Env env, uint64_t value, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::BigInt::NewFromUnsigned(env->isolate, value)); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateBigintWords(JSVM_Env env, int signBit, size_t wordCount, const uint64_t* words, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, words); CHECK_ARG(env, result); v8::Local context = env->context(); RETURN_STATUS_IF_FALSE(env, wordCount <= INT_MAX, JSVM_INVALID_ARG); v8::MaybeLocal b = v8::BigInt::NewFromWords(context, signBit, wordCount, words); CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, b, JSVM_GENERIC_FAILURE); *result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked()); return ClearLastError(env); } JSVM_Status OH_JSVM_GetBoolean(JSVM_Env env, bool value, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; if (value) { *result = v8impl::JsValueFromV8LocalValue(v8::True(isolate)); } else { *result = v8impl::JsValueFromV8LocalValue(v8::False(isolate)); } return ClearLastError(env); } JSVM_Status OH_JSVM_CreateSymbol(JSVM_Env env, JSVM_Value description, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; if (description == nullptr) { *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); } else { v8::Local desc = v8impl::V8LocalValueFromJsValue(description); RETURN_STATUS_IF_FALSE(env, desc->IsString(), JSVM_STRING_EXPECTED); *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate, desc.As())); } return ClearLastError(env); } JSVM_Status OH_JSVM_SymbolFor(JSVM_Env env, const char* utf8description, size_t length, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); JSVM_Value jsDescriptionString; STATUS_CALL(OH_JSVM_CreateStringUtf8(env, utf8description, length, &jsDescriptionString)); v8::Local descriptionString = v8impl::V8LocalValueFromJsValue(jsDescriptionString).As(); *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::For(env->isolate, descriptionString)); return ClearLastError(env); } static JSVM_Status SetErrorCode(JSVM_Env env, v8::Local error, JSVM_Value code, const char* codeCstring) { if ((code != nullptr) || (codeCstring != nullptr)) { v8::Local context = env->context(); v8::Local errObject = error.As(); v8::Local codeValue = v8impl::V8LocalValueFromJsValue(code); if (code != nullptr) { codeValue = v8impl::V8LocalValueFromJsValue(code); RETURN_STATUS_IF_FALSE(env, codeValue->IsString(), JSVM_STRING_EXPECTED); } else { CHECK_NEW_FROM_UTF8(env, codeValue, codeCstring); } v8::Local codeKey; CHECK_NEW_FROM_UTF8(env, codeKey, "code"); v8::Maybe setMaybe = errObject->Set(context, codeKey, codeValue); RETURN_STATUS_IF_FALSE(env, setMaybe.FromMaybe(false), JSVM_GENERIC_FAILURE); } return JSVM_OK; } JSVM_Status OH_JSVM_CreateError(JSVM_Env env, JSVM_Value code, JSVM_Value msg, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, msg); CHECK_ARG(env, result); v8::Local messageValue = v8impl::V8LocalValueFromJsValue(msg); RETURN_STATUS_IF_FALSE(env, messageValue->IsString(), JSVM_STRING_EXPECTED); v8::Local errorObj = v8::Exception::Error(messageValue.As()); STATUS_CALL(SetErrorCode(env, errorObj, code, nullptr)); *result = v8impl::JsValueFromV8LocalValue(errorObj); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateTypeError(JSVM_Env env, JSVM_Value code, JSVM_Value msg, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, msg); CHECK_ARG(env, result); v8::Local messageValue = v8impl::V8LocalValueFromJsValue(msg); RETURN_STATUS_IF_FALSE(env, messageValue->IsString(), JSVM_STRING_EXPECTED); v8::Local errorObj = v8::Exception::TypeError(messageValue.As()); STATUS_CALL(SetErrorCode(env, errorObj, code, nullptr)); *result = v8impl::JsValueFromV8LocalValue(errorObj); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateRangeError(JSVM_Env env, JSVM_Value code, JSVM_Value msg, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, msg); CHECK_ARG(env, result); v8::Local messageValue = v8impl::V8LocalValueFromJsValue(msg); RETURN_STATUS_IF_FALSE(env, messageValue->IsString(), JSVM_STRING_EXPECTED); v8::Local errorObj = v8::Exception::RangeError(messageValue.As()); STATUS_CALL(SetErrorCode(env, errorObj, code, nullptr)); *result = v8impl::JsValueFromV8LocalValue(errorObj); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateSyntaxError(JSVM_Env env, JSVM_Value code, JSVM_Value msg, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, msg); CHECK_ARG(env, result); v8::Local messageValue = v8impl::V8LocalValueFromJsValue(msg); RETURN_STATUS_IF_FALSE(env, messageValue->IsString(), JSVM_STRING_EXPECTED); v8::Local errorObj = v8::Exception::SyntaxError(messageValue.As()); STATUS_CALL(SetErrorCode(env, errorObj, code, nullptr)); *result = v8impl::JsValueFromV8LocalValue(errorObj); return ClearLastError(env); } JSVM_Status OH_JSVM_Typeof(JSVM_Env env, JSVM_Value value, JSVM_ValueType* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local v = v8impl::V8LocalValueFromJsValue(value); if (v->IsNumber()) { *result = JSVM_NUMBER; } else if (v->IsBigInt()) { *result = JSVM_BIGINT; } else if (v->IsString()) { *result = JSVM_STRING; } else if (v->IsFunction()) { // This test has to come before IsObject because IsFunction // implies IsObject *result = JSVM_FUNCTION; } else if (v->IsExternal()) { // This test has to come before IsObject because IsExternal // implies IsObject *result = JSVM_EXTERNAL; } else if (v->IsObject()) { *result = JSVM_OBJECT; } else if (v->IsBoolean()) { *result = JSVM_BOOLEAN; } else if (v->IsUndefined()) { *result = JSVM_UNDEFINED; } else if (v->IsSymbol()) { *result = JSVM_SYMBOL; } else if (v->IsNull()) { *result = JSVM_NULL; } else { // Should not get here unless V8 has added some new kind of value. return SetLastError(env, JSVM_INVALID_ARG); } return ClearLastError(env); } JSVM_Status OH_JSVM_GetUndefined(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Undefined(env->isolate)); return ClearLastError(env); } JSVM_Status OH_JSVM_GetNull(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Null(env->isolate)); return ClearLastError(env); } // Gets all callback info in a single call. (Ugly, but faster.) JSVM_Status OH_JSVM_GetCbInfo(JSVM_Env env, // [in] JSVM environment handle JSVM_CallbackInfo cbinfo, // [in] Opaque callback-info handle size_t* argc, // [in-out] Specifies the size of the provided argv array // and receives the actual count of args. JSVM_Value* argv, // [out] Array of values JSVM_Value* thisArg, // [out] Receives the JS 'this' arg for the call void** data) { // [out] Receives the data pointer for the callback. CHECK_ENV(env); CHECK_ARG(env, cbinfo); v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); if (argv != nullptr) { CHECK_ARG(env, argc); info->GetArgs(argv, *argc); } if (argc != nullptr) { *argc = info->ArgsLength(); } if (thisArg != nullptr) { *thisArg = info->This(); } if (data != nullptr) { *data = info->Data(); } return ClearLastError(env); } JSVM_Status OH_JSVM_GetNewTarget(JSVM_Env env, JSVM_CallbackInfo cbinfo, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, cbinfo); CHECK_ARG(env, result); v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); *result = info->GetNewTarget(); return ClearLastError(env); } JSVM_Status OH_JSVM_CallFunction(JSVM_Env env, JSVM_Value recv, JSVM_Value func, size_t argc, const JSVM_Value* argv, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, recv); if (argc > 0) { CHECK_ARG(env, argv); } v8::Local context = env->context(); v8::Local v8recv = v8impl::V8LocalValueFromJsValue(recv); v8::Local v8func; CHECK_TO_FUNCTION(env, v8func, func); auto maybe = v8func->Call(context, v8recv, argc, reinterpret_cast*>(const_cast(argv))); RETURN_IF_EXCEPTION_HAS_CAUGHT(env); if (result != nullptr) { CHECK_MAYBE_EMPTY(env, maybe, JSVM_GENERIC_FAILURE); *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); } return ClearLastError(env); } JSVM_Status OH_JSVM_GetGlobal(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(env->context()->Global()); return ClearLastError(env); } JSVM_Status OH_JSVM_Throw(JSVM_Env env, JSVM_Value error) { JSVM_PREAMBLE(env); CHECK_ARG(env, error); v8::Isolate* isolate = env->isolate; isolate->ThrowException(v8impl::V8LocalValueFromJsValue(error)); // any VM calls after this point and before returning // to the javascript invoker will fail return ClearLastError(env); } JSVM_Status OH_JSVM_ThrowError(JSVM_Env env, const char* code, const char* msg) { JSVM_PREAMBLE(env); v8::Isolate* isolate = env->isolate; v8::Local str; CHECK_NEW_FROM_UTF8(env, str, msg); v8::Local errorObj = v8::Exception::Error(str); STATUS_CALL(SetErrorCode(env, errorObj, nullptr, code)); isolate->ThrowException(errorObj); // any VM calls after this point and before returning // to the javascript invoker will fail return ClearLastError(env); } JSVM_Status OH_JSVM_ThrowTypeError(JSVM_Env env, const char* code, const char* msg) { JSVM_PREAMBLE(env); v8::Isolate* isolate = env->isolate; v8::Local str; CHECK_NEW_FROM_UTF8(env, str, msg); v8::Local errorObj = v8::Exception::TypeError(str); STATUS_CALL(SetErrorCode(env, errorObj, nullptr, code)); isolate->ThrowException(errorObj); // any VM calls after this point and before returning // to the javascript invoker will fail return ClearLastError(env); } JSVM_Status OH_JSVM_ThrowRangeError(JSVM_Env env, const char* code, const char* msg) { JSVM_PREAMBLE(env); v8::Isolate* isolate = env->isolate; v8::Local str; CHECK_NEW_FROM_UTF8(env, str, msg); v8::Local errorObj = v8::Exception::RangeError(str); STATUS_CALL(SetErrorCode(env, errorObj, nullptr, code)); isolate->ThrowException(errorObj); // any VM calls after this point and before returning // to the javascript invoker will fail return ClearLastError(env); } JSVM_Status OH_JSVM_ThrowSyntaxError(JSVM_Env env, const char* code, const char* msg) { JSVM_PREAMBLE(env); v8::Isolate* isolate = env->isolate; v8::Local str; CHECK_NEW_FROM_UTF8(env, str, msg); v8::Local errorObj = v8::Exception::SyntaxError(str); STATUS_CALL(SetErrorCode(env, errorObj, nullptr, code)); isolate->ThrowException(errorObj); // any VM calls after this point and before returning // to the javascript invoker will fail return ClearLastError(env); } JSVM_Status OH_JSVM_IsError(JSVM_Env env, JSVM_Value value, bool* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot // throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsNativeError(); return ClearLastError(env); } JSVM_Status OH_JSVM_GetValueDouble(JSVM_Env env, JSVM_Value value, double* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED); *result = val.As()->Value(); return ClearLastError(env); } JSVM_Status OH_JSVM_GetValueInt32(JSVM_Env env, JSVM_Value value, int32_t* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); if (val->IsInt32()) { *result = val.As()->Value(); } else { RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED); // Empty context: https://github.com/nodejs/node/issues/14379 v8::Local context; *result = val->Int32Value(context).FromJust(); } return ClearLastError(env); } JSVM_Status OH_JSVM_GetValueUint32(JSVM_Env env, JSVM_Value value, uint32_t* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); if (val->IsUint32()) { *result = val.As()->Value(); } else { RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED); // Empty context: https://github.com/nodejs/node/issues/14379 v8::Local context; *result = val->Uint32Value(context).FromJust(); } return ClearLastError(env); } JSVM_Status OH_JSVM_GetValueInt64(JSVM_Env env, JSVM_Value value, int64_t* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); // This is still a fast path very likely to be taken. if (val->IsInt32()) { *result = val.As()->Value(); return ClearLastError(env); } RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED); // v8::Value::IntegerValue() converts NaN, +Inf, and -Inf to INT64_MIN, // inconsistent with v8::Value::Int32Value() which converts those values to 0. // Special-case all non-finite values to match that behavior. double doubleValue = val.As()->Value(); if (std::isfinite(doubleValue)) { // Empty context: https://github.com/nodejs/node/issues/14379 v8::Local context; *result = val->IntegerValue(context).FromJust(); } else { *result = 0; } return ClearLastError(env); } JSVM_Status OH_JSVM_GetValueBigintInt64(JSVM_Env env, JSVM_Value value, int64_t* result, bool* lossless) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); CHECK_ARG(env, lossless); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), JSVM_BIGINT_EXPECTED); *result = val.As()->Int64Value(lossless); return ClearLastError(env); } JSVM_Status OH_JSVM_GetValueBigintUint64(JSVM_Env env, JSVM_Value value, uint64_t* result, bool* lossless) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); CHECK_ARG(env, lossless); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), JSVM_BIGINT_EXPECTED); *result = val.As()->Uint64Value(lossless); return ClearLastError(env); } JSVM_Status OH_JSVM_GetValueBigintWords(JSVM_Env env, JSVM_Value value, int* signBit, size_t* wordCount, uint64_t* words) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, wordCount); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), JSVM_BIGINT_EXPECTED); v8::Local big = val.As(); int wordCountInt = *wordCount; if (signBit == nullptr && words == nullptr) { wordCountInt = big->WordCount(); } else { CHECK_ARG(env, signBit); CHECK_ARG(env, words); big->ToWordsArray(signBit, &wordCountInt, words); } *wordCount = wordCountInt; return ClearLastError(env); } JSVM_Status OH_JSVM_GetValueBool(JSVM_Env env, JSVM_Value value, bool* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsBoolean(), JSVM_BOOLEAN_EXPECTED); *result = val.As()->Value(); return ClearLastError(env); } // Copies a JavaScript string into a LATIN-1 string buffer. The result is the // number of bytes (excluding the null terminator) copied into buf. // A sufficient buffer size should be greater than the length of string, // reserving space for null terminator. // If bufsize is insufficient, the string will be truncated and null terminated. // If buf is NULL, this method returns the length of the string (in bytes) // via the result parameter. // The result argument is optional unless buf is NULL. JSVM_Status OH_JSVM_GetValueStringLatin1(JSVM_Env env, JSVM_Value value, char* buf, size_t bufsize, size_t* result) { CHECK_ENV(env); CHECK_ARG(env, value); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED); if (!buf) { CHECK_ARG(env, result); *result = val.As()->Length(); } else if (bufsize != 0) { int copied = val.As()->WriteOneByte(env->isolate, reinterpret_cast(buf), 0, bufsize - 1, v8::String::NO_NULL_TERMINATION); buf[copied] = '\0'; if (result != nullptr) { *result = copied; } } else if (result != nullptr) { *result = 0; } return ClearLastError(env); } // Copies a JavaScript string into a UTF-8 string buffer. The result is the // number of bytes (excluding the null terminator) copied into buf. // A sufficient buffer size should be greater than the length of string, // reserving space for null terminator. // If bufsize is insufficient, the string will be truncated and null terminated. // If buf is NULL, this method returns the length of the string (in bytes) // via the result parameter. // The result argument is optional unless buf is NULL. JSVM_Status OH_JSVM_GetValueStringUtf8(JSVM_Env env, JSVM_Value value, char* buf, size_t bufsize, size_t* result) { CHECK_ENV(env); CHECK_ARG(env, value); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED); if (!buf) { CHECK_ARG(env, result); *result = val.As()->Utf8Length(env->isolate); } else if (bufsize != 0) { int copied = val.As()->WriteUtf8(env->isolate, buf, bufsize - 1, nullptr, v8::String::REPLACE_INVALID_UTF8 | v8::String::NO_NULL_TERMINATION); buf[copied] = '\0'; if (result != nullptr) { *result = copied; } } else if (result != nullptr) { *result = 0; } return ClearLastError(env); } // Copies a JavaScript string into a UTF-16 string buffer. The result is the // number of 2-byte code units (excluding the null terminator) copied into buf. // A sufficient buffer size should be greater than the length of string, // reserving space for null terminator. // If bufsize is insufficient, the string will be truncated and null terminated. // If buf is NULL, this method returns the length of the string (in 2-byte // code units) via the result parameter. // The result argument is optional unless buf is NULL. JSVM_Status OH_JSVM_GetValueStringUtf16(JSVM_Env env, JSVM_Value value, char16_t* buf, size_t bufsize, size_t* result) { CHECK_ENV(env); CHECK_ARG(env, value); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED); if (!buf) { CHECK_ARG(env, result); // V8 assumes UTF-16 length is the same as the number of characters. *result = val.As()->Length(); } else if (bufsize != 0) { int copied = val.As()->Write(env->isolate, reinterpret_cast(buf), 0, bufsize - 1, v8::String::NO_NULL_TERMINATION); buf[copied] = '\0'; if (result != nullptr) { *result = copied; } } else if (result != nullptr) { *result = 0; } return ClearLastError(env); } JSVM_Status OH_JSVM_CoerceToBool(JSVM_Env env, JSVM_Value value, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local b = v8impl::V8LocalValueFromJsValue(value)->ToBoolean(isolate); *result = v8impl::JsValueFromV8LocalValue(b); return GET_RETURN_STATUS(env); } #define GEN_COERCE_FUNCTION(UpperCaseName, MixedCaseName, LowerCaseName) \ JSVM_Status OH_JSVM_CoerceTo##MixedCaseName(JSVM_Env env, JSVM_Value value, JSVM_Value* result) \ { \ JSVM_PREAMBLE(env); \ CHECK_ARG(env, value); \ CHECK_ARG(env, result); \ \ v8::Local context = env->context(); \ v8::Local str; \ \ CHECK_TO_##UpperCaseName(env, context, str, value); \ \ *result = v8impl::JsValueFromV8LocalValue(str); \ return GET_RETURN_STATUS(env); \ } GEN_COERCE_FUNCTION(NUMBER, Number, number) GEN_COERCE_FUNCTION(OBJECT, Object, object) GEN_COERCE_FUNCTION(STRING, String, string) GEN_COERCE_FUNCTION(BIGINT, BigInt, bigint) #undef GEN_COERCE_FUNCTION JSVM_Status OH_JSVM_Wrap(JSVM_Env env, JSVM_Value jsObject, void* nativeObject, JSVM_Finalize finalizeCb, void* finalizeHint, JSVM_Ref* result) { return v8impl::Wrap(env, jsObject, nativeObject, finalizeCb, finalizeHint, result); } JSVM_Status OH_JSVM_Unwrap(JSVM_Env env, JSVM_Value obj, void** result) { return v8impl::Unwrap(env, obj, result, v8impl::KEEP_WRAP); } JSVM_Status OH_JSVM_RemoveWrap(JSVM_Env env, JSVM_Value obj, void** result) { return v8impl::Unwrap(env, obj, result, v8impl::REMOVE_WRAP); } JSVM_Status OH_JSVM_CreateExternal(JSVM_Env env, void* data, JSVM_Finalize finalizeCb, void* finalizeHint, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local externalValue = v8::External::New(isolate, data); v8impl::RuntimeReference::New(env, externalValue, finalizeCb, data, finalizeHint); *result = v8impl::JsValueFromV8LocalValue(externalValue); return ClearLastError(env); } JSVM_Status OH_JSVM_TypeTagObject(JSVM_Env env, JSVM_Value object, const JSVM_TypeTag* typeTag) { JSVM_PREAMBLE(env); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, obj, object); CHECK_ARG_WITH_PREAMBLE(env, typeTag); auto key = JSVM_PRIVATE_KEY(env->isolate, typeTag); auto maybeHas = obj->HasPrivate(context, key); CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybeHas, JSVM_GENERIC_FAILURE); RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, !maybeHas.FromJust(), JSVM_INVALID_ARG); auto tag = v8::BigInt::NewFromWords(context, 0, 2, reinterpret_cast(typeTag)); CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, tag, JSVM_GENERIC_FAILURE); auto maybeSet = obj->SetPrivate(context, key, tag.ToLocalChecked()); CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybeSet, JSVM_GENERIC_FAILURE); RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, maybeSet.FromJust(), JSVM_GENERIC_FAILURE); return ClearLastError(env); } JSVM_Status OH_JSVM_CheckObjectTypeTag(JSVM_Env env, JSVM_Value object, const JSVM_TypeTag* typeTag, bool* result) { JSVM_PREAMBLE(env); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, obj, object); CHECK_ARG_WITH_PREAMBLE(env, typeTag); CHECK_ARG_WITH_PREAMBLE(env, result); auto maybeValue = obj->GetPrivate(context, JSVM_PRIVATE_KEY(env->isolate, typeTag)); CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybeValue, JSVM_GENERIC_FAILURE); v8::Local val = maybeValue.ToLocalChecked(); // We consider the type check to have failed unless we reach the line below // where we set whether the type check succeeded or not based on the // comparison of the two type tags. *result = false; if (val->IsBigInt()) { int sign; int size = ByteSize::SIZE_2_BYTES; JSVM_TypeTag tag; val.As()->ToWordsArray(&sign, &size, reinterpret_cast(&tag)); if (sign == 0) { if (size == ByteSize::SIZE_2_BYTES) { *result = (tag.lower == typeTag->lower && tag.upper == typeTag->upper); } else if (size == ByteSize::SIZE_1_BYTES) { *result = (tag.lower == typeTag->lower && 0 == typeTag->upper); } else if (size == ByteSize::SIZE_0_BYTES) { *result = (0 == typeTag->lower && 0 == typeTag->upper); } } } return ClearLastError(env); } JSVM_Status OH_JSVM_GetValueExternal(JSVM_Env env, JSVM_Value value, void** result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsExternal(), JSVM_INVALID_ARG); v8::Local externalValue = val.As(); *result = externalValue->Value(); return ClearLastError(env); } // Set initialRefcount to 0 for a weak reference, >0 for a strong reference. JSVM_Status OH_JSVM_CreateReference(JSVM_Env env, JSVM_Value value, uint32_t initialRefcount, JSVM_Ref* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local v8Value = v8impl::V8LocalValueFromJsValue(value); v8impl::UserReference* reference = v8impl::UserReference::New(env, v8Value, initialRefcount); *result = reinterpret_cast(reference); return ClearLastError(env); } // Deletes a reference. The referenced value is released, and may be GC'd unless // there are other references to it. JSVM_Status OH_JSVM_DeleteReference(JSVM_Env env, JSVM_Ref ref) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, ref); delete reinterpret_cast(ref); return ClearLastError(env); } // Increments the reference count, optionally returning the resulting count. // After this call the reference will be a strong reference because its // refcount is >0, and the referenced object is effectively "pinned". // Calling this when the refcount is 0 and the object is unavailable // results in an error. JSVM_Status OH_JSVM_ReferenceRef(JSVM_Env env, JSVM_Ref ref, uint32_t* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, ref); v8impl::UserReference* reference = reinterpret_cast(ref); uint32_t count = reference->Ref(); if (result != nullptr) { *result = count; } return ClearLastError(env); } // Decrements the reference count, optionally returning the resulting count. If // the result is 0 the reference is now weak and the object may be GC'd at any // time if there are no other references. Calling this when the refcount is // already 0 results in an error. JSVM_Status OH_JSVM_ReferenceUnref(JSVM_Env env, JSVM_Ref ref, uint32_t* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, ref); v8impl::UserReference* reference = reinterpret_cast(ref); if (reference->RefCount() == 0) { return SetLastError(env, JSVM_GENERIC_FAILURE); } uint32_t count = reference->Unref(); if (result != nullptr) { *result = count; } return ClearLastError(env); } // Attempts to get a referenced value. If the reference is weak, the value might // no longer be available, in that case the call is still successful but the // result is NULL. JSVM_Status OH_JSVM_GetReferenceValue(JSVM_Env env, JSVM_Ref ref, JSVM_Value* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, ref); CHECK_ARG(env, result); v8impl::UserReference* reference = reinterpret_cast(ref); *result = v8impl::JsValueFromV8LocalValue(reference->Get()); return ClearLastError(env); } JSVM_Status OH_JSVM_OpenHandleScope(JSVM_Env env, JSVM_HandleScope* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsHandleScopeFromV8HandleScope(new v8impl::HandleScopeWrapper(env->isolate)); env->openHandleScopes++; return ClearLastError(env); } JSVM_Status OH_JSVM_CloseHandleScope(JSVM_Env env, JSVM_HandleScope scope) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, scope); if (env->openHandleScopes == 0) { return JSVM_HANDLE_SCOPE_MISMATCH; } env->ReleaseJsvmData(); env->openHandleScopes--; delete v8impl::V8HandleScopeFromJsHandleScope(scope); return ClearLastError(env); } JSVM_Status OH_JSVM_OpenEscapableHandleScope(JSVM_Env env, JSVM_EscapableHandleScope* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope(new v8impl::EscapableHandleScopeWrapper(env->isolate)); env->openHandleScopes++; return ClearLastError(env); } JSVM_Status OH_JSVM_CloseEscapableHandleScope(JSVM_Env env, JSVM_EscapableHandleScope scope) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, scope); if (env->openHandleScopes == 0) { return JSVM_HANDLE_SCOPE_MISMATCH; } delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); env->openHandleScopes--; return ClearLastError(env); } JSVM_Status OH_JSVM_EscapeHandle(JSVM_Env env, JSVM_EscapableHandleScope scope, JSVM_Value escapee, JSVM_Value* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, scope); CHECK_ARG(env, escapee); CHECK_ARG(env, result); v8impl::EscapableHandleScopeWrapper* s = v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); if (!s->IsEscapeCalled()) { *result = v8impl::JsValueFromV8LocalValue(s->Escape(v8impl::V8LocalValueFromJsValue(escapee))); return ClearLastError(env); } return SetLastError(env, JSVM_ESCAPE_CALLED_TWICE); } JSVM_Status OH_JSVM_NewInstance(JSVM_Env env, JSVM_Value constructor, size_t argc, const JSVM_Value* argv, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, constructor); if (argc > 0) { CHECK_ARG(env, argv); } CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local ctor; CHECK_TO_FUNCTION(env, ctor, constructor); auto maybe = ctor->NewInstance(context, argc, reinterpret_cast*>(const_cast(argv))); CHECK_MAYBE_EMPTY(env, maybe, JSVM_PENDING_EXCEPTION); *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_Instanceof(JSVM_Env env, JSVM_Value object, JSVM_Value constructor, bool* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, object); CHECK_ARG(env, result); *result = false; v8::Local ctor; v8::Local context = env->context(); CHECK_TO_OBJECT(env, context, ctor, constructor); if (!ctor->IsFunction()) { OH_JSVM_ThrowTypeError(env, "ERR_NAPI_CONS_FUNCTION", "Constructor must be a function"); return SetLastError(env, JSVM_FUNCTION_EXPECTED); } JSVM_Status status = JSVM_GENERIC_FAILURE; v8::Local val = v8impl::V8LocalValueFromJsValue(object); auto maybeResult = val->InstanceOf(context, ctor); CHECK_MAYBE_NOTHING(env, maybeResult, status); *result = maybeResult.FromJust(); return GET_RETURN_STATUS(env); } // Methods to support catching exceptions JSVM_Status OH_JSVM_IsExceptionPending(JSVM_Env env, bool* result) { // JSVM_PREAMBLE is not used here: this function must execute when there is a // pending exception. CHECK_ENV(env); CHECK_ARG(env, result); *result = !env->lastException.IsEmpty(); return ClearLastError(env); } JSVM_Status OH_JSVM_GetAndClearLastException(JSVM_Env env, JSVM_Value* result) { // JSVM_PREAMBLE is not used here: this function must execute when there is a // pending exception. CHECK_ENV(env); CHECK_ARG(env, result); if (env->lastException.IsEmpty()) { return OH_JSVM_GetUndefined(env, result); } else { *result = v8impl::JsValueFromV8LocalValue(v8::Local::New(env->isolate, env->lastException)); env->lastException.Reset(); } return ClearLastError(env); } JSVM_Status OH_JSVM_IsArraybuffer(JSVM_Env env, JSVM_Value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsArrayBuffer(); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateArraybuffer(JSVM_Env env, size_t byteLength, void** data, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; v8::Local buffer = v8::ArrayBuffer::New(isolate, byteLength); // Optionally return a pointer to the buffer's data, to avoid another call to // retrieve it. if (data != nullptr) { *data = buffer->Data(); } *result = v8impl::JsValueFromV8LocalValue(buffer); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_AllocateArrayBufferBackingStoreData(size_t byteLength, JSVM_InitializedFlag initialized, void** data) { if (!data) { return JSVM_INVALID_ARG; } auto allocator = v8impl::GetOrCreateDefaultArrayBufferAllocator(); *data = (initialized == JSVM_ZERO_INITIALIZED) ? allocator->Allocate(byteLength) : allocator->AllocateUninitialized(byteLength); return *data ? JSVM_OK : JSVM_GENERIC_FAILURE; } JSVM_Status OH_JSVM_FreeArrayBufferBackingStoreData(void* data) { if (!data) { return JSVM_INVALID_ARG; } auto allocator = v8impl::GetOrCreateDefaultArrayBufferAllocator(); allocator->Free(data, JSVM_AUTO_LENGTH); return JSVM_OK; } JSVM_Status OH_JSVM_CreateArrayBufferFromBackingStoreData(JSVM_Env env, void* data, size_t backingStoreSize, size_t offset, size_t arrayBufferSize, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, data); CHECK_ARG(env, result); CHECK_ARG_NOT_ZERO(env, backingStoreSize); CHECK_ARG_NOT_ZERO(env, arrayBufferSize); void* dataPtr = static_cast(data) + offset; RETURN_STATUS_IF_FALSE(env, offset + arrayBufferSize <= backingStoreSize, JSVM_INVALID_ARG); auto backingStore = v8::ArrayBuffer::NewBackingStore(dataPtr, arrayBufferSize, v8::BackingStore::EmptyDeleter, nullptr); v8::Local arrayBuffer = v8::ArrayBuffer::New(env->isolate, std::move(backingStore)); *result = v8impl::JsValueFromV8LocalValue(arrayBuffer); return ClearLastError(env); } JSVM_Status OH_JSVM_GetArraybufferInfo(JSVM_Env env, JSVM_Value arraybuffer, void** data, size_t* byteLength) { CHECK_ENV(env); CHECK_ARG(env, arraybuffer); v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), JSVM_INVALID_ARG); v8::Local ab = value.As(); if (data != nullptr) { *data = ab->Data(); } if (byteLength != nullptr) { *byteLength = ab->ByteLength(); } return ClearLastError(env); } JSVM_Status OH_JSVM_IsTypedarray(JSVM_Env env, JSVM_Value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsTypedArray(); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateTypedarray(JSVM_Env env, JSVM_TypedarrayType type, size_t length, JSVM_Value arraybuffer, size_t byteOffset, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, arraybuffer); CHECK_ARG(env, result); v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), JSVM_INVALID_ARG); v8::Local buffer = value.As(); v8::Local typedArray; switch (type) { case JSVM_INT8_ARRAY: CREATE_TYPED_ARRAY(env, Int8Array, ByteSize::SIZE_1_BYTES, buffer, byteOffset, length, typedArray); break; case JSVM_UINT8_ARRAY: CREATE_TYPED_ARRAY(env, Uint8Array, ByteSize::SIZE_1_BYTES, buffer, byteOffset, length, typedArray); break; case JSVM_UINT8_CLAMPED_ARRAY: CREATE_TYPED_ARRAY(env, Uint8ClampedArray, ByteSize::SIZE_1_BYTES, buffer, byteOffset, length, typedArray); break; case JSVM_INT16_ARRAY: CREATE_TYPED_ARRAY(env, Int16Array, ByteSize::SIZE_2_BYTES, buffer, byteOffset, length, typedArray); break; case JSVM_UINT16_ARRAY: CREATE_TYPED_ARRAY(env, Uint16Array, ByteSize::SIZE_2_BYTES, buffer, byteOffset, length, typedArray); break; case JSVM_INT32_ARRAY: CREATE_TYPED_ARRAY(env, Int32Array, ByteSize::SIZE_4_BYTES, buffer, byteOffset, length, typedArray); break; case JSVM_UINT32_ARRAY: CREATE_TYPED_ARRAY(env, Uint32Array, ByteSize::SIZE_4_BYTES, buffer, byteOffset, length, typedArray); break; case JSVM_FLOAT32_ARRAY: CREATE_TYPED_ARRAY(env, Float32Array, ByteSize::SIZE_4_BYTES, buffer, byteOffset, length, typedArray); break; case JSVM_FLOAT64_ARRAY: CREATE_TYPED_ARRAY(env, Float64Array, ByteSize::SIZE_8_BYTES, buffer, byteOffset, length, typedArray); break; case JSVM_BIGINT64_ARRAY: CREATE_TYPED_ARRAY(env, BigInt64Array, ByteSize::SIZE_8_BYTES, buffer, byteOffset, length, typedArray); break; case JSVM_BIGUINT64_ARRAY: CREATE_TYPED_ARRAY(env, BigUint64Array, ByteSize::SIZE_8_BYTES, buffer, byteOffset, length, typedArray); break; default: return SetLastError(env, JSVM_INVALID_ARG); } *result = v8impl::JsValueFromV8LocalValue(typedArray); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_GetTypedarrayInfo(JSVM_Env env, JSVM_Value typedarray, JSVM_TypedarrayType* type, size_t* length, void** data, JSVM_Value* arraybuffer, size_t* byteOffset) { CHECK_ENV(env); CHECK_ARG(env, typedarray); v8::Local value = v8impl::V8LocalValueFromJsValue(typedarray); RETURN_STATUS_IF_FALSE(env, value->IsTypedArray(), JSVM_INVALID_ARG); v8::Local array = value.As(); if (type != nullptr) { if (value->IsInt8Array()) { *type = JSVM_INT8_ARRAY; } else if (value->IsUint8Array()) { *type = JSVM_UINT8_ARRAY; } else if (value->IsUint8ClampedArray()) { *type = JSVM_UINT8_CLAMPED_ARRAY; } else if (value->IsInt16Array()) { *type = JSVM_INT16_ARRAY; } else if (value->IsUint16Array()) { *type = JSVM_UINT16_ARRAY; } else if (value->IsInt32Array()) { *type = JSVM_INT32_ARRAY; } else if (value->IsUint32Array()) { *type = JSVM_UINT32_ARRAY; } else if (value->IsFloat32Array()) { *type = JSVM_FLOAT32_ARRAY; } else if (value->IsFloat64Array()) { *type = JSVM_FLOAT64_ARRAY; } else if (value->IsBigInt64Array()) { *type = JSVM_BIGINT64_ARRAY; } else if (value->IsBigUint64Array()) { *type = JSVM_BIGUINT64_ARRAY; } } if (length != nullptr) { *length = array->Length(); } v8::Local buffer; if (data != nullptr || arraybuffer != nullptr) { // Calling Buffer() may have the side effect of allocating the buffer, // so only do this when it’s needed. buffer = array->Buffer(); } if (data != nullptr) { *data = static_cast(buffer->Data()) + array->ByteOffset(); } if (arraybuffer != nullptr) { *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); } if (byteOffset != nullptr) { *byteOffset = array->ByteOffset(); } return ClearLastError(env); } JSVM_Status OH_JSVM_CreateDataview(JSVM_Env env, size_t byteLength, JSVM_Value arraybuffer, size_t byteOffset, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, arraybuffer); CHECK_ARG(env, result); v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), JSVM_INVALID_ARG); v8::Local buffer = value.As(); if (byteLength + byteOffset > buffer->ByteLength()) { OH_JSVM_ThrowRangeError(env, "ERR_JSVM_INVALID_DATAVIEW_ARGS", "byteOffset + byteLength should be less than or " "equal to the size in bytes of the array passed in"); return SetLastError(env, JSVM_PENDING_EXCEPTION); } v8::Local DataView = v8::DataView::New(buffer, byteOffset, byteLength); *result = v8impl::JsValueFromV8LocalValue(DataView); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_IsDataview(JSVM_Env env, JSVM_Value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsDataView(); return ClearLastError(env); } JSVM_Status OH_JSVM_GetDataviewInfo(JSVM_Env env, JSVM_Value dataview, size_t* byteLength, void** data, JSVM_Value* arraybuffer, size_t* byteOffset) { CHECK_ENV(env); CHECK_ARG(env, dataview); v8::Local value = v8impl::V8LocalValueFromJsValue(dataview); RETURN_STATUS_IF_FALSE(env, value->IsDataView(), JSVM_INVALID_ARG); v8::Local array = value.As(); if (byteLength != nullptr) { *byteLength = array->ByteLength(); } v8::Local buffer; if (data != nullptr || arraybuffer != nullptr) { // Calling Buffer() may have the side effect of allocating the buffer, // so only do this when it’s needed. buffer = array->Buffer(); } if (data != nullptr) { *data = static_cast(buffer->Data()) + array->ByteOffset(); } if (arraybuffer != nullptr) { *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); } if (byteOffset != nullptr) { *byteOffset = array->ByteOffset(); } return ClearLastError(env); } JSVM_Status OH_JSVM_GetVersion(JSVM_Env env, uint32_t* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = JSVM_API_VERSION; return ClearLastError(env); } JSVM_Status OH_JSVM_CreatePromise(JSVM_Env env, JSVM_Deferred* deferred, JSVM_Value* promise) { JSVM_PREAMBLE(env); CHECK_ARG(env, deferred); CHECK_ARG(env, promise); auto maybe = v8::Promise::Resolver::New(env->context()); CHECK_MAYBE_EMPTY(env, maybe, JSVM_GENERIC_FAILURE); auto resolver = maybe.ToLocalChecked(); auto v8Deferred = new v8impl::Persistent(); v8Deferred->Reset(env->isolate, resolver); *deferred = v8impl::JsDeferredFromPersistent(v8Deferred); *promise = v8impl::JsValueFromV8LocalValue(resolver->GetPromise()); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_ResolveDeferred(JSVM_Env env, JSVM_Deferred deferred, JSVM_Value resolution) { JSVM_PREAMBLE(env); CHECK_ARG(env, resolution); auto success = v8impl::ConcludeDeferred(env, deferred, resolution); RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), JSVM_GENERIC_FAILURE); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_RejectDeferred(JSVM_Env env, JSVM_Deferred deferred, JSVM_Value resolution) { JSVM_PREAMBLE(env); CHECK_ARG(env, resolution); auto success = v8impl::ConcludeDeferred(env, deferred, resolution); RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), JSVM_GENERIC_FAILURE); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_IsPromise(JSVM_Env env, JSVM_Value value, bool* isPromise) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isPromise); *isPromise = v8impl::V8LocalValueFromJsValue(value)->IsPromise(); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateDate(JSVM_Env env, double time, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); v8::MaybeLocal maybeDate = v8::Date::New(env->context(), time); CHECK_MAYBE_EMPTY(env, maybeDate, JSVM_GENERIC_FAILURE); *result = v8impl::JsValueFromV8LocalValue(maybeDate.ToLocalChecked()); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_IsDate(JSVM_Env env, JSVM_Value value, bool* isDate) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isDate); *isDate = v8impl::V8LocalValueFromJsValue(value)->IsDate(); return ClearLastError(env); } JSVM_Status OH_JSVM_GetDateValue(JSVM_Env env, JSVM_Value value, double* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsDate(), JSVM_DATE_EXPECTED); v8::Local date = val.As(); *result = date->ValueOf(); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_AddFinalizer(JSVM_Env env, JSVM_Value jsObject, void* finalizeData, JSVM_Finalize finalizeCb, void* finalizeHint, JSVM_Ref* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, jsObject); CHECK_ARG(env, finalizeCb); v8::Local v8Value = v8impl::V8LocalValueFromJsValue(jsObject); RETURN_STATUS_IF_FALSE(env, v8Value->IsObject(), JSVM_INVALID_ARG); v8impl::RuntimeReference::New(env, v8Value, finalizeCb, finalizeData, finalizeHint); if (result != nullptr) { auto* ref = v8impl::UserReference::New(env, v8Value, 0); *result = reinterpret_cast(ref); } return ClearLastError(env); } JSVM_Status OH_JSVM_AdjustExternalMemory(JSVM_Env env, int64_t changeInBytes, int64_t* adjustedValue) { CHECK_ENV(env); CHECK_ARG(env, adjustedValue); *adjustedValue = env->isolate->AdjustAmountOfExternalAllocatedMemory(changeInBytes); return ClearLastError(env); } JSVM_Status OH_JSVM_SetInstanceData(JSVM_Env env, void* data, JSVM_Finalize finalizeCb, void* finalizeHint) { CHECK_ENV(env); v8impl::FinalizerTracker* oldData = static_cast(env->instanceData); if (oldData != nullptr) { // Our contract so far has been to not finalize any old data there may be. // So we simply delete it. delete oldData; } env->instanceData = v8impl::FinalizerTracker::New(env, finalizeCb, data, finalizeHint); return ClearLastError(env); } JSVM_Status OH_JSVM_GetInstanceData(JSVM_Env env, void** data) { CHECK_ENV(env); CHECK_ARG(env, data); v8impl::FinalizerTracker* idata = static_cast(env->instanceData); *data = (idata == nullptr ? nullptr : idata->GetData()); return ClearLastError(env); } JSVM_Status OH_JSVM_DetachArraybuffer(JSVM_Env env, JSVM_Value arraybuffer) { CHECK_ENV(env); CHECK_ARG(env, arraybuffer); v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer() || value->IsSharedArrayBuffer(), JSVM_ARRAYBUFFER_EXPECTED); v8::Local it = value.As(); RETURN_STATUS_IF_FALSE(env, it->IsDetachable(), JSVM_DETACHABLE_ARRAYBUFFER_EXPECTED); it->Detach(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsDetachedArraybuffer(JSVM_Env env, JSVM_Value arraybuffer, bool* result) { CHECK_ENV(env); CHECK_ARG(env, arraybuffer); CHECK_ARG(env, result); v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); *result = value->IsArrayBuffer() && value.As()->WasDetached(); return ClearLastError(env); } JSVM_Status OH_JSVM_DefineClassWithPropertyHandler(JSVM_Env env, const char* utf8name, size_t length, JSVM_Callback constructor, size_t propertyCount, const JSVM_PropertyDescriptor* properties, JSVM_PropertyHandlerCfg propertyHandlerCfg, JSVM_Callback callAsFunctionCallback, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); CHECK_ARG(env, constructor); CHECK_ARG(env, constructor->callback); CHECK_ARG(env, propertyHandlerCfg); if (propertyCount > 0) { CHECK_ARG(env, properties); } v8::Isolate* isolate = env->isolate; v8::EscapableHandleScope scope(isolate); v8::Local tpl; STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, constructor, &tpl)); v8::Local nameString; CHECK_NEW_FROM_UTF8_LEN(env, nameString, utf8name, length); tpl->SetClassName(nameString); size_t staticPropertyCount = 0; for (size_t i = 0; i < propertyCount; i++) { const JSVM_PropertyDescriptor* p = properties + i; if ((p->attributes & JSVM_STATIC) != 0) { // attributes // Static properties are handled separately below. staticPropertyCount++; continue; } v8::Local propertyName; STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &propertyName)); v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); // This code is similar to that in OH_JSVM_DefineProperties(); the // difference is it applies to a template instead of an object, // and preferred PropertyAttribute for lack of PropertyDescriptor // support on ObjectTemplate. if (p->getter != nullptr || p->setter != nullptr) { v8::Local getterTpl; v8::Local setterTpl; if (p->getter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->getter, &getterTpl)); } if (p->setter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->setter, &setterTpl)); } tpl->PrototypeTemplate()->SetAccessorProperty(propertyName, getterTpl, setterTpl, attributes, v8::AccessControl::DEFAULT); } else if (p->method != nullptr) { v8::Local t; if (p->attributes & JSVM_NO_RECEIVER_CHECK) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &t)); } else { STATUS_CALL( v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &t, v8::Signature::New(isolate, tpl))); } tpl->PrototypeTemplate()->Set(propertyName, t, attributes); } else { v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); tpl->PrototypeTemplate()->Set(propertyName, value, attributes); } } /* register property handler for instance object */ v8impl::JSVM_PropertyHandlerCfgStruct* propertyHandleCfg = v8impl::CreatePropertyCfg(env, propertyHandlerCfg); if (propertyHandleCfg == nullptr) { return JSVM_Status::JSVM_GENERIC_FAILURE; } v8::Local cbdata = v8impl::CallbackBundle::New(env, propertyHandleCfg); // register named property handler v8::NamedPropertyHandlerConfiguration namedPropertyHandler; if (propertyHandlerCfg->genericNamedPropertyGetterCallback) { namedPropertyHandler.getter = v8impl::PropertyCallbackWrapper::NameGetterInvoke; } if (propertyHandlerCfg->genericNamedPropertySetterCallback) { namedPropertyHandler.setter = v8impl::PropertyCallbackWrapper::NameSetterInvoke; } if (propertyHandlerCfg->genericNamedPropertyDeleterCallback) { namedPropertyHandler.deleter = v8impl::PropertyCallbackWrapper::NameDeleterInvoke; } if (propertyHandlerCfg->genericNamedPropertyEnumeratorCallback) { namedPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper::NameEnumeratorInvoke; } namedPropertyHandler.data = cbdata; tpl->InstanceTemplate()->SetHandler(namedPropertyHandler); // register indexed property handle v8::IndexedPropertyHandlerConfiguration indexPropertyHandler; if (propertyHandlerCfg->genericIndexedPropertyGetterCallback) { indexPropertyHandler.getter = v8impl::PropertyCallbackWrapper::IndexGetterInvoke; } if (propertyHandlerCfg->genericIndexedPropertySetterCallback) { indexPropertyHandler.setter = v8impl::PropertyCallbackWrapper::IndexSetterInvoke; } if (propertyHandlerCfg->genericIndexedPropertyDeleterCallback) { indexPropertyHandler.deleter = v8impl::PropertyCallbackWrapper::IndexDeleterInvoke; } if (propertyHandlerCfg->genericIndexedPropertyEnumeratorCallback) { indexPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper::IndexEnumeratorInvoke; } indexPropertyHandler.data = cbdata; tpl->InstanceTemplate()->SetHandler(indexPropertyHandler); // register call as function if (callAsFunctionCallback && callAsFunctionCallback->callback) { v8::Local funcCbdata = v8impl::CallbackBundle::New(env, callAsFunctionCallback); tpl->InstanceTemplate()->SetCallAsFunctionHandler(v8impl::FunctionCallbackWrapper::Invoke, funcCbdata); } v8::Local context = env->context(); *result = v8impl::JsValueFromV8LocalValue(scope.Escape(tpl->GetFunction(context).ToLocalChecked())); v8impl::RuntimeReference::New(env, v8impl::V8LocalValueFromJsValue(*result), v8impl::CfgFinalizedCallback, propertyHandleCfg, nullptr); if (staticPropertyCount > 0) { std::vector staticDescriptors; staticDescriptors.reserve(staticPropertyCount); for (size_t i = 0; i < propertyCount; i++) { const JSVM_PropertyDescriptor* p = properties + i; if ((p->attributes & JSVM_STATIC) != 0) { staticDescriptors.push_back(*p); } } STATUS_CALL(OH_JSVM_DefineProperties(env, *result, staticDescriptors.size(), staticDescriptors.data())); } return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_IsLocked(JSVM_Env env, bool* isLocked) { CHECK_ENV(env); CHECK_ARG(env, isLocked); *isLocked = v8::Locker::IsLocked(env->isolate); return ClearLastError(env); } JSVM_Status OH_JSVM_AcquireLock(JSVM_Env env) { CHECK_ENV(env); bool isLocked = v8::Locker::IsLocked(env->isolate); if (!isLocked) { env->locker = new v8::Locker(env->isolate); } return ClearLastError(env); } JSVM_Status OH_JSVM_ReleaseLock(JSVM_Env env) { CHECK_ENV(env); bool isLocked = v8::Locker::IsLocked(env->isolate); if (isLocked && env->locker != nullptr) { delete env->locker; env->locker = nullptr; } return ClearLastError(env); } JSVM_Status OH_JSVM_IsCallable(JSVM_Env env, JSVM_Value value, bool* isCallable) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isCallable); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isCallable = val->IsFunction(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsUndefined(JSVM_Env env, JSVM_Value value, bool* isUndefined) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isUndefined); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isUndefined = val->IsUndefined(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsNull(JSVM_Env env, JSVM_Value value, bool* isNull) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isNull); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isNull = val->IsNull(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsNullOrUndefined(JSVM_Env env, JSVM_Value value, bool* isNullOrUndefined) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isNullOrUndefined); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isNullOrUndefined = val->IsNullOrUndefined(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsBoolean(JSVM_Env env, JSVM_Value value, bool* isBoolean) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isBoolean); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isBoolean = val->IsBoolean(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsNumber(JSVM_Env env, JSVM_Value value, bool* isNumber) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isNumber); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isNumber = val->IsNumber(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsString(JSVM_Env env, JSVM_Value value, bool* isString) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isString); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isString = val->IsString(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsSymbol(JSVM_Env env, JSVM_Value value, bool* isSymbol) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isSymbol); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isSymbol = val->IsSymbol(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsFunction(JSVM_Env env, JSVM_Value value, bool* isFunction) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isFunction); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isFunction = val->IsFunction(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsObject(JSVM_Env env, JSVM_Value value, bool* isObject) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isObject); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isObject = val->IsObject(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsBigInt(JSVM_Env env, JSVM_Value value, bool* isBigInt) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isBigInt); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isBigInt = val->IsBigInt(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsConstructor(JSVM_Env env, JSVM_Value value, bool* isConstructor) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isConstructor); v8::Local val = v8impl::V8LocalValueFromJsValue(value); if (!val->IsObject()) { *isConstructor = false; return ClearLastError(env); } v8::Local obj = val.As(); *isConstructor = obj->IsConstructor(); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateRegExp(JSVM_Env env, JSVM_Value value, JSVM_RegExpFlags flags, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local pattern = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, pattern->IsString(), JSVM_STRING_EXPECTED); v8::Local context = env->context(); v8::MaybeLocal regExp = v8::RegExp::New(context, pattern.As(), static_cast(flags)); CHECK_MAYBE_EMPTY(env, regExp, JSVM_GENERIC_FAILURE); *result = v8impl::JsValueFromV8LocalValue(regExp.ToLocalChecked()); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_CreateMap(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Map::New(env->isolate)); return ClearLastError(env); } JSVM_Status OH_JSVM_IsMap(JSVM_Env env, JSVM_Value value, bool* isMap) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isMap); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isMap = val->IsMap(); return ClearLastError(env); } JSVM_Status OH_JSVM_RetainScript(JSVM_Env env, JSVM_Script script) { CHECK_ENV(env); auto jsvmData = reinterpret_cast(script); RETURN_STATUS_IF_FALSE(env, jsvmData && !jsvmData->isGlobal, JSVM_INVALID_ARG); jsvmData->taggedPointer = v8::Global(env->isolate, jsvmData->ToV8Local(env->isolate)); jsvmData->isGlobal = true; return ClearLastError(env); } JSVM_Status OH_JSVM_ReleaseScript(JSVM_Env env, JSVM_Script script) { CHECK_ENV(env); auto jsvmData = reinterpret_cast(script); RETURN_STATUS_IF_FALSE(env, jsvmData && jsvmData->isGlobal, JSVM_INVALID_ARG); std::get>(jsvmData->taggedPointer).Reset(); delete jsvmData; return ClearLastError(env); } JSVM_Status OH_JSVM_OpenInspectorWithName(JSVM_Env env, int pid, const char* name) { JSVM_PREAMBLE(env); RETURN_STATUS_IF_FALSE(env, !name || strlen(name) < SIZE_MAX, JSVM_INVALID_ARG); RETURN_STATUS_IF_FALSE(env, pid >= 0, JSVM_INVALID_ARG); std::string path(name ? name : "jsvm"); if (!env->GetInspectorAgent()->Start(path, pid)) { LOG(Error) << "Open Inspector failed: Please check the internet permisson."; return SetLastError(env, JSVM_GENERIC_FAILURE); } return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_CreateSet(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); *result = v8impl::JsValueFromV8LocalValue(v8::Set::New(env->isolate)); return ClearLastError(env); } JSVM_Status OH_JSVM_IsSet(JSVM_Env env, JSVM_Value value, bool* isSet) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isSet); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isSet = val->IsSet(); return ClearLastError(env); } JSVM_Status OH_JSVM_ObjectGetPrototypeOf(JSVM_Env env, JSVM_Value object, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Local val = obj->GetPrototypeV2(); *result = v8impl::JsValueFromV8LocalValue(val); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_ObjectSetPrototypeOf(JSVM_Env env, JSVM_Value object, JSVM_Value prototype) { JSVM_PREAMBLE(env); CHECK_ARG(env, prototype); v8::Local context = env->context(); v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); v8::Local type = v8impl::V8LocalValueFromJsValue(prototype); RETURN_STATUS_IF_FALSE(env, type->IsObject(), JSVM_INVALID_ARG); v8::Maybe setMaybe = obj->SetPrototypeV2(context, type); RETURN_STATUS_IF_FALSE(env, setMaybe.FromMaybe(false), JSVM_GENERIC_FAILURE); return GET_RETURN_STATUS((env)); } JSVM_Status OH_JSVM_CompileWasmModule(JSVM_Env env, const uint8_t* wasmBytecode, size_t wasmBytecodeLength, const uint8_t* cacheData, size_t cacheDataLength, bool* cacheRejected, JSVM_Value* wasmModule) { JSVM_PREAMBLE(env); CHECK_ARG(env, wasmBytecode); RETURN_STATUS_IF_FALSE(env, wasmBytecodeLength > 0, JSVM_INVALID_ARG); v8::MaybeLocal maybeModule; if (cacheData == nullptr) { maybeModule = v8::WasmModuleObject::Compile(env->isolate, { wasmBytecode, wasmBytecodeLength }); } else { RETURN_STATUS_IF_FALSE(env, cacheDataLength > 0, JSVM_INVALID_ARG); bool rejected; maybeModule = v8::WasmModuleObject::DeserializeOrCompile(env->isolate, { wasmBytecode, wasmBytecodeLength }, { cacheData, cacheDataLength }, rejected); if (cacheRejected != nullptr) { *cacheRejected = rejected; } } // To avoid the status code caused by exception being override, check exception once v8 API finished RETURN_IF_EXCEPTION_HAS_CAUGHT(env); CHECK_MAYBE_EMPTY(env, maybeModule, JSVM_GENERIC_FAILURE); *wasmModule = v8impl::JsValueFromV8LocalValue(maybeModule.ToLocalChecked()); return ClearLastError(env); } JSVM_Status OH_JSVM_CompileWasmFunction(JSVM_Env env, JSVM_Value wasmModule, uint32_t functionIndex, JSVM_WasmOptLevel optLevel) { JSVM_PREAMBLE(env); CHECK_ARG(env, wasmModule); v8::Local val = v8impl::V8LocalValueFromJsValue(wasmModule); RETURN_STATUS_IF_FALSE(env, val->IsWasmModuleObject(), JSVM_INVALID_ARG); v8::Local v8WasmModule = val.As(); v8::WasmExecutionTier tier = v8::WasmExecutionTier::kNone; if (optLevel == JSVM_WASM_OPT_BASELINE) { // v8 liftoff has bug, keep BASELINE same as HIGH. tier = v8::WasmExecutionTier::kTurbofan; } else if (optLevel == JSVM_WASM_OPT_HIGH) { tier = v8::WasmExecutionTier::kTurbofan; } else { // Unsupported optLevel return SetLastError(env, JSVM_INVALID_ARG); } bool compileSuccess = v8WasmModule->CompileFunction(env->isolate, functionIndex, tier); // To avoid the status code caused by exception being override, check exception once v8 API finished RETURN_IF_EXCEPTION_HAS_CAUGHT(env); RETURN_STATUS_IF_FALSE(env, compileSuccess, JSVM_GENERIC_FAILURE); return ClearLastError(env); } JSVM_Status OH_JSVM_IsWasmModuleObject(JSVM_Env env, JSVM_Value value, bool* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 // calls here cannot throw JS exceptions. CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *result = val->IsWasmModuleObject(); return ClearLastError(env); } JSVM_Status OH_JSVM_CreateWasmCache(JSVM_Env env, JSVM_Value wasmModule, const uint8_t** data, size_t* length) { JSVM_PREAMBLE(env); CHECK_ARG(env, wasmModule); CHECK_ARG(env, data); CHECK_ARG(env, length); v8::Local val = v8impl::V8LocalValueFromJsValue(wasmModule); RETURN_STATUS_IF_FALSE(env, val->IsWasmModuleObject(), JSVM_INVALID_ARG); v8::Local v8WasmModule = val.As(); v8::CompiledWasmModule compiledWasmModule = v8WasmModule->GetCompiledModule(); v8::OwnedBuffer serializedBytes = compiledWasmModule.Serialize(); // To avoid the status code caused by exception being override, check exception once v8 API finished RETURN_IF_EXCEPTION_HAS_CAUGHT(env); // If buffer size is 0, create wasm cache failed. RETURN_STATUS_IF_FALSE(env, serializedBytes.size > 0, JSVM_GENERIC_FAILURE); *data = serializedBytes.buffer.get(); *length = serializedBytes.size; // Release the ownership of buffer, OH_JSVM_ReleaseCache must be called explicitly to release the buffer serializedBytes.buffer.release(); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_ReleaseCache(JSVM_Env env, const uint8_t* cacheData, JSVM_CacheType cacheType) { CHECK_ENV(env); CHECK_ARG(env, cacheData); if (cacheType == JSVM_CACHE_TYPE_JS) { // The release behavior MUST match the memory allocation of OH_JSVM_CreateCodeCache. delete[] cacheData; } else if (cacheType == JSVM_CACHE_TYPE_WASM) { // The release behavior MUST match the memory allocation of OH_JSVM_CreateWasmCache. delete[] cacheData; } else { // Unsupported cacheType return SetLastError(env, JSVM_INVALID_ARG); } return ClearLastError(env); } JSVM_Status OH_JSVM_IsBooleanObject(JSVM_Env env, JSVM_Value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local v = v8impl::V8LocalValueFromJsValue(value); *result = v->IsBooleanObject(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsBigIntObject(JSVM_Env env, JSVM_Value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local v = v8impl::V8LocalValueFromJsValue(value); *result = v->IsBigIntObject(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsStringObject(JSVM_Env env, JSVM_Value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local v = v8impl::V8LocalValueFromJsValue(value); *result = v->IsStringObject(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsNumberObject(JSVM_Env env, JSVM_Value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local v = v8impl::V8LocalValueFromJsValue(value); *result = v->IsNumberObject(); return ClearLastError(env); } JSVM_Status OH_JSVM_IsSymbolObject(JSVM_Env env, JSVM_Value value, bool* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local v = v8impl::V8LocalValueFromJsValue(value); *result = v->IsSymbolObject(); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolToStringTag(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolToStringTag = v8::Symbol::GetToStringTag(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolToStringTag); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolIterator(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolIterator = v8::Symbol::GetIterator(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolIterator); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolAsyncIterator(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolAsyncIterator = v8::Symbol::GetAsyncIterator(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolAsyncIterator); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolHasInstance(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolHasInstance = v8::Symbol::GetHasInstance(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolHasInstance); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolUnscopables(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolUnscopables = v8::Symbol::GetUnscopables(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolUnscopables); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolIsConcatSpreadable(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolIsConcatSpreadable = v8::Symbol::GetIsConcatSpreadable(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolIsConcatSpreadable); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolMatch(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolMatch = v8::Symbol::GetMatch(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolMatch); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolReplace(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolReplace = v8::Symbol::GetReplace(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolReplace); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolSearch(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolSearch = v8::Symbol::GetSearch(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolSearch); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolSplit(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolSplit = v8::Symbol::GetSplit(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolSplit); return ClearLastError(env); } JSVM_Status OH_JSVM_GetSymbolToPrimitive(JSVM_Env env, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Local symbolToPrimitive = v8::Symbol::GetToPrimitive(env->isolate); *result = v8impl::JsValueFromV8LocalValue(symbolToPrimitive); return ClearLastError(env); } JSVM_Status OH_JSVM_SetMicrotaskPolicy(JSVM_VM vm, JSVM_MicrotaskPolicy policy) { static constexpr v8::MicrotasksPolicy converter[] = { v8::MicrotasksPolicy::kExplicit, v8::MicrotasksPolicy::kAuto }; constexpr size_t policyCount = jsvm::ArraySize(converter); if (!vm || static_cast(policy) >= policyCount) { return JSVM_INVALID_ARG; } auto isolate = reinterpret_cast(vm); isolate->SetMicrotasksPolicy(converter[policy]); return JSVM_OK; } JSVM_Status OH_JSVM_CreateProxy(JSVM_Env env, JSVM_Value target, JSVM_Value handler, JSVM_Value* result) { // Check args is not null JSVM_PREAMBLE(env); CHECK_ARG(env, target); CHECK_ARG(env, handler); CHECK_ARG(env, result); // Check target and handler are v8 Object auto localTarget = v8impl::V8LocalValueFromJsValue(target); RETURN_STATUS_IF_FALSE(env, localTarget->IsObject(), JSVM_OBJECT_EXPECTED); auto localHandler = v8impl::V8LocalValueFromJsValue(handler); RETURN_STATUS_IF_FALSE(env, localHandler->IsObject(), JSVM_OBJECT_EXPECTED); v8::Local context = env->context(); v8::MaybeLocal maybeProxy = v8::Proxy::New(context, localTarget.As(), localHandler.As()); CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybeProxy, JSVM_GENERIC_FAILURE); v8::Local proxy = maybeProxy.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(proxy); return ClearLastError(env); } JSVM_Status OH_JSVM_IsProxy(JSVM_Env env, JSVM_Value value, bool* isProxy) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, isProxy); v8::Local val = v8impl::V8LocalValueFromJsValue(value); *isProxy = val->IsProxy(); return ClearLastError(env); } JSVM_Status OH_JSVM_ProxyGetTarget(JSVM_Env env, JSVM_Value value, JSVM_Value* result) { CHECK_ENV(env); CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); RETURN_STATUS_IF_FALSE(env, val->IsProxy(), JSVM_INVALID_TYPE); *result = v8impl::JsValueFromV8LocalValue(val.As()->GetTarget()); return ClearLastError(env); } // ref for data can not be weak, so initialRefcount must be greater than 0 JSVM_Status OH_JSVM_CreateDataReference(JSVM_Env env, JSVM_Data data, uint32_t initialRefcount, JSVM_Ref* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, data); CHECK_ARG(env, result); RETURN_STATUS_IF_FALSE(env, initialRefcount != 0, JSVM_INVALID_ARG); v8::Local v8_value = v8impl::V8LocalDataFromJsData(data); v8impl::UserReference* reference = v8impl::UserReference::NewData(env, v8_value, initialRefcount); *result = reinterpret_cast(reference); return ClearLastError(env); } // Attempts to get a referenced value. If the reference is weak, the value might // no longer be available, in that case the call is still successful but the // result is NULL. JSVM_Status OH_JSVM_GetReferenceData(JSVM_Env env, JSVM_Ref ref, JSVM_Data* result) { // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, ref); CHECK_ARG(env, result); v8impl::UserReference* reference = reinterpret_cast(ref); RETURN_STATUS_IF_FALSE(env, !reference->IsValue(), JSVM_INVALID_ARG); *result = v8impl::JsDataFromV8LocalData(reference->GetData()); return ClearLastError(env); } JSVM_Status OH_JSVM_CreatePrivate(JSVM_Env env, JSVM_Value description, JSVM_Data* result) { CHECK_ENV(env); CHECK_ARG(env, result); v8::Isolate* isolate = env->isolate; if (description == nullptr) { *result = v8impl::JsDataFromV8LocalData(v8::Private::New(isolate)); } else { v8::Local v8Name = v8impl::V8LocalValueFromJsValue(description); RETURN_STATUS_IF_FALSE(env, v8Name->IsString(), JSVM_STRING_EXPECTED); *result = v8impl::JsDataFromV8LocalData(v8::Private::New(isolate, v8Name.As())); } return ClearLastError(env); } JSVM_Status OH_JSVM_SetPrivate(JSVM_Env env, JSVM_Value object, JSVM_Data key, JSVM_Value value) { JSVM_PREAMBLE(env); CHECK_ARG(env, object); CHECK_ARG(env, key); CHECK_ARG(env, value); auto context = env->context(); auto obj = v8impl::V8LocalValueFromJsValue(object); RETURN_STATUS_IF_FALSE(env, obj->IsObject(), JSVM_OBJECT_EXPECTED); auto privateKey = v8impl::V8LocalDataFromJsData(key); RETURN_STATUS_IF_FALSE(env, privateKey->IsPrivate(), JSVM_INVALID_ARG); auto val = v8impl::V8LocalValueFromJsValue(value); auto set_maybe = obj.As()->SetPrivate(context, privateKey.As(), val); RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, set_maybe.FromMaybe(false), JSVM_GENERIC_FAILURE); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_GetPrivate(JSVM_Env env, JSVM_Value object, JSVM_Data key, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, object); CHECK_ARG(env, key); CHECK_ARG(env, result); auto context = env->context(); auto obj = v8impl::V8LocalValueFromJsValue(object); RETURN_STATUS_IF_FALSE(env, obj->IsObject(), JSVM_OBJECT_EXPECTED); auto privateKey = v8impl::V8LocalDataFromJsData(key); RETURN_STATUS_IF_FALSE(env, privateKey->IsPrivate(), JSVM_INVALID_ARG); auto getMaybe = obj.As()->GetPrivate(context, privateKey.As()); CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, getMaybe, JSVM_GENERIC_FAILURE); v8::Local val = getMaybe.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(val); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_DeletePrivate(JSVM_Env env, JSVM_Value object, JSVM_Data key) { JSVM_PREAMBLE(env); CHECK_ARG(env, object); CHECK_ARG(env, key); auto context = env->context(); auto obj = v8impl::V8LocalValueFromJsValue(object); RETURN_STATUS_IF_FALSE(env, obj->IsObject(), JSVM_OBJECT_EXPECTED); auto privateKey = v8impl::V8LocalDataFromJsData(key); RETURN_STATUS_IF_FALSE(env, privateKey->IsPrivate(), JSVM_INVALID_ARG); auto deleteMaybe = obj.As()->DeletePrivate(context, privateKey.As()); auto success = deleteMaybe.IsJust() && deleteMaybe.FromMaybe(false); RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, success, JSVM_GENERIC_FAILURE); return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_CreateExternalStringLatin1(JSVM_Env env, char* str, size_t length, JSVM_Finalize finalizeCallback, void* finalizeHint, JSVM_Value* result, bool* copied) { CHECK_ARG(env, copied); return v8impl::NewExternalString(env, str, length, finalizeCallback, finalizeHint, result, copied, OH_JSVM_CreateStringLatin1, [&](v8::Isolate* isolate) { if (length == JSVM_AUTO_LENGTH) { length = (std::string_view(str)).length(); } auto resource = new v8impl::ExternalOneByteStringResource(env, str, length, finalizeCallback, finalizeHint); return v8::String::NewExternalOneByte(isolate, resource); }); } JSVM_Status OH_JSVM_CreateExternalStringUtf16(JSVM_Env env, char16_t* str, size_t length, JSVM_Finalize finalizeCallback, void* finalizeHint, JSVM_Value* result, bool* copied) { CHECK_ARG(env, copied); return v8impl::NewExternalString(env, str, length, finalizeCallback, finalizeHint, result, copied, OH_JSVM_CreateStringUtf16, [&](v8::Isolate* isolate) { if (length == JSVM_AUTO_LENGTH) { length = (std::u16string_view(str)).length(); } auto resource = new v8impl::ExternalStringResource(env, str, length, finalizeCallback, finalizeHint); return v8::String::NewExternalTwoByte(isolate, resource); }); } JSVM_GCType GetJSVMGCType(v8::GCType gcType) { switch (gcType) { case v8::GCType::kGCTypeScavenge: return JSVM_GC_TYPE_SCAVENGE; case v8::GCType::kGCTypeMinorMarkCompact: return JSVM_GC_TYPE_MINOR_MARK_COMPACT; case v8::GCType::kGCTypeMarkSweepCompact: return JSVM_GC_TYPE_MARK_SWEEP_COMPACT; case v8::GCType::kGCTypeIncrementalMarking: return JSVM_GC_TYPE_INCREMENTAL_MARKING; case v8::GCType::kGCTypeProcessWeakCallbacks: return JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS; default: return JSVM_GC_TYPE_ALL; } } static v8::GCType GetV8GCType(JSVM_GCType gcType) { switch (gcType) { case JSVM_GC_TYPE_SCAVENGE: return v8::GCType::kGCTypeScavenge; case JSVM_GC_TYPE_MINOR_MARK_COMPACT: return v8::GCType::kGCTypeMinorMarkCompact; case JSVM_GC_TYPE_MARK_SWEEP_COMPACT: return v8::GCType::kGCTypeMarkSweepCompact; case JSVM_GC_TYPE_INCREMENTAL_MARKING: return v8::GCType::kGCTypeIncrementalMarking; case JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS: return v8::GCType::kGCTypeProcessWeakCallbacks; default: return v8::GCType::kGCTypeAll; } } JSVM_GCCallbackFlags GetJSVMGCCallbackFlags(v8::GCCallbackFlags flag) { switch (flag) { case v8::GCCallbackFlags::kGCCallbackFlagConstructRetainedObjectInfos: return JSVM_GC_CALLBACK_CONSTRUCT_RETAINED_OBJECT_INFOS; case v8::GCCallbackFlags::kGCCallbackFlagForced: return JSVM_GC_CALLBACK_FORCED; case v8::GCCallbackFlags::kGCCallbackFlagSynchronousPhantomCallbackProcessing: return JSVM_GC_CALLBACK_SYNCHRONOUS_PHANTOM_CALLBACK_PROCESSING; case v8::GCCallbackFlags::kGCCallbackFlagCollectAllAvailableGarbage: return JSVM_GC_CALLBACK_COLLECT_ALL_AVAILABLE_GARBAGE; case v8::GCCallbackFlags::kGCCallbackFlagCollectAllExternalMemory: return JSVM_GC_CALLBACK_COLLECT_ALL_EXTERNAL_MEMORY; case v8::GCCallbackFlags::kGCCallbackScheduleIdleGarbageCollection: return JSVM_GC_CALLBACK_SCHEDULE_IDLE_GARBAGE_COLLECTION; default: return JSVM_NO_GC_CALLBACK_FLAGS; } } static void OnBeforeGC(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags, void* data) { DCHECK_NOT_NULL(v8impl::GetIsolateHandlerPool(isolate)); JSVM_GCType gcType = GetJSVMGCType(type); JSVM_GCCallbackFlags gcFlags = GetJSVMGCCallbackFlags(flags); auto* gcHandlerWrapper = (v8impl::GCHandlerWrapper*)data; gcHandlerWrapper->handler(reinterpret_cast(isolate), gcType, gcFlags, gcHandlerWrapper->userData); } static void OnAfterGC(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags, void* data) { DCHECK_NOT_NULL(v8impl::GetIsolateHandlerPool(isolate)); JSVM_GCType gcType = GetJSVMGCType(type); JSVM_GCCallbackFlags gcFlags = GetJSVMGCCallbackFlags(flags); auto* gcHandlerWrapper = (v8impl::GCHandlerWrapper*)data; gcHandlerWrapper->handler(reinterpret_cast(isolate), gcType, gcFlags, gcHandlerWrapper->userData); } JSVM_Status OH_JSVM_AddHandlerForGC(JSVM_VM vm, JSVM_CBTriggerTimeForGC triggerTime, JSVM_HandlerForGC handler, JSVM_GCType gcType, void* data) { if (!vm || !handler) { return JSVM_INVALID_ARG; } auto* isolate = reinterpret_cast(vm); auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); auto& handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; auto it = std::find_if(handlers.begin(), handlers.end(), [handler, data](v8impl::GCHandlerWrapper* callbackData) { return callbackData->handler == handler && callbackData->userData == data; }); if (it != handlers.end()) { return JSVM_INVALID_ARG; } auto* callbackData = new v8impl::GCHandlerWrapper(gcType, handler, data); handlers.push_back(callbackData); if (triggerTime == JSVM_CB_TRIGGER_BEFORE_GC) { isolate->AddGCPrologueCallback(OnBeforeGC, callbackData, GetV8GCType(gcType)); } else { isolate->AddGCEpilogueCallback(OnAfterGC, callbackData, GetV8GCType(gcType)); } return JSVM_OK; } JSVM_Status OH_JSVM_RemoveHandlerForGC(JSVM_VM vm, JSVM_CBTriggerTimeForGC triggerTime, JSVM_HandlerForGC handler, void* userData) { if (!vm || !handler) { return JSVM_INVALID_ARG; } auto* isolate = reinterpret_cast(vm); auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); if (pool == nullptr) { return JSVM_INVALID_ARG; } auto& handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; auto it = std::find_if(handlers.begin(), handlers.end(), [handler, userData](v8impl::GCHandlerWrapper* callbackData) { return callbackData->handler == handler && callbackData->userData == userData; }); if (it == handlers.end()) { return JSVM_INVALID_ARG; } handlers.erase(it); if (triggerTime == JSVM_CB_TRIGGER_BEFORE_GC) { isolate->RemoveGCPrologueCallback(OnBeforeGC, (*it)); } else { isolate->RemoveGCEpilogueCallback(OnAfterGC, (*it)); } delete (*it); return JSVM_OK; } static void OnOOMError(const char* location, const v8::OOMDetails& details) { auto* isolate = v8::Isolate::GetCurrent(); auto* pool = v8impl::GetIsolateHandlerPool(isolate); if (pool == nullptr) { return; } auto* handler = pool->handlerForOOMError; if (handler == nullptr) { return; } (*handler)(location, details.detail, details.is_heap_oom); } JSVM_Status OH_JSVM_SetHandlerForOOMError(JSVM_VM vm, JSVM_HandlerForOOMError handler) { if (vm == nullptr) { return JSVM_INVALID_ARG; } auto* isolate = reinterpret_cast(vm); auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); pool->handlerForOOMError = handler; isolate->SetOOMErrorHandler(OnOOMError); return JSVM_OK; } static void OnFatalError(const char* location, const char* message) { auto* isolate = v8::Isolate::GetCurrent(); auto* pool = v8impl::GetIsolateHandlerPool(isolate); if (pool == nullptr) { return; } auto* handler = pool->handlerForFatalError; if (handler == nullptr) { return; } (*handler)(location, message); } JSVM_Status OH_JSVM_SetHandlerForFatalError(JSVM_VM vm, JSVM_HandlerForFatalError handler) { if (vm == nullptr) { return JSVM_INVALID_ARG; } auto* isolate = reinterpret_cast(vm); auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); pool->handlerForFatalError = handler; isolate->SetFatalErrorHandler(OnFatalError); return JSVM_OK; } static void OnPromiseReject(v8::PromiseRejectMessage rejectMessage) { auto* isolate = v8::Isolate::GetCurrent(); auto* pool = v8impl::GetIsolateHandlerPool(isolate); if (pool == nullptr) { return; } auto* handler = pool->handlerForPromiseReject; if (handler == nullptr) { return; } auto context = isolate->GetCurrentContext(); auto env = v8impl::GetEnvByContext(context); v8::HandleScope scope(isolate); v8::Local rejectInfo = v8::Object::New(isolate); auto strPromise = v8::String::NewFromUtf8(isolate, "promise").ToLocalChecked(); (void)rejectInfo->Set(context, strPromise, rejectMessage.GetPromise()); auto strValue = v8::String::NewFromUtf8(isolate, "value").ToLocalChecked(); (void)rejectInfo->Set(context, strValue, rejectMessage.GetValue()); JSVM_Value jsvmRejectInfo = v8impl::JsValueFromV8LocalValue(rejectInfo); JSVM_PromiseRejectEvent rejectEvent = JSVM_PROMISE_REJECT_OTHER_REASONS; switch (rejectMessage.GetEvent()) { case v8::kPromiseRejectWithNoHandler: { rejectEvent = JSVM_PROMISE_REJECT_WITH_NO_HANDLER; break; } case v8::kPromiseHandlerAddedAfterReject: { rejectEvent = JSVM_PROMISE_ADD_HANDLER_AFTER_REJECTED; break; } case v8::kPromiseRejectAfterResolved: { rejectEvent = JSVM_PROMISE_REJECT_AFTER_RESOLVED; break; } case v8::kPromiseResolveAfterResolved: { rejectEvent = JSVM_PROMISE_RESOLVE_AFTER_RESOLVED; break; } default: { rejectEvent = JSVM_PROMISE_REJECT_OTHER_REASONS; } } (*handler)(env, rejectEvent, jsvmRejectInfo); } JSVM_Status OH_JSVM_SetHandlerForPromiseReject(JSVM_VM vm, JSVM_HandlerForPromiseReject handler) { if (vm == nullptr) { return JSVM_INVALID_ARG; } auto* isolate = reinterpret_cast(vm); auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); pool->handlerForPromiseReject = handler; isolate->SetPromiseRejectCallback(OnPromiseReject); return JSVM_OK; } JSVM_EXTERN JSVM_Status OH_JSVM_TraceStart(size_t count, const JSVM_TraceCategory* categories, const char* tag, size_t eventsCount) { if (count > v8impl::TRACE_CATEGORY_COUNT || ((count != 0) != (categories != nullptr))) { return JSVM_INVALID_ARG; } for (size_t i = 0; i < count; ++i) { if (categories[i] >= v8impl::TRACE_CATEGORY_COUNT) { return JSVM_INVALID_ARG; } } using namespace v8::platform::tracing; TraceConfig* trace_config = new TraceConfig(); if (count == 0) { count = v8impl::DEFAULT_CATEGORY_COUNT; categories = v8impl::DEFAULT_CATAGORIES; } for (size_t i = 0; i < count; ++i) { trace_config->AddIncludedCategory(v8impl::INTERNAL_TRACE_CATEGORIES[categories[i]]); } v8::Platform* platform = v8impl::g_platform.get(); TracingController* controller = static_cast(platform->GetTracingController()); v8impl::g_trace_stream.reset(new std::stringstream()); auto stream = v8impl::g_trace_stream.get(); TraceWriter* writer = nullptr; if (tag != nullptr) { writer = TraceWriter::CreateJSONTraceWriter(*stream, tag); } else { writer = TraceWriter::CreateJSONTraceWriter(*stream); } size_t max_chunks; if (eventsCount != 0) { size_t chunk_size = TraceBufferChunk::kChunkSize; max_chunks = (eventsCount + chunk_size - 1) / chunk_size; } else { max_chunks = TraceBuffer::kRingBufferChunks; } TraceBuffer* ring_buffer = TraceBuffer::CreateTraceBufferRingBuffer(max_chunks, writer); controller->Initialize(ring_buffer); controller->StartTracing(trace_config); return JSVM_OK; } JSVM_Status OH_JSVM_TraceStop(JSVM_OutputStream stream, void* streamData) { if (stream == nullptr || streamData == nullptr || v8impl::g_trace_stream.get() == nullptr) { return JSVM_INVALID_ARG; } using namespace v8::platform::tracing; v8::Platform* platform = v8impl::g_platform.get(); auto controller = static_cast(platform->GetTracingController()); DCHECK(controller != nullptr); controller->StopTracing(); // Call the destructor of TraceBuffer to print the JSON end. controller->Initialize(nullptr); std::string output = v8impl::g_trace_stream.get()->rdbuf()->str(); stream(output.c_str(), output.size(), streamData); v8impl::g_trace_stream.reset(nullptr); return JSVM_OK; } JSVM_Status ProcessPropertyHandler(JSVM_Env env, v8::Local tpl, JSVM_PropertyHandlerCfg propertyHandlerCfg, JSVM_Callback callAsFunctionCallback, v8impl::JSVM_PropertyHandlerCfgStruct** propertyHandlerCfgStruct) { CHECK_ARG(env, propertyHandlerCfg); *propertyHandlerCfgStruct = v8impl::CreatePropertyCfg(env, propertyHandlerCfg); if (*propertyHandlerCfgStruct == nullptr) { return JSVM_GENERIC_FAILURE; } v8::Local cbdata = v8impl::CallbackBundle::New(env, *propertyHandlerCfgStruct); // register named property handler v8::NamedPropertyHandlerConfiguration namedPropertyHandler; if (propertyHandlerCfg->genericNamedPropertyGetterCallback) { namedPropertyHandler.getter = v8impl::PropertyCallbackWrapper::NameGetterInvoke; } if (propertyHandlerCfg->genericNamedPropertySetterCallback) { namedPropertyHandler.setter = v8impl::PropertyCallbackWrapper::NameSetterInvoke; } if (propertyHandlerCfg->genericNamedPropertyDeleterCallback) { namedPropertyHandler.deleter = v8impl::PropertyCallbackWrapper::NameDeleterInvoke; } if (propertyHandlerCfg->genericNamedPropertyEnumeratorCallback) { namedPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper::NameEnumeratorInvoke; } namedPropertyHandler.data = cbdata; tpl->InstanceTemplate()->SetHandler(namedPropertyHandler); // register indexed property handle v8::IndexedPropertyHandlerConfiguration indexPropertyHandler; if (propertyHandlerCfg->genericIndexedPropertyGetterCallback) { indexPropertyHandler.getter = v8impl::PropertyCallbackWrapper::IndexGetterInvoke; } if (propertyHandlerCfg->genericIndexedPropertySetterCallback) { indexPropertyHandler.setter = v8impl::PropertyCallbackWrapper::IndexSetterInvoke; } if (propertyHandlerCfg->genericIndexedPropertyDeleterCallback) { indexPropertyHandler.deleter = v8impl::PropertyCallbackWrapper::IndexDeleterInvoke; } if (propertyHandlerCfg->genericIndexedPropertyEnumeratorCallback) { indexPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper::IndexEnumeratorInvoke; } indexPropertyHandler.data = cbdata; tpl->InstanceTemplate()->SetHandler(indexPropertyHandler); // register call as function if (callAsFunctionCallback && callAsFunctionCallback->callback) { v8::Local funcCbdata = v8impl::CallbackBundle::New(env, callAsFunctionCallback); tpl->InstanceTemplate()->SetCallAsFunctionHandler(v8impl::FunctionCallbackWrapper::Invoke, funcCbdata); } return JSVM_OK; } class DefineClassOptionsResolver { public: void ProcessOptions(size_t length, JSVM_DefineClassOptions options[], JSVM_Env env, v8::Local tpl) { for (size_t i = 0; i < length; i++) { if (status != JSVM_OK) { break; } switch (options[i].id) { case JSVM_DEFINE_CLASS_NORMAL: break; case JSVM_DEFINE_CLASS_WITH_COUNT: { auto count = options[i].content.num; v8::Local instance_templ = tpl->InstanceTemplate(); instance_templ->SetInternalFieldCount(count); break; } case JSVM_DEFINE_CLASS_WITH_PROPERTY_HANDLER: { hasPropertyHandle = true; auto* propertyHandle = static_cast(options[i].content.ptr); propertyHandlerCfg = propertyHandle->propertyHandlerCfg; callAsFunctionCallback = propertyHandle->callAsFunctionCallback; status = ProcessPropertyHandler(env, tpl, propertyHandlerCfg, callAsFunctionCallback, &propertyHandlerCfgStruct); break; } default: { status = JSVM_INVALID_ARG; } } } } JSVM_Status GetStatus() { return status; } v8impl::JSVM_PropertyHandlerCfgStruct* GetPropertyHandler() { return propertyHandlerCfgStruct; } bool HasPropertyHandler() { return hasPropertyHandle; } private: JSVM_PropertyHandlerCfg propertyHandlerCfg = nullptr; JSVM_Callback callAsFunctionCallback = nullptr; bool hasPropertyHandle = false; JSVM_Status status = JSVM_OK; v8impl::JSVM_PropertyHandlerCfgStruct* propertyHandlerCfgStruct = nullptr; }; JSVM_Status OH_JSVM_DefineClassWithOptions(JSVM_Env env, const char* utf8name, size_t length, JSVM_Callback constructor, size_t propertyCount, const JSVM_PropertyDescriptor* properties, JSVM_Value parentClass, size_t optionCount, JSVM_DefineClassOptions options[], JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, result); CHECK_ARG(env, constructor); CHECK_ARG(env, constructor->callback); if (propertyCount > 0) { CHECK_ARG(env, properties); } v8::Isolate* isolate = env->isolate; v8::EscapableHandleScope scope(isolate); v8::Local tpl; STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, constructor, &tpl)); v8::Local nameString; CHECK_NEW_FROM_UTF8_LEN(env, nameString, utf8name, length); tpl->SetClassName(nameString); size_t staticPropertyCount = 0; for (size_t i = 0; i < propertyCount; i++) { const JSVM_PropertyDescriptor* p = properties + i; if ((p->attributes & JSVM_STATIC) != 0) { // attributes // Static properties are handled separately below. staticPropertyCount++; continue; } v8::Local propertyName; STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &propertyName)); v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); // This code is similar to that in OH_JSVM_DefineProperties(); the // and preferred PropertyAttribute for lack of PropertyDescriptor // support on ObjectTemplate. if (p->getter != nullptr || p->setter != nullptr) { v8::Local getterTpl; v8::Local setterTpl; if (p->getter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->getter, &getterTpl)); } if (p->setter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->setter, &setterTpl)); } tpl->PrototypeTemplate()->SetAccessorProperty(propertyName, getterTpl, setterTpl, attributes, v8::AccessControl::DEFAULT); } else if (p->method != nullptr) { v8::Local temp; STATUS_CALL( v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &temp, v8::Signature::New(isolate, tpl))); tpl->PrototypeTemplate()->Set(propertyName, temp, attributes); } else { v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); tpl->PrototypeTemplate()->Set(propertyName, value, attributes); } } if (parentClass != nullptr) { v8::Local parentFunc; CHECK_TO_FUNCTION(env, parentFunc, parentClass); if (!tpl->Inherit(parentFunc)) { return JSVM_INVALID_ARG; } } DefineClassOptionsResolver optionResolver; optionResolver.ProcessOptions(optionCount, options, env, tpl); if (optionResolver.GetStatus() != JSVM_OK) { return optionResolver.GetStatus(); } v8::Local context = env->context(); *result = v8impl::JsValueFromV8LocalValue(scope.Escape(tpl->GetFunction(context).ToLocalChecked())); if (optionResolver.HasPropertyHandler()) { v8impl::RuntimeReference::New(env, v8impl::V8LocalValueFromJsValue(*result), v8impl::CfgFinalizedCallback, optionResolver.GetPropertyHandler(), nullptr); } if (staticPropertyCount > 0) { std::vector static_descriptors; static_descriptors.reserve(staticPropertyCount); for (size_t i = 0; i < propertyCount; i++) { const JSVM_PropertyDescriptor* p = properties + i; if ((p->attributes & JSVM_STATIC) != 0) { static_descriptors.push_back(*p); } } STATUS_CALL(OH_JSVM_DefineProperties(env, *result, static_descriptors.size(), static_descriptors.data())); } return GET_RETURN_STATUS(env); } JSVM_Status OH_JSVM_PromiseRegisterHandler(JSVM_Env env, JSVM_Value promise, JSVM_Value onFulfilled, JSVM_Value onRejected, JSVM_Value* result) { JSVM_PREAMBLE(env); CHECK_ARG(env, promise); RETURN_STATUS_IF_FALSE(env, onFulfilled || onRejected, JSVM_INVALID_ARG); v8::Local value = v8impl::V8LocalValueFromJsValue(promise); RETURN_STATUS_IF_FALSE(env, value->IsPromise(), JSVM_INVALID_TYPE); auto localPromise = value.As(); v8::Local ctx = env->context(); v8::MaybeLocal maybe; if (!onFulfilled) { // Only pass onRejected, call v8::Promise::Catch auto rejectedHandler = v8impl::V8LocalValueFromJsValue(onRejected); RETURN_STATUS_IF_FALSE(env, rejectedHandler->IsFunction(), JSVM_INVALID_TYPE); maybe = localPromise->Catch(ctx, rejectedHandler.As()); } else if (!onRejected) { // Only pass onFulfilled, call v8::Promise::Then auto fulfiledHandler = v8impl::V8LocalValueFromJsValue(onFulfilled); RETURN_STATUS_IF_FALSE(env, fulfiledHandler->IsFunction(), JSVM_INVALID_TYPE); maybe = value.As()->Then(ctx, fulfiledHandler.As()); } else { // Pass onFulfilled and onRejected, call v8::Promise::Then auto fulfiledHandler = v8impl::V8LocalValueFromJsValue(onFulfilled); RETURN_STATUS_IF_FALSE(env, fulfiledHandler->IsFunction(), JSVM_INVALID_TYPE); auto rejectedHandler = v8impl::V8LocalValueFromJsValue(onRejected); RETURN_STATUS_IF_FALSE(env, rejectedHandler->IsFunction(), JSVM_INVALID_TYPE); maybe = value.As()->Then(ctx, fulfiledHandler.As(), rejectedHandler.As()); } CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, JSVM_GENERIC_FAILURE); if (result) { auto retPromise = maybe.ToLocalChecked(); *result = v8impl::JsValueFromV8LocalValue(retPromise); } return ClearLastError(env); }