1# Working with Class Using JSVM-API 2 3## Introduction 4 5JSVM-API provides APIs for managing JavaScript (JS) classes, for example, defining a JS class and creating a JS instance. 6 7## Basic Concepts 8 9To begin with, it is important to understand the following basic concepts: 10 11- Class: a template used to create an object. It provides a way to define object properties and methods in a structured manner. Classes in JavaScript are based on prototypes. Moreover, unique syntax and semantics of classes are introduced. 12- Instance: an object created from a class. A class defines the structure and behavior of an object, and an instance is a specific representation of a class. Instantiating a class allows access to the properties and methods defined in the class. Each instance has its own property values. 13 14## Available APIs 15 16| API | Description | 17| ------------------- | ---------------------------------- | 18| OH_JSVM_NewInstance | Creates an instance from the given constructor.| 19| OH_JSVM_GetNewTarget | Obtains the meta property **new.target** of a function.| 20| OH_JSVM_DefineClass | Defines a JS class and associated functions within a C/C++ addon. It allows you to define a constructor, methods, and properties that can be accessed from JS.| 21| OH_JSVM_Wrap | Wraps a native instance in a JS object. You can use **OH_JSVM_Unwrap()** to retrieve the native instance later.| 22| OH_JSVM_Unwrap | Unwraps the native instance that is previously encapsulated in a JS object.| 23| OH_JSVM_RemoveWrap | Removes the wrapping after the native instance is unwrapped from a JS object.| 24|OH_JSVM_DefineClassWithOptions | Defines a JS class with the given class name, constructor, properties, callback handler, and parent class. The **DefineClassOptions** parameter specifies whether to set a property proxy for the defined class, reserve the internal-field slot, and set a callback when the class is called as a function.| 25 26## Example 27 28If you are just starting out with JSVM-API, see [JSVM-API Development Process](use-jsvm-process.md). The following demonstrates only the C++ code involved in class development. 29 30### OH_JSVM_NewInstance 31 32Call **OH_JSVM_NewInstance** to create an instance from the given constructor. 33 34CPP code: 35 36```cpp 37// hello.cpp 38#include <string.h> 39 40std::string ToString(JSVM_Env env, JSVM_Value val) { 41 JSVM_Value jsonString; 42 JSVM_CALL(OH_JSVM_JsonStringify(env, val, &jsonString)); 43 size_t totalLen = 0; 44 JSVM_CALL(OH_JSVM_GetValueStringUtf8(env, jsonString, nullptr, 0, &totalLen)); 45 size_t needLen = totalLen + 1; 46 char* buff = new char[needLen]; 47 std::memset(buff, 0, needLen); 48 JSVM_CALL(OH_JSVM_GetValueStringUtf8(env, jsonString, buff, needLen, &totalLen)); 49 std::string str(buff); 50 delete[] buff; 51 return str; 52} 53 54// Define OH_JSVM_NewInstance. 55static JSVM_Value NewInstance(JSVM_Env env, JSVM_CallbackInfo info) { 56 // Obtain the two parameters passed from JS. 57 size_t argc = 2; 58 JSVM_Value args[2] = {nullptr}; 59 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr)); 60 JSVM_Value result = nullptr; 61 // Call OH_JSVM_NewInstance to create an instance and return the instance created. 62 JSVM_CALL(OH_JSVM_NewInstance(env, args[0], 1, &args[1], &result)); 63 std::string str = ToString(env, result); 64 OH_LOG_INFO(LOG_APP, "NewInstance:%{public}s", str.c_str()); 65 return nullptr; 66} 67 68// Create an instance from the given constructor. 69// Register the NewInstance callback. 70static JSVM_CallbackStruct param[] = { 71 {.data = nullptr, .callback = NewInstance}, 72}; 73 74static JSVM_CallbackStruct *method = param; 75 76// Alias for the NewInstance method, which can be called from JS. 77static JSVM_PropertyDescriptor descriptor[] = { 78 {"newInstance", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 79}; 80``` 81 82#### JS Example 83 84const char *srcCallNative = R"JS( 85 function Fruit(name) { 86 this.name = name; 87 } 88 newInstance(Fruit, "apple"); 89)JS"; 90 91#### Execution Result 92 93The following information is displayed in the log: 94NewInstance:{"name":"apple"} 95 96### OH_JSVM_GetNewTarget 97 98Call **OH_JSVM_GetNewTarget** to obtain the **new.target** value in a function. In JS, **new.target** is a special meta-property used to determine whether a function or constructor is called using the **new** operator. 99 100### OH_JSVM_DefineClass 101 102Call **OH_JSVM_DefineClass** to define a JS class and associated functions within a C/C++ addon. It allows you to define a constructor, methods, and properties that can be accessed from JS. 103 104CPP code: 105 106```cpp 107// hello.cpp 108#include <string> 109 110JSVM_Value CreateInstance(JSVM_Env env, JSVM_CallbackInfo info) { 111 JSVM_Value newTarget; 112 // Obtain the new.target value of the constructor. 113 JSVM_CALL(OH_JSVM_GetNewTarget(env, info, &newTarget)); 114 OH_LOG_INFO(LOG_APP, "Create Instance"); 115 OH_LOG_INFO(LOG_APP, "NAPI MyObject::New %{public}s", newTarget != nullptr ? "newTarget != nullptr" : "newTarget == nullptr"); 116 JSVM_Value jsObject = nullptr; 117 JSVM_CALL(OH_JSVM_CreateObject(env, &jsObject)); 118 JSVM_Value jsName = nullptr; 119 JSVM_CALL(OH_JSVM_CreateStringUtf8(env, "name", JSVM_AUTO_LENGTH, &jsName)); 120 JSVM_Value jsValue = nullptr; 121 JSVM_CALL(OH_JSVM_CreateStringUtf8(env, "lilei", JSVM_AUTO_LENGTH, &jsValue)); 122 JSVM_CALL(OH_JSVM_SetProperty(env, jsObject, jsName, jsValue)); 123 return jsObject; 124} 125 126std::string ToString(JSVM_Env env, JSVM_Value val) { 127 JSVM_Value jsonString; 128 JSVM_CALL(OH_JSVM_JsonStringify(env, val, &jsonString)); 129 size_t totalLen = 0; 130 JSVM_CALL(OH_JSVM_GetValueStringUtf8(env, jsonString, nullptr, 0, &totalLen)); 131 size_t needLen = totalLen + 1; 132 char* buff = new char[needLen]; 133 std::memset(buff, 0, needLen); 134 JSVM_CALL(OH_JSVM_GetValueStringUtf8(env, jsonString, buff, needLen, &totalLen)); 135 std::string str(buff); 136 delete[] buff; 137 return str; 138} 139 140// Encapsulate the struct in C++. 141JSVM_Value DefineClass(JSVM_Env env, JSVM_CallbackInfo info) { 142 JSVM_CallbackStruct param; 143 param.data = nullptr; 144 param.callback = CreateInstance; 145 JSVM_Value cons; 146 // Define a class in JS. 147 JSVM_CALL(OH_JSVM_DefineClass(env, "MyObject", JSVM_AUTO_LENGTH, ¶m, 0, nullptr, &cons)); 148 JSVM_Value instanceValue = nullptr; 149 // Called as a constructor of the class. 150 JSVM_CALL(OH_JSVM_NewInstance(env, cons, 0, nullptr, &instanceValue)); 151 std::string str = ToString(env, instanceValue); 152 OH_LOG_INFO(LOG_APP, "NewInstance:%{public}s", str.c_str()); 153 154 // Called as a common function. 155 JSVM_Value global; 156 JSVM_CALL(OH_JSVM_GetGlobal(env, &global)); 157 JSVM_Value key; 158 JSVM_CALL(OH_JSVM_CreateStringUtf8(env, "Constructor", JSVM_AUTO_LENGTH, &key)); 159 JSVM_CALL(OH_JSVM_SetProperty(env, global, key, cons)); 160 JSVM_Value result; 161 JSVM_CALL(OH_JSVM_CallFunction(env, global, cons, 0, nullptr, &result)); 162 std::string buf = ToString(env, result); 163 OH_LOG_INFO(LOG_APP, "NewInstance:%{public}s", buf.c_str()); 164 return nullptr; 165} 166 167// Register the DefineClass method. 168JSVM_CallbackStruct param[] = { 169 {.data = nullptr, .callback = DefineClass}, 170}; 171 172static JSVM_CallbackStruct *method = param; 173 174// Alias for the defineClass method, which can be called from JS. 175static JSVM_PropertyDescriptor descriptor[] = { 176 {"defineClass", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 177}; 178 179``` 180 181#### JS Example 182 183const char *srcCallNative = R"JS( 184 defineClass(); 185)JS"; 186 187#### Execution Result 188 189The following information is displayed in the log: 190 191Create Instance 192 193NAPI MyObject::New newTarget != nullptr 194 195NewInstance:{"name":"lilei"} 196 197Create Instance 198 199NAPI MyObject::New newTarget == nullptr 200 201NewInstance:{"name":"lilei"} 202 203### OH_JSVM_Wrap 204 205Call **OH_JSVM_Wrap** to wrap a native instance in a JS object. You can use **OH_JSVM_Unwrap()** to retrieve the native instance later. 206 207### OH_JSVM_Unwrap 208 209Call **OH_JSVM_Unwrap** to unwrap the native instance that is previously encapsulated in a JS object. 210 211### OH_JSVM_RemoveWrap 212 213Call **OH_JSVM_RemoveWrap** to remove the wrapping after the native instance is unwrapped from a JS object. 214 215CPP code: 216 217```cpp 218// hello.cpp 219#include <string> 220 221// Define OH_JSVM_GetNewTarget, OH_JSVM_DefineClass, OH_JSVM_Wrap, OH_JSVM_Unwrap, and OH_JSVM_RemoveWrap. 222 223// Define the struct Object. 224struct Object { 225 std::string name; 226 int32_t age; 227}; 228 229// Define a callback function. 230static void DerefItem(JSVM_Env env, void *data, void *hint) { 231 OH_LOG_INFO(LOG_APP, "JSVM deref_item"); 232 (void)hint; 233} 234 235static JSVM_Value WrapObject(JSVM_Env env, JSVM_CallbackInfo info) { 236 OH_LOG_INFO(LOG_APP, "JSVM wrap"); 237 Object obj; 238 // Set a property for the object. 239 obj.name = "lilei"; 240 obj.age = 18; 241 Object *objPointer = &obj; 242 // Obtain the number of parameters in the callback and the values to be wrapped. 243 size_t argc = 1; 244 JSVM_Value toWrap = nullptr; 245 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, &toWrap, nullptr, nullptr)); 246 // Call OH_JSVM_Wrap to wrap the custom struct object. 247 JSVM_CALL(OH_JSVM_Wrap(env, toWrap, reinterpret_cast<void *>(objPointer), DerefItem, NULL, NULL)); 248 Object *data; 249 // Call OH_JSVM_Unwrap to retrieve the native instance that is previously encapsulated in the JS object. 250 JSVM_CALL(OH_JSVM_Unwrap(env, toWrap, reinterpret_cast<void **>(&data))); 251 OH_LOG_INFO(LOG_APP, "JSVM name: %{public}s", data->name.c_str()); 252 OH_LOG_INFO(LOG_APP, "JSVM age: %{public}d", data->age); 253 return nullptr; 254} 255 256static JSVM_Value RemoveWrap(JSVM_Env env, JSVM_CallbackInfo info) { 257 OH_LOG_INFO(LOG_APP, "JSVM removeWrap"); 258 Object obj; 259 // Set a property for the object. 260 obj.name = "lilei"; 261 obj.age = 18; 262 Object *objPointer = &obj; 263 // Obtain the number of parameters in the callback and the values to be wrapped. 264 size_t argc = 1; 265 JSVM_Value toWrap = nullptr; 266 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, &toWrap, nullptr, nullptr)); 267 // Wrap the Object struct. 268 JSVM_CALL(OH_JSVM_Wrap(env, toWrap, reinterpret_cast<void *>(objPointer), DerefItem, NULL, NULL)); 269 Object *data; 270 // Unwrap the previously encapsulated object and remove the wrapping. 271 JSVM_CALL(OH_JSVM_RemoveWrap(env, toWrap, reinterpret_cast<void **>(&objPointer))); 272 // Check whether the wrapping is removed. 273 JSVM_Status status = OH_JSVM_Unwrap(env, toWrap, reinterpret_cast<void **>(&data)); 274 if (status != JSVM_OK) { 275 OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_RemoveWrap success"); 276 } 277 return nullptr; 278} 279 280// Register the WrapObject and RemoveWrap callbacks. 281static JSVM_CallbackStruct param[] = { 282 {.data = nullptr, .callback = WrapObject}, 283 {.data = nullptr, .callback = RemoveWrap}, 284}; 285static JSVM_CallbackStruct *method = param; 286// Aliases for the WrapObject and RemoveWrap methods, which can be called from JS. 287static JSVM_PropertyDescriptor descriptor[] = { 288 {"wrapObject", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 289 {"removeWrap", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 290}; 291``` 292 293#### JS Example 294 295const char *srcCallNative = R"JS( 296 class Obj {}; 297 wrapObject(new Obj()); 298 removeWrap(new Obj()); 299)JS"; 300 301#### Execution Result 302 303The following information is displayed in the log: 304 305JSVM wrap 306 307JSVM name: lilei 308 309JSVM age: 18 310 311JSVM removeWrap 312 313JSVM OH_JSVM_RemoveWrap success 314 315JSVM deref_item 316 317### OH_JSVM_DefineClassWithOptions 318> **NOTE**<br>The parent class passed in must be created by using an **OH_JSVM_DefineClass** API. Otherwise, the **JSVM_INVALID_ARG** error will be returned. 319**DefineClassOptions** supports the following options: 320- **JSVM_DEFINE_CLASS_NORMAL**: defines a class in normal mode. The default status is **JSVM_DEFINE_CLASS_NORMAL**. 321- **JSVM_DEFINE_CLASS_WITH_COUNT**: reserves **internal-field** slot for the created class. 322- **JSVM_DEFINE_CLASS_WITH_PROPERTY_HANDLER**: sets a listener property for the created class and sets a callback to be invoked when it is called as a function. 323#### CPP Code 324```c++ 325static JSVM_PropertyHandlerConfigurationStruct propertyCfg{ 326 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr 327}; 328 329static bool g_call_as_function_flag = false; 330static bool g_set_named_property_flag = false; 331static bool g_call_as_constructor_flag = false; 332static bool g_properties_flag = false; 333 334static JSVM_Value SetNamedPropertyCbInfo2(JSVM_Env env, JSVM_Value name, JSVM_Value property, JSVM_Value thisArg, 335 JSVM_Value data) 336{ 337 g_set_named_property_flag = true; 338 return property; 339} 340 341static JSVM_Value Add(JSVM_Env env, JSVM_CallbackInfo info) { 342 g_properties_flag = true; 343 size_t argc = 2; 344 JSVM_Value args[2]; 345 OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL); 346 double num1, num2; 347 OH_JSVM_GetValueDouble(env, args[0], &num1); 348 OH_JSVM_GetValueDouble(env, args[1], &num2); 349 JSVM_Value sum = nullptr; 350 OH_JSVM_CreateDouble(env, num1 + num2, &sum); 351 return sum; 352} 353 354std::string ToString(JSVM_Env jsvm_env, JSVM_Value val) 355{ 356 JSVM_Value js_string; 357 OH_JSVM_CoerceToString(jsvm_env, val, &js_string); 358 size_t length = 0; 359 OH_JSVM_GetValueStringUtf8(jsvm_env, js_string, NULL, 0, &length); 360 size_t capacity = length + 1; 361 char *buffer = new char[capacity]; 362 size_t copy_length = 0; 363 OH_JSVM_GetValueStringUtf8(jsvm_env, js_string, buffer, capacity, ©_length); 364 std::string str(buffer); 365 delete[] buffer; 366 return str; 367} 368 369JSVM_Value Run(JSVM_Env env, const char *s) 370{ 371 // 1. Convert const char* to JS_String. 372 JSVM_Value str; 373 JSVM_CALL(OH_JSVM_CreateStringUtf8(env, s, JSVM_AUTO_LENGTH, &str)); 374 // 2. Convert JS_String to JS_Script. 375 JSVM_Script script; 376 OH_JSVM_CompileScript(jsvm_env, str, nullptr, JSVM_AUTO_LENGTH, false, nullptr, &script); 377 // 3. Execute JS_Script. 378 JSVM_Value result; 379 OH_JSVM_RunScript(jsvm_env, script, &result); 380 return result; 381} 382 383static JSVM_Value TestDefineClassWithOptions(JSVM_Env env, JSVM_CallbackInfo info) 384{ 385 g_call_as_function_flag = false; 386 g_set_named_property_flag = false; 387 g_call_as_constructor_flag = false; 388 g_properties_flag = false; 389 // 1. Define parent-class. 390 JSVM_Value parentClass = nullptr; 391 JSVM_CallbackStruct parentClassConstructor; 392 parentClassConstructor.data = nullptr; 393 parentClassConstructor.callback = [](JSVM_Env env, JSVM_CallbackInfo info) -> JSVM_Value { 394 JSVM_Value thisVar = nullptr; 395 OH_JSVM_GetCbInfo(env, info, nullptr, nullptr, &thisVar, nullptr); 396 return thisVar; 397 }; 398 JSVM_Value fooVal = Str(env, "bar"); 399 JSVM_PropertyDescriptor des[2]; 400 des[0] = { 401 .utf8name = "foo", 402 .value = fooVal, 403 }; 404 JSVM_CallbackStruct parentProperties[] = { 405 {.callback = Add, .data = nullptr}, 406 }; 407 des[1] = { 408 .utf8name = "add", 409 .method = &parentProperties[0], 410 }; 411 JSVM_DefineClassOptions options[1]; 412 options[0].id = JSVM_DEFINE_CLASS_WITH_COUNT; 413 options[0].content.num = 3; 414 JSVM_CALL(OH_JSVM_DefineClassWithOptions(env, "parentClass", JSVM_AUTO_LENGTH, &parentClassConstructor, 2, des, 415 nullptr, 1, options, &parentClass)); 416 417 // 2. Define sub-class. 418 JSVM_Value subClass = nullptr; 419 JSVM_CallbackStruct subClassConstructor; 420 subClassConstructor.data = nullptr; 421 subClassConstructor.callback = [](JSVM_Env env, JSVM_CallbackInfo info) -> JSVM_Value { 422 JSVM_Value thisVar = nullptr; 423 g_call_as_constructor_flag = true; 424 OH_JSVM_GetCbInfo(env, info, nullptr, nullptr, &thisVar, nullptr); 425 return thisVar; 426 }; 427 JSVM_DefineClassOptions subOptions[2]; 428 JSVM_CallbackStruct callAsFuncParam; 429 callAsFuncParam.data = nullptr; 430 callAsFuncParam.callback = [](JSVM_Env env, JSVM_CallbackInfo info) -> JSVM_Value { 431 JSVM_Value thisVar = nullptr; 432 g_call_as_function_flag = true; 433 OH_JSVM_GetCbInfo(env, info, nullptr, nullptr, &thisVar, nullptr); 434 return thisVar; 435 }; 436 propertyCfg.genericNamedPropertySetterCallback = SetNamedPropertyCbInfo2; 437 JSVM_PropertyHandler propertyHandler = { 438 .propertyHandlerCfg = &propertyCfg, 439 .callAsFunctionCallback = &callAsFuncParam, 440 }; 441 subOptions[0].id = JSVM_DEFINE_CLASS_WITH_COUNT; 442 subOptions[0].content.num = 4; 443 subOptions[1].id = JSVM_DEFINE_CLASS_WITH_PROPERTY_HANDLER; 444 subOptions[1].content.ptr = &propertyHandler; 445 JSVM_CALL(OH_JSVM_DefineClassWithOptions(env, "subClass", JSVM_AUTO_LENGTH, &subClassConstructor, 0, nullptr, 446 parentClass, 2, subOptions, &subClass)); 447 // 3. Verify the validity of 'constructor'. 448 JSVM_Value subInstance; 449 JSVM_CALL(OH_JSVM_NewInstance(env, subClass, 0, nullptr, &subInstance)); 450 451 JSVM_Value globalVal; 452 OH_JSVM_GetGlobal(env, &globalVal); 453 OH_JSVM_SetNamedProperty(env, globalVal, "obj", subInstance); 454 455 // 4. Verify the validity of 'parentClass'. 456 JSVM_Value subRes = nullptr; 457 JSVM_CALL(OH_JSVM_GetNamedProperty(env, subInstance, "foo", &subRes)); 458 if (ToString(env, subRes).compare("bar") != 0) { 459 OH_LOG_ERROR(LOG_APP, "Run OH_JSVM_DefineClassWithOptions: Failed"); 460 } 461 // 5. Verify the validity of 'properties'. 462 Run(env, "obj.add(3, 4);"); 463 // 6. Verify the validity of 'options'. 464 Run(env, "obj()"); 465 Run(env, "obj.x = 123;"); 466 if (g_call_as_function_flag == true && 467 g_set_named_property_flag == true && 468 g_call_as_constructor_flag == true && 469 g_properties_flag == true) { 470 OH_LOG_INFO(LOG_APP, "Run OH_JSVM_DefineClassWithOptions: Success"); 471 } else { 472 OH_LOG_ERROR(LOG_APP, "Run OH_JSVM_DefineClassWithOptions: Failed"); 473 } 474 JSVM_Value checked; 475 OH_JSVM_GetBoolean(env, true, &checked); 476 return checked; 477} 478 479static JSVM_CallbackStruct param[] = { 480 {.data = nullptr, .callback = TestDefineClassWithOptions}, 481}; 482static JSVM_CallbackStruct *method = param; 483 484static JSVM_PropertyDescriptor descriptor[] = { 485 {"testDefineClassWithOptions", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 486}; 487 488``` 489#### JS Example 490 491const char *srcCallNative = R"JS(testDefineClassWithOptions();)JS"; 492#### Execution Result 493 494The following information is displayed in the log: 495Run OH_JSVM_DefineClassWithOptions: Success 496