From 71a7dc25ab66ae555bdc8db46081774f4ce05730 Mon Sep 17 00:00:00 2001 From: chenraozhong Date: Tue, 26 Nov 2024 17:45:30 +0800 Subject: [PATCH] Add Define_Class_with_options Signed-off-by: chenraozhong --- src/js_native_api_v8.cc | 225 ++++++++++++++++++++++++++++++++++++++++ src/jsvm.h | 54 ++++++++-- src/jsvm_types.h | 45 ++++++++ 3 files changed, 317 insertions(+), 7 deletions(-) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index a61038398..054adc48a 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -4891,6 +4891,231 @@ OH_JSVM_DefineClassWithPropertyHandler(JSVM_Env env, return GET_RETURN_STATUS(env); } +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_set_last_error(env, 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_clear_last_error(env); +} + +class DefineClassOptionsResolver { + public: + void ProcessOptions(size_t length, JSVM_DefineClassOptions options[], JSVM_Env env, + v8::Local tpl) { + for (int32_t i = 0; i < length; i++) { + if (status != JSVM_OK) { + break; + } + switch(options[i].id) { + case JSVM_DEFINECLASS_NORMAL: + break; + case JSVM_DEFINECLASS_WITH_COUNT: { + auto count = options[i].content.count; + v8::Local instance_templ = tpl->InstanceTemplate(); + instance_templ->SetInternalFieldCount(count); + break; + } + case JSVM_DEFINECLASS_WITH_PROPERTY_HANDLER: { + has_propertyHandle = 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; + } + } + } + status = JSVM_OK; + } + + JSVM_Status GetStatus() { + return status; + } + + v8impl::JSVM_PropertyHandlerCfgStruct* GetPropertyHandler() { + return propertyHandlerCfgStruct; + } + + bool HasPropertyHandler() { + return has_propertyHandle; + } + + private: + JSVM_PropertyHandlerCfg propertyHandlerCfg = nullptr; + JSVM_Callback callAsFunctionCallback = nullptr; + bool has_propertyHandle = false; + JSVM_Status status = JSVM_OK; + v8impl::JSVM_PropertyHandlerCfgStruct* propertyHandlerCfgStruct = nullptr; +}; + +JSVM_Status JSVM_CDECL +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 option_count, + 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 name_string; + CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); + tpl->SetClassName(name_string); + + size_t static_property_count = 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. + static_property_count++; + continue; + } + + v8::Local property_name; + STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name)); + 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 getter_tpl; + v8::Local setter_tpl; + if (p->getter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, p->getter, &getter_tpl)); + } + if (p->setter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, p->setter, &setter_tpl)); + } + + tpl->PrototypeTemplate()->SetAccessorProperty(property_name, + getter_tpl, + setter_tpl, + attributes, + v8::AccessControl::DEFAULT); + } else if (p->method != nullptr) { + v8::Local t; + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, p->method, &t, v8::Signature::New(isolate, tpl))); + + tpl->PrototypeTemplate()->Set(property_name, t, attributes); + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + tpl->PrototypeTemplate()->Set(property_name, value, attributes); + } + } + + if (parentClass != nullptr) { + v8::Local parentFunc; + CHECK_TO_FUNCTION(env, parentFunc, parentClass); + tpl->Inherit(parentFunc); + } + + DefineClassOptionsResolver optionResolver; + optionResolver.ProcessOptions(option_count, 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::Reference::New(env, v8impl::V8LocalValueFromJsValue(*result), 0, v8impl::Ownership::kRuntime, + v8impl::CfgFinalizedCallback, optionResolver.GetPropertyHandler(), nullptr); + } + + if (static_property_count > 0) { + std::vector static_descriptors; + static_descriptors.reserve(static_property_count); + + 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 JSVM_CDECL OH_JSVM_IsLocked(JSVM_Env env, bool* isLocked) { CHECK_ENV(env); diff --git a/src/jsvm.h b/src/jsvm.h index 89ce7ffbd..c6306ac32 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -102,7 +102,7 @@ EXTERN_C_START /** * @brief Init a JavaScript vm. * - * @param options: The options for initialize the JavaScript VM. + * @param options The options for initialize the JavaScript VM. * @return Returns JSVM funtions result code. * Returns {@link JSVM_OK } in all cases.\n * @since 11 @@ -1546,9 +1546,9 @@ JSVM_EXTERN JSVM_Status OH_JSVM_IsArraybuffer(JSVM_Env env, /** * @brief This API checks if the Object passed in is a date. * - * @param env: The environment that the API is invoked under. - * @param value: The JavaScript value to check. - * @param result: Whether the given JSVM_Value represents a JavaScript Date object. + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param isDate Whether the given JSVM_Value represents a JavaScript Date object. * @return Returns JSVM funtions result code. * {@link JSVM_OK } If the function executed successfully.\n * @since 11 @@ -2522,8 +2522,8 @@ JSVM_EXTERN JSVM_Status OH_JSVM_ReleaseLock(JSVM_Env env); * @brief Starts the running of the task queue inside the VM. * This task queue can be executed by an external event loop. * - * @param env: The VM instance on which to start the task queue. - * @param result: Whether the task queue was successfully started. + * @param vm The VM instance on which to start the task queue. + * @param result Whether the task queue was successfully started. * @return Returns JSVM funtions result code. * {@link JSVM_OK } If the function executed successfully.\n * @since 12 @@ -2534,7 +2534,7 @@ JSVM_EXTERN JSVM_Status OH_JSVM_PumpMessageLoop(JSVM_VM vm, /** * @brief Check to see if there are any microtasks waiting in the queue, and if there are, execute them. * - * @param env: The VM instance on which to check microtasks. + * @param vm The VM instance on which to check microtasks. * @return Returns JSVM funtions result code. * {@link JSVM_OK } If the function executed successfully.\n * @since 12 @@ -3045,6 +3045,46 @@ JSVM_EXTERN JSVM_Status OH_JSVM_CreateWasmCache(JSVM_Env env, JSVM_EXTERN JSVM_Status OH_JSVM_ReleaseCache(JSVM_Env env, const uint8_t* cacheData, JSVM_CacheType cacheType); + +/** + * @brief When wrapping a C++ class, the C++ constructor callback passed via constructor + * should be a static method on the class that calls the actual class constructor, then + * wraps the new C++ instance in a JavaScript object according to the different Options + * passed in, and returns the wrapper object. + * + * @param env The environment that the API is invoked under. + * @param utf8name Name of the JavaScript constructor function. For clarity, it is + * recommended to use the C++ class name when wrapping a C++ class. + * @param length The length of the utf8name in bytes, or JSVM_AUTO_LENGTH if it + * is null-terminated. + * @param constructor Struct include callback function that handles constructing instances of the class. + * When wrapping a C++ class, this method must be a static member with the JSVM_Callback.callback + * signature. A C++ class constructor cannot be used. + * Include Optional data to be passed to the constructor callback as the data + * property of the callback info. JSVM_Callback provides more details. + * @param propertyCount Number of items in the properties array argument. + * @param properties Array of property descriptors describing static and instance data + * properties, accessors, and methods on the class See JSVM_PropertyDescriptor. + * @param parentClass The parent-class of the currently defined class. + * @param option_count Number of items in an option array argument. + * @param options DefineClass options to be passed. + * @param result A JSVM_Value representing the constructor function for the class. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the function executed successfully. \n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL. \n + * {@link JSVM_GENERIC_FAILURE} if the input utf8name | constructor | properties is invalid. \n + * @since 16 + */ +JSVM_EXTERN 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 option_count, + JSVM_DefineClassOptions options[], + JSVM_Value* result); EXTERN_C_END /** @} */ #endif /* ARK_RUNTIME_JSVM_JSVM_H */ diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 4ee1793b3..8d2a303d4 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -770,6 +770,51 @@ typedef enum { /** WebAssembly cache, generated by OH_JSVM_CreateWasmCache */ JSVM_CACHE_TYPE_WASM, } JSVM_CacheType; + +/** + * @brief The property-handler used to define class. + * + * @since 16 + */ +typedef struct { + /** The instance object triggers the corresponding callback function. */ + JSVM_PropertyHandlerCfg propertyHandlerCfg; + /** Calling an instance object as a function will trigger this callback. */ + JSVM_Callback callAsFunctionCallback; +} JSVM_PropertyHandler; + +/** + * @brief DefineClass options id. + * + * @since 16 + */ +typedef enum { + /** Defining a class in normal mode. */ + JSVM_DEFINE_CLASS_NORMAL, + /** Defining a class with count. */ + JSVM_DEFINE_CLASS_WITH_COUNT, + /** Defining a class with property handler. */ + JSVM_DEFINE_CLASS_WITH_PROPERTY_HANDLER, +} JSVM_DefineClassOptionsId; + +/** + * @brief DefineClass options. + * + * @since 16 + */ +typedef struct { + /** DefineClass options id. */ + JSVM_DefineClassOptionsId id; + /** option content. */ + union { + /** for option value with pointer type. */ + void* ptr; + /** for option value with integer type */ + int num; + /** for option value with bool type */ + bool boolean; + } content; +} JSVM_DefineClassOptions; /** @} */ #endif /* ARK_RUNTIME_JSVM_JSVM_TYPE_H */ -- Gitee