1# 使用JSVM进行class相关开发 2 3## 简介 4 5使用JSVM-API接口进行class相关开发,处理JavaScript中的类,例如定义类、构造实例等。 6 7## 基本概念 8 9在使用JSVM-API接口进行class相关开发时,需要理解以下基本概念: 10 11- **类**:类是用于创建对象的模板。它提供了一种封装数据和行为的方式,以便于对数据进行处理和操作。类在JavaScript中是建立在原型(prototype)的基础上的,并且还引入了一些类独有的语法和语义。 12- **实例**:实例是通过类创建具体的对象。类定义了对象的结构和行为,而实例则是类的具体表现。通过实例化类,我们可以访问类中定义的属性和方法,并且每个实例都具有自己的属性值。 13 14## 接口说明 15 16| 接口 | 功能说明 | 17| ------------------- | ---------------------------------- | 18| OH_JSVM_NewInstance | 通过给定的构造函数,构建一个实例。| 19| OH_JSVM_GetNewTarget | 获取函数的元属性new.target。| 20| OH_JSVM_DefineClass | 用于在JavaScript中定义一个类,并与对应的C类进行封装和交互。它提供了创建类的构造函数、定义属性和方法的能力,以及在C和JavaScript之间进行数据交互的支持。| 21| OH_JSVM_Wrap | 在JavaScript对象中封装原生实例。稍后可以使用OH_JSVM_Unwrap()解包原生实例。| 22| OH_JSVM_Unwrap | 解包先前封装在JavaScript对象中的原生实例。| 23| OH_JSVM_RemoveWrap | 解包先前封装在JavaScript对象中的原生实例并释放封装。| 24|OH_JSVM_DefineClassWithOptions | 定义一个具有给定类名、构造函数、属性和回调处理程序、父类的JavaScript类,并根据传入了DefineClassOptions来决定是否需要为所定义的Class设置属性代理、预留internal-field槽位、为class作为函数进行调用时设置函数回调。| 25 26## 使用示例 27 28JSVM-API接口开发流程参考[使用JSVM-API实现JS与C/C++语言交互开发流程](use-jsvm-process.md),本文仅对接口对应C++相关代码进行展示。 29 30### OH_JSVM_NewInstance 31 32通过给定的构造函数,构建一个实例。 33 34cpp部分代码 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// OH_JSVM_NewInstance的样例方法 55static JSVM_Value NewInstance(JSVM_Env env, JSVM_CallbackInfo info) { 56 // 获取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 // 调用OH_JSVM_NewInstance接口,实例化一个对象,将这个对象返回 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// 通过给定的构造函数,构建一个实例。 69// NewInstance注册回调 70static JSVM_CallbackStruct param[] = { 71 {.data = nullptr, .callback = NewInstance}, 72}; 73 74static JSVM_CallbackStruct *method = param; 75 76// NewInstance方法别名,供JS调用 77static JSVM_PropertyDescriptor descriptor[] = { 78 {"newInstance", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 79}; 80``` 81 82#### 样例JS 83 84const char *srcCallNative = R"JS( 85 function Fruit(name) { 86 this.name = name; 87 } 88 newInstance(Fruit, "apple"); 89)JS"; 90 91#### 执行结果 92 93在LOG中输出下面的结果: 94NewInstance:{"name":"apple"} 95 96### OH_JSVM_GetNewTarget 97 98用于获取函数的元属性new.target值。在JavaScript中,new.target是一个特殊的元属性,用于检测函数或构造函数是否是通过 'new' 运算符被调用的。 99 100### OH_JSVM_DefineClass 101 102用于在JavaScript中定义一个类,并与对应的C类进行封装和交互。它提供了创建类的构造函数、定义属性和方法的能力,以及在C和JavaScript之间进行数据交互的支持。 103 104cpp部分代码 105 106```cpp 107// hello.cpp 108#include <string> 109 110JSVM_Value CreateInstance(JSVM_Env env, JSVM_CallbackInfo info) { 111 JSVM_Value newTarget; 112 // 获取构造函数的new.target值 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// 封装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 // 用于在JavaScript中定义一个类 147 JSVM_CALL(OH_JSVM_DefineClass(env, "MyObject", JSVM_AUTO_LENGTH, ¶m, 0, nullptr, &cons)); 148 JSVM_Value instanceValue = nullptr; 149 // 作为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 // 作为普通的函数调用 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// 注册DefineClass的方法 168JSVM_CallbackStruct param[] = { 169 {.data = nullptr, .callback = DefineClass}, 170}; 171 172static JSVM_CallbackStruct *method = param; 173 174// DefineClass方法别名,供JS调用 175static JSVM_PropertyDescriptor descriptor[] = { 176 {"defineClass", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 177}; 178 179``` 180 181#### 样例JS 182 183const char *srcCallNative = R"JS( 184 defineClass(); 185)JS"; 186 187#### 执行结果 188 189在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 205在JavaScript对象中封装原生实例。稍后可以使用OH_JSVM_Unwrap()解包原生实例 206 207### OH_JSVM_Unwrap 208 209解包先前封装在JavaScript对象中的原生实例 210 211### OH_JSVM_RemoveWrap 212 213解包先前封装在JavaScript对象中的原生实例并释放封装 214 215cpp部分代码 216 217```cpp 218// hello.cpp 219#include <string> 220 221// OH_JSVM_GetNewTarget、OH_JSVM_DefineClass、OH_JSVM_Wrap、OH_JSVM_Unwrap、OH_JSVM_RemoveWrap的样例方法 222 223// 自定义类结构体Object 224struct Object { 225 std::string name; 226 int32_t age; 227}; 228 229// 定义一个回调函数 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 // 设置Object属性 239 obj.name = "lilei"; 240 obj.age = 18; 241 Object *objPointer = &obj; 242 // 获取回调信息中的参数数量和将要被封装的值 243 size_t argc = 1; 244 JSVM_Value toWrap = nullptr; 245 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, &toWrap, nullptr, nullptr)); 246 // OH_JSVM_Wrap将自定义结构Object进行封装 247 JSVM_CALL(OH_JSVM_Wrap(env, toWrap, reinterpret_cast<void *>(objPointer), DerefItem, NULL, NULL)); 248 Object *data; 249 // OH_JSVM_UnWrap解包先前封装在JavaScript对象中的原生实例 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 // 设置Object属性 260 obj.name = "lilei"; 261 obj.age = 18; 262 Object *objPointer = &obj; 263 // 获取回调信息中的参数数量和将要被封装的值 264 size_t argc = 1; 265 JSVM_Value toWrap = nullptr; 266 JSVM_CALL(OH_JSVM_GetCbInfo(env, info, &argc, &toWrap, nullptr, nullptr)); 267 // 将自定义结构Object封装 268 JSVM_CALL(OH_JSVM_Wrap(env, toWrap, reinterpret_cast<void *>(objPointer), DerefItem, NULL, NULL)); 269 Object *data; 270 // 解包先前封装的object,并移除封装 271 JSVM_CALL(OH_JSVM_RemoveWrap(env, toWrap, reinterpret_cast<void **>(&objPointer))); 272 // 检查是否已被移除 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// WrapObject、RemoveWrap注册回调 281static JSVM_CallbackStruct param[] = { 282 {.data = nullptr, .callback = WrapObject}, 283 {.data = nullptr, .callback = RemoveWrap}, 284}; 285static JSVM_CallbackStruct *method = param; 286// WrapObject、RemoveWrap方法别名,供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 294 295const char *srcCallNative = R"JS( 296 class Obj {}; 297 wrapObject(new Obj()); 298 removeWrap(new Obj()); 299)JS"; 300 301#### 执行结果 302 303在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:** 传入的父类class必须是通过OH_JSVM_DefineClass系列接口创建出来的,不然会被视为无效参数,返回JSVM_INVALID_ARG错误码。 319目前支持以下的DefineClassOptions: 320- JSVM_DEFINE_CLASS_NORMAL: 按正常模式创建Class。默认缺省状态为JSVM_DEFINE_CLASS_NORMAL状态。 321- JSVM_DEFINE_CLASS_WITH_COUNT: 为所创建的Class预留interfield槽位。 322- JSVM_DEFINE_CLASS_WITH_PROPERTY_HANDLER: 为所创建的Class设置监听拦截属性以及设置作为函数调用时回调函数。 323#### cpp代码 324```c++ 325#include <string> 326static JSVM_PropertyHandlerConfigurationStruct propertyCfg{ 327 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr 328}; 329 330static bool g_call_as_function_flag = false; 331static bool g_set_named_property_flag = false; 332static bool g_call_as_constructor_flag = false; 333static bool g_properties_flag = false; 334 335static JSVM_Value SetNamedPropertyCbInfo2(JSVM_Env env, JSVM_Value name, JSVM_Value property, JSVM_Value thisArg, 336 JSVM_Value data) 337{ 338 g_set_named_property_flag = true; 339 return property; 340} 341 342static JSVM_Value Add(JSVM_Env env, JSVM_CallbackInfo info) { 343 g_properties_flag = true; 344 size_t argc = 2; 345 JSVM_Value args[2]; 346 OH_JSVM_GetCbInfo(env, info, &argc, args, NULL, NULL); 347 double num1, num2; 348 OH_JSVM_GetValueDouble(env, args[0], &num1); 349 OH_JSVM_GetValueDouble(env, args[1], &num2); 350 JSVM_Value sum = nullptr; 351 OH_JSVM_CreateDouble(env, num1 + num2, &sum); 352 return sum; 353} 354 355std::string ToString(JSVM_Env jsvm_env, JSVM_Value val) 356{ 357 JSVM_Value js_string; 358 OH_JSVM_CoerceToString(jsvm_env, val, &js_string); 359 size_t length = 0; 360 OH_JSVM_GetValueStringUtf8(jsvm_env, js_string, NULL, 0, &length); 361 size_t capacity = length + 1; 362 char *buffer = new char[capacity]; 363 size_t copy_length = 0; 364 OH_JSVM_GetValueStringUtf8(jsvm_env, js_string, buffer, capacity, ©_length); 365 std::string str(buffer); 366 delete[] buffer; 367 return str; 368} 369 370JSVM_Value Run(JSVM_Env env, const char *s) 371{ 372 // 1. 将const char*转换成JS_String。 373 JSVM_Value str; 374 JSVM_CALL(OH_JSVM_CreateStringUtf8(env, s, JSVM_AUTO_LENGTH, &str)); 375 // 2. 将JS_String转换成JS_Script。 376 JSVM_Script script; 377 OH_JSVM_CompileScript(env, str, nullptr, JSVM_AUTO_LENGTH, false, nullptr, &script); 378 // 3. 执行JS_Script。 379 JSVM_Value result; 380 OH_JSVM_RunScript(env, script, &result); 381 return result; 382} 383 384static JSVM_Value TestDefineClassWithOptions(JSVM_Env env, JSVM_CallbackInfo info) 385{ 386 g_call_as_function_flag = false; 387 g_set_named_property_flag = false; 388 g_call_as_constructor_flag = false; 389 g_properties_flag = false; 390 // 1. Define parent-class. 391 JSVM_Value parentClass = nullptr; 392 JSVM_CallbackStruct parentClassConstructor; 393 parentClassConstructor.data = nullptr; 394 parentClassConstructor.callback = [](JSVM_Env env, JSVM_CallbackInfo info) -> JSVM_Value { 395 JSVM_Value thisVar = nullptr; 396 OH_JSVM_GetCbInfo(env, info, nullptr, nullptr, &thisVar, nullptr); 397 return thisVar; 398 }; 399 JSVM_Value fooVal; 400 OH_JSVM_CreateStringUtf8(env, "bar", JSVM_AUTO_LENGTH, &fooVal); 401 JSVM_PropertyDescriptor des[2]; 402 des[0] = { 403 .utf8name = "foo", 404 .value = fooVal, 405 }; 406 JSVM_CallbackStruct parentProperties[] = { 407 {.callback = Add, .data = nullptr}, 408 }; 409 des[1] = { 410 .utf8name = "add", 411 .method = &parentProperties[0], 412 }; 413 JSVM_DefineClassOptions options[1]; 414 options[0].id = JSVM_DEFINE_CLASS_WITH_COUNT; 415 options[0].content.num = 3; 416 JSVM_CALL(OH_JSVM_DefineClassWithOptions(env, "parentClass", JSVM_AUTO_LENGTH, &parentClassConstructor, 2, des, 417 nullptr, 1, options, &parentClass)); 418 419 // 2. Define sub-class. 420 JSVM_Value subClass = nullptr; 421 JSVM_CallbackStruct subClassConstructor; 422 subClassConstructor.data = nullptr; 423 subClassConstructor.callback = [](JSVM_Env env, JSVM_CallbackInfo info) -> JSVM_Value { 424 JSVM_Value thisVar = nullptr; 425 g_call_as_constructor_flag = true; 426 OH_JSVM_GetCbInfo(env, info, nullptr, nullptr, &thisVar, nullptr); 427 return thisVar; 428 }; 429 JSVM_DefineClassOptions subOptions[2]; 430 JSVM_CallbackStruct callAsFuncParam; 431 callAsFuncParam.data = nullptr; 432 callAsFuncParam.callback = [](JSVM_Env env, JSVM_CallbackInfo info) -> JSVM_Value { 433 JSVM_Value thisVar = nullptr; 434 g_call_as_function_flag = true; 435 OH_JSVM_GetCbInfo(env, info, nullptr, nullptr, &thisVar, nullptr); 436 return thisVar; 437 }; 438 propertyCfg.genericNamedPropertySetterCallback = SetNamedPropertyCbInfo2; 439 JSVM_PropertyHandler propertyHandler = { 440 .propertyHandlerCfg = &propertyCfg, 441 .callAsFunctionCallback = &callAsFuncParam, 442 }; 443 subOptions[0].id = JSVM_DEFINE_CLASS_WITH_COUNT; 444 subOptions[0].content.num = 4; 445 subOptions[1].id = JSVM_DEFINE_CLASS_WITH_PROPERTY_HANDLER; 446 subOptions[1].content.ptr = &propertyHandler; 447 JSVM_CALL(OH_JSVM_DefineClassWithOptions(env, "subClass", JSVM_AUTO_LENGTH, &subClassConstructor, 0, nullptr, 448 parentClass, 2, subOptions, &subClass)); 449 // 3. Verify the validity of 'constructor'. 450 JSVM_Value subInstance; 451 JSVM_CALL(OH_JSVM_NewInstance(env, subClass, 0, nullptr, &subInstance)); 452 453 JSVM_Value globalVal; 454 OH_JSVM_GetGlobal(env, &globalVal); 455 OH_JSVM_SetNamedProperty(env, globalVal, "obj", subInstance); 456 457 // 4. Verify the validity of 'parentClass'. 458 JSVM_Value subRes = nullptr; 459 JSVM_CALL(OH_JSVM_GetNamedProperty(env, subInstance, "foo", &subRes)); 460 if (ToString(env, subRes).compare("bar") != 0) { 461 OH_LOG_ERROR(LOG_APP, "Run OH_JSVM_DefineClassWithOptions: Failed"); 462 } 463 // 5. Verify the validity of 'properties'. 464 Run(env, "obj.add(3, 4);"); 465 // 6. Verify the validity of 'options'. 466 Run(env, "obj()"); 467 Run(env, "obj.x = 123;"); 468 if (g_call_as_function_flag == true && 469 g_set_named_property_flag == true && 470 g_call_as_constructor_flag == true && 471 g_properties_flag == true) { 472 OH_LOG_INFO(LOG_APP, "Run OH_JSVM_DefineClassWithOptions: Success"); 473 } else { 474 OH_LOG_ERROR(LOG_APP, "Run OH_JSVM_DefineClassWithOptions: Failed"); 475 } 476 JSVM_Value checked; 477 OH_JSVM_GetBoolean(env, true, &checked); 478 return checked; 479} 480 481static JSVM_CallbackStruct param[] = { 482 {.data = nullptr, .callback = TestDefineClassWithOptions}, 483}; 484static JSVM_CallbackStruct *method = param; 485 486static JSVM_PropertyDescriptor descriptor[] = { 487 {"testDefineClassWithOptions", nullptr, method++, nullptr, nullptr, nullptr, JSVM_DEFAULT}, 488}; 489 490``` 491#### 样例JS 492 493const char *srcCallNative = R"JS(testDefineClassWithOptions();)JS"; 494#### 执行结果 495 496在LOG中输出下面的结果: 497Run OH_JSVM_DefineClassWithOptions: Success 498