1# Working with Classes 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 **new.target** of the constructor call.|
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 a native instance from a JS object.|
23| OH_JSVM_RemoveWrap     | Removes the wrapping after the native instance is unwrapped from a JS object.|
24
25## Example
26
27If you are just starting out with JSVM-API, see [JSVM-API Development Process](use-jsvm-process.md). The following only demonstrates the C++ and ArkTS code involved in the class-related APIs.
28
29### OH_JSVM_NewInstance
30
31Use **OH_JSVM_NewInstance** to create an instance from the given constructor.
32
33CPP code:
34
35```cpp
36// hello.cpp
37#include "napi/native_api.h"
38#include "ark_runtime/jsvm.h"
39#include <hilog/log.h>
40Create an instance from the given constructor.
41// Register the NewInstance callback.
42static JSVM_CallbackStruct param[] = {
43    {.data = nullptr, .callback = NewInstance},
44};
45static JSVM_CallbackStruct *method = param;
46// Set a property descriptor named newInstance and associate it with a callback. This allows the NewInstance callback to be called from JS.
47static JSVM_PropertyDescriptor descriptor[] = {
48    {"newInstance", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
49};
50// Define OH_JSVM_NewInstance.
51static JSVM_Value NewInstance(JSVM_Env env, JSVM_CallbackInfo info) {
52    // Obtain the two parameters passed from JS.
53    size_t argc = 2;
54    JSVM_Value args[2] = {nullptr};
55    OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr);
56    // Call OH_JSVM_NewInstance to create an instance and return the instance created.
57    JSVM_Value result = nullptr;
58    JSVM_Status status = OH_JSVM_NewInstance(env, args[0], 1, &args[1], &result);
59    if (status != JSVM_OK) {
60        OH_LOG_ERROR(LOG_APP, "JSVM API TEST RESULT: PASS");
61    } else {
62        OH_LOG_INFO(LOG_APP, "JSVM API TEST RESULT: PASS");
63    }
64    return result;
65}
66```
67
68ArkTS code:
69
70```ts
71import hilog from '@ohos.hilog'
72// Import the native APIs.
73import napitest from 'libentry.so'
74let script: string = `
75   function Fruit(name) {
76       this.name = name;
77   }
78   newInstance(Fruit, "apple");
79  `;
80try {
81  let result = napitest.runJsVm(script.toString());
82  hilog.info(0x0000, 'JSVM', 'NewInstance:%{public}s', result);
83} catch (error) {
84  hilog.error(0x0000, 'JSVM', 'NewInstance:%{public}s', error.message);
85}
86```
87
88### OH_JSVM_GetNewTarget
89
90Use **OH_JSVM_GetNewTarget** to obtain **new.target** of a constructor. In JS, **new.target** is a meta property used to determine whether a constructor was called using the **new** operator.
91
92### OH_JSVM_DefineClass
93
94Use **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.
95
96CPP code:
97
98```cpp
99// hello.cpp
100#include "napi/native_api.h"
101#include "ark_runtime/jsvm.h"
102#include <hilog/log.h>
103// Define a class struct.
104struct DefineObject {
105    std::string name;
106    int32_t age;
107    JSVM_Ref wrapper_;
108};
109static thread_local JSVM_Ref g_ref = nullptr;
110// Create an instance.
111struct DefineObject *defineObject = new struct DefineObject();
112JSVM_Value New(JSVM_Env env, JSVM_CallbackInfo info) {
113    JSVM_Value newTarget;
114    OH_JSVM_GetNewTarget(env, info, &newTarget);
115    if (newTarget != nullptr) {
116        OH_LOG_INFO(LOG_APP, "NAPI MyObject::New newTarget != nullptr");
117        // Invoked as the constructor `new MyObject(...)`.
118        size_t argc = 1;
119        JSVM_Value args[1];
120        JSVM_Value jsThis;
121        OH_JSVM_GetCbInfo(env, info, &argc, args, &jsThis, nullptr);
122        double value = 0.0;
123        JSVM_ValueType valuetype;
124        OH_JSVM_Typeof(env, args[0], &valuetype);
125        if (valuetype != JSVM_UNDEFINED) {
126            OH_JSVM_GetValueDouble(env, args[0], &value);
127        }
128        defineObject->name = "lilei";
129        defineObject->age = 18;
130        return nullptr;
131    } else {
132        OH_LOG_INFO(LOG_APP, "NAPI MyObject::New newTarget == nullptr");
133        size_t argc = 1;
134        JSVM_Value args[1];
135        (OH_JSVM_GetCbInfo(env, info, &argc, args, nullptr, nullptr));
136        JSVM_Value cons;
137        (OH_JSVM_GetReferenceValue(env, g_ref, &cons));
138        JSVM_Value instance;
139        (OH_JSVM_NewInstance(env, cons, argc, args, &instance));
140        return instance;
141    }
142}
143
144// Obtain the data in the encapsulated C++ struct.
145napi_value GetObj(napi_env env) {
146    std::string str = "{\"name\": \"" + defineObject->name + "\",\"age\": " + std::to_string(defineObject->age) + "}";
147    napi_value jsResult;
148    napi_create_string_utf8(env, str.c_str(), NAPI_AUTO_LENGTH, &jsResult);
149    return jsResult;
150}
151
152// Encapsulate the struct in C++.
153JSVM_Value DefineClass(JSVM_Env env, JSVM_Value exports) {
154    JSVM_CallbackStruct param1;
155    param1.data = nullptr;
156    param1.callback = [](JSVM_Env env, JSVM_CallbackInfo info) -> JSVM_Value { return New(env, info); };
157    JSVM_Value cons;
158    OH_JSVM_DefineClass(env, "MyObject", JSVM_AUTO_LENGTH, &param1, 0, nullptr, &cons);
159    JSVM_Value instanceValue = nullptr;
160    OH_JSVM_NewInstance(env, cons, 0, nullptr, &instanceValue);
161    return nullptr;
162}
163```
164
165Modify the **RunJsVm** method defined in [JSVM-API Development Process](use-jsvm-process.md) as follows:
166
167```cpp
168    // hello.cpp
169    // Change the reserved code in defineClass() as follows:
170    if (strcmp(sourceCodeStr.c_str(), "defineClass") == 0) {
171        JSVM_Value obj;
172        DefineClass(env, obj);
173        nResult = GetObj(nEnv);
174    }
175```
176
177ArkTS code:
178
179```ts
180import hilog from '@ohos.hilog'
181// Import the native APIs.
182import napitest from 'libentry.so'
183// test defineclass
184try {
185  let result = napitest.runJsVm("defineClass");
186  hilog.info(0x0000, 'testJSVM', 'Test JSVM defineclass:%{public}s', JSON.stringify(result));
187} catch (error) {
188  hilog.error(0x0000, 'testJSVM', 'Test JSVM AssertEqual error: %{public}s', error);
189}
190```
191
192### OH_JSVM_Wrap
193
194Use **OH_JSVM_Wrap** to wrap a native instance in a JS object. You can use **OH_JSVM_Unwrap()** to retrieve the native instance later.
195
196### OH_JSVM_Unwrap
197
198Use **OH_JSVM_Unwrap** to unwrap a native instance from a JS object.
199
200### OH_JSVM_RemoveWrap
201
202Use **OH_JSVM_RemoveWrap** to retrieve a native instance previously wrapped in a JS object and remove the wrapping.
203
204CPP code:
205
206```cpp
207// hello.cpp
208#include "napi/native_api.h"
209#include "ark_runtime/jsvm.h"
210#include <hilog/log.h>
211// Register the WrapObject and RemoveWrap callbacks.
212static JSVM_CallbackStruct param[] = {
213    {.data = nullptr, .callback = WrapObject},
214    {.data = nullptr, .callback = RemoveWrap},
215};
216static JSVM_CallbackStruct *method = param;
217// Set property descriptors named wrapObject and removeWrap and associate them with a callback each. This allows the WrapObject and RemoveWrap callbacks to be called from JS.
218static JSVM_PropertyDescriptor descriptor[] = {
219    {"wrapObject", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT},
220    {"removeWrap", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}
221};
222// Define OH_JSVM_GetNewTarget, OH_JSVM_DefineClass, OH_JSVM_Wrap, OH_JSVM_Unwrap, and OH_JSVM_RemoveWrap.
223// Check whether the deref_item function is called.
224static bool deref_item_called = false;
225
226// Define the struct Object.
227struct Object {
228    std::string name;
229    int32_t age;
230};
231struct Object *obj = new struct Object();
232
233// Define a callback function.
234static void DerekItem(JSVM_Env env, void *data, void *hint) {
235    OH_LOG_INFO(LOG_APP, "JSVM deref_item");
236    (void)hint;
237}
238
239static JSVM_Value WrapObject(JSVM_Env env, JSVM_CallbackInfo info) {
240    OH_LOG_INFO(LOG_APP, "JSVM wrap");
241    // Set a property for the object.
242    obj->name = "lilei";
243    obj->age = 18;
244    // Obtain the number of parameters in the callback and the values to be wrapped.
245    size_t argc = 1;
246    JSVM_Value toWrap = nullptr;
247    OH_JSVM_GetCbInfo(env, info, &argc, &toWrap, nullptr, nullptr);
248    // Wrap the Object struct.
249    OH_JSVM_Wrap(env, toWrap, reinterpret_cast<void *>(obj), DerekItem, NULL, NULL);
250    struct Object *data;
251    struct Object *data1;
252    OH_JSVM_Unwrap(env, toWrap, reinterpret_cast<void **>(&data));
253    OH_LOG_INFO(LOG_APP, "JSVM name: %{public}s", data->name.c_str());
254    OH_LOG_INFO(LOG_APP, "JSVM age: %{public}d", data->age);
255    // Retrieve the previously wrapped object and remove the wrapping.
256    OH_JSVM_RemoveWrap(env, toWrap, reinterpret_cast<void **>(&obj));
257    JSVM_Status status = OH_JSVM_Unwrap(env, toWrap, reinterpret_cast<void **>(&data1));
258    if (status != JSVM_OK) {
259        OH_LOG_INFO(LOG_APP, "JSVM OH_JSVM_RemoveWrap success");
260    }
261    JSVM_Value checked;
262    OH_JSVM_GetBoolean(env, true, &checked);
263    return checked;
264}
265
266static JSVM_Value RemoveWrap(JSVM_Env env, JSVM_CallbackInfo info) {
267    OH_LOG_INFO(LOG_APP, "JSVM removeWrap");
268    size_t argc = 1;
269    JSVM_Value wrapped = nullptr;
270    void *data;
271    OH_JSVM_GetCbInfo(env, info, &argc, &wrapped, nullptr, nullptr);
272    OH_JSVM_RemoveWrap(env, wrapped, &data);
273    return nullptr;
274}
275```
276
277ArkTS code:
278
279```ts
280import hilog from '@ohos.hilog'
281// Import the native APIs.
282import napitest from 'libentry.so'
283// wrapObject
284class Obj {}
285let obj: Obj = `{}`;
286let script: string = `
287  wrapObject(${obj});
288  `;
289try {
290  let result = napitest.runJsVm(script);
291  hilog.info(0x0000, 'JSVM', 'WrapObject:%{public}s', result);
292} catch (error) {
293  hilog.error(0x0000, 'JSVM', 'WrapObject:%{public}s', error.message);
294}
295
296// removeWrap
297class Obj {}
298let obj: Obj = `{}`;
299let script: string = `
300  removeWrap(${obj});
301  `;
302try {
303  let result = napitest.runJsVm(script);
304  hilog.info(0x0000, 'JSVM', 'RemoveWrap:%{public}s', result);
305} catch (error) {
306  hilog.error(0x0000, 'JSVM', 'RemoveWrap:%{public}s', error.message);
307}
308```
309