|
259 | 259 | }
|
260 | 260 | }
|
261 | 261 |
|
| 262 | +// Static method to extend native classes with JavaScript-defined methods |
| 263 | +napi_value ClassBuilder::ExtendCallback(napi_env env, napi_callback_info info) { |
| 264 | + size_t argc = 2; |
| 265 | + napi_value args[2]; |
| 266 | + napi_value thisArg; |
| 267 | + |
| 268 | + napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr); |
| 269 | + |
| 270 | + if (argc < 1) { |
| 271 | + napi_throw_error(env, nullptr, "extend() requires at least one parameter with method definitions"); |
| 272 | + return nullptr; |
| 273 | + } |
| 274 | + |
| 275 | + // Validate that the first argument is an object |
| 276 | + napi_valuetype argType; |
| 277 | + napi_typeof(env, args[0], &argType); |
| 278 | + if (argType != napi_object) { |
| 279 | + napi_throw_error(env, nullptr, "extend() first parameter must be an object"); |
| 280 | + return nullptr; |
| 281 | + } |
| 282 | + |
| 283 | + // Get the native class from 'this' (the constructor function) |
| 284 | + Class baseNativeClass = nullptr; |
| 285 | + napi_unwrap(env, thisArg, (void**)&baseNativeClass); |
| 286 | + |
| 287 | + if (baseNativeClass == nullptr) { |
| 288 | + napi_throw_error(env, nullptr, "extend() can only be called on native class constructors"); |
| 289 | + return nullptr; |
| 290 | + } |
| 291 | + |
| 292 | + // Create a unique class name |
| 293 | + napi_value baseClassName; |
| 294 | + napi_get_named_property(env, thisArg, "name", &baseClassName); |
| 295 | + static char baseClassNameBuf[512]; |
| 296 | + napi_get_value_string_utf8(env, baseClassName, baseClassNameBuf, 512, nullptr); |
| 297 | + |
| 298 | + std::string newClassName = baseClassNameBuf; |
| 299 | + newClassName += "_Extended_"; |
| 300 | + newClassName += std::to_string(rand()); |
| 301 | + |
| 302 | + // Create the new constructor function that extends the base |
| 303 | + napi_value newConstructor; |
| 304 | + napi_define_class(env, newClassName.c_str(), newClassName.length(), |
| 305 | + [](napi_env env, napi_callback_info info) -> napi_value { |
| 306 | + // Constructor implementation - delegate to base class |
| 307 | + napi_value thisArg; |
| 308 | + napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr); |
| 309 | + return thisArg; |
| 310 | + }, nullptr, 0, nullptr, &newConstructor); |
| 311 | + |
| 312 | + // Set up JavaScript inheritance from the base class |
| 313 | + napi_inherits(env, newConstructor, thisArg); |
| 314 | + |
| 315 | + // Get prototype for adding methods |
| 316 | + napi_value newPrototype; |
| 317 | + napi_get_named_property(env, newConstructor, "prototype", &newPrototype); |
| 318 | + |
| 319 | + // Add methods from the first parameter to the prototype |
| 320 | + napi_value methodNames; |
| 321 | + napi_get_all_property_names(env, args[0], napi_key_own_only, napi_key_skip_symbols, |
| 322 | + napi_key_numbers_to_strings, &methodNames); |
| 323 | + |
| 324 | + uint32_t methodCount = 0; |
| 325 | + napi_get_array_length(env, methodNames, &methodCount); |
| 326 | + |
| 327 | + for (uint32_t i = 0; i < methodCount; i++) { |
| 328 | + napi_value methodName, methodFunc; |
| 329 | + napi_get_element(env, methodNames, i, &methodName); |
| 330 | + |
| 331 | + static char methodNameBuf[512]; |
| 332 | + napi_get_value_string_utf8(env, methodName, methodNameBuf, 512, nullptr); |
| 333 | + std::string name = methodNameBuf; |
| 334 | + |
| 335 | + napi_get_named_property(env, args[0], name.c_str(), &methodFunc); |
| 336 | + |
| 337 | + // Add the method to the prototype |
| 338 | + napi_set_named_property(env, newPrototype, name.c_str(), methodFunc); |
| 339 | + } |
| 340 | + |
| 341 | + // Handle optional second parameter for protocols |
| 342 | + if (argc >= 2) { |
| 343 | + napi_valuetype secondArgType; |
| 344 | + napi_typeof(env, args[1], &secondArgType); |
| 345 | + if (secondArgType == napi_object) { |
| 346 | + napi_value protocols; |
| 347 | + bool hasProtocols = false; |
| 348 | + napi_has_named_property(env, args[1], "protocols", &hasProtocols); |
| 349 | + |
| 350 | + if (hasProtocols) { |
| 351 | + napi_get_named_property(env, args[1], "protocols", &protocols); |
| 352 | + napi_set_named_property(env, newConstructor, "ObjCProtocols", protocols); |
| 353 | + } |
| 354 | + } |
| 355 | + } |
| 356 | + |
| 357 | + // Use ClassBuilder to create the native class and bridge the methods |
| 358 | + ClassBuilder* builder = new ClassBuilder(env, newConstructor); |
| 359 | + builder->build(); |
| 360 | + |
| 361 | + // Register the builder in the bridge state |
| 362 | + auto bridgeState = ObjCBridgeState::InstanceData(env); |
| 363 | + bridgeState->classesByPointer[builder->nativeClass] = builder; |
| 364 | + |
| 365 | + return newConstructor; |
| 366 | +} |
| 367 | + |
262 | 368 | } // namespace nativescript
|
0 commit comments