• 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 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, &param, 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, &copy_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