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