• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &param, 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, &copy_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