Skip to content

Commit 4ec3407

Browse files
committed
feat: Enhance ObjC class member handling and add URLPattern support
- Introduced ObjCClassMemberOverload structure to manage method overloads. - Updated ObjCClassMember to support multiple overloads and refined member definition logic. - Added jsReadOnlySetter to handle readonly property assignments gracefully. - Enhanced jsCall to resolve method overloads based on argument types. - Implemented URLPattern class in the runtime for URL pattern matching. - Updated metadata generation to include protocol names in category declarations. - Improved duplicate method handling in class member processing.
1 parent ebc6a1c commit 4ec3407

17 files changed

Lines changed: 1561 additions & 188 deletions

File tree

NativeScript/ffi/Block.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,22 @@ class FunctionPointer {
1414
void* function;
1515
metagen::MDSectionOffset offset;
1616
Cif* cif;
17+
bool ownsCif = false;
1718

1819
static napi_value wrap(napi_env env, void* function,
1920
metagen::MDSectionOffset offset, bool isBlock);
21+
static napi_value wrapWithEncoding(napi_env env, void* function,
22+
const char* encoding, bool isBlock);
2023
static void finalize(napi_env env, void* finalize_data, void* finalize_hint);
2124

2225
static napi_value jsCallAsCFunction(napi_env env, napi_callback_info cbinfo);
2326
static napi_value jsCallAsBlock(napi_env env, napi_callback_info cbinfo);
2427
};
2528

2629
id registerBlock(napi_env env, Closure* closure, napi_value callback);
30+
napi_value getCachedBlockCallback(napi_env env, void* blockPtr);
31+
bool isObjCBlockObject(id obj);
32+
const char* getObjCBlockSignature(void* blockPtr);
2733

2834
NAPI_FUNCTION(registerBlock);
2935

NativeScript/ffi/Block.mm

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
#include "Block.h"
22
#import <Foundation/Foundation.h>
3+
#include <cstdint>
4+
#include <cstring>
5+
#include <unordered_map>
36
#include "Interop.h"
47
#include "ObjCBridge.h"
58
#include "js_native_api.h"
69
#include "js_native_api_types.h"
710
#include "node_api_util.h"
811
#include "objc/runtime.h"
9-
#include <cstring>
10-
#include <unordered_map>
1112

1213
struct Block_descriptor_1 {
1314
unsigned long int reserved; // NULL
@@ -34,6 +35,7 @@
3435
constexpr int kBlockNeedsFree = (1 << 24);
3536
constexpr int kBlockHasCopyDispose = (1 << 25);
3637
constexpr int kBlockRefCountOne = (1 << 1);
38+
constexpr int kBlockHasSignature = (1 << 30);
3739
std::unordered_map<void*, napi_ref> g_blockToJsFunction;
3840

3941
void block_copy(void* dest, void* src) {
@@ -165,6 +167,54 @@ id registerBlock(napi_env env, Closure* closure, napi_value callback) {
165167
return (id)block;
166168
}
167169

170+
napi_value getCachedBlockCallback(napi_env env, void* blockPtr) {
171+
return getCachedBlockJsFunction(env, blockPtr);
172+
}
173+
174+
bool isObjCBlockObject(id obj) {
175+
if (obj == nil) {
176+
return false;
177+
}
178+
179+
Class cls = object_getClass(obj);
180+
if (cls == nil) {
181+
return false;
182+
}
183+
184+
const char* className = class_getName(cls);
185+
if (className == nullptr) {
186+
return false;
187+
}
188+
189+
// Runtime block classes are typically internal names like
190+
// __NSGlobalBlock__, __NSMallocBlock__, __NSStackBlock__.
191+
return className[0] == '_' && className[1] == '_' && strstr(className, "Block") != nullptr;
192+
}
193+
194+
const char* getObjCBlockSignature(void* blockPtr) {
195+
auto block = static_cast<Block_literal_1*>(blockPtr);
196+
if (block == nullptr || block->descriptor == nullptr) {
197+
return nullptr;
198+
}
199+
200+
if ((block->flags & kBlockHasSignature) == 0) {
201+
return nullptr;
202+
}
203+
204+
// Descriptor layout:
205+
// unsigned long reserved;
206+
// unsigned long size;
207+
// [copy_helper, dispose_helper] if BLOCK_HAS_COPY_DISPOSE
208+
// const char* signature if BLOCK_HAS_SIGNATURE
209+
auto descriptorCursor = reinterpret_cast<uint8_t*>(block->descriptor);
210+
descriptorCursor += sizeof(unsigned long) * 2;
211+
if ((block->flags & kBlockHasCopyDispose) != 0) {
212+
descriptorCursor += sizeof(void*) * 2;
213+
}
214+
215+
return *reinterpret_cast<const char**>(descriptorCursor);
216+
}
217+
168218
NAPI_FUNCTION(registerBlock) {
169219
NAPI_CALLBACK_BEGIN(2)
170220

@@ -234,8 +284,63 @@ id registerBlock(napi_env env, Closure* closure, napi_value callback) {
234284
return result;
235285
}
236286

287+
napi_value FunctionPointer::wrapWithEncoding(napi_env env, void* function, const char* encoding,
288+
bool isBlock) {
289+
if (function == nullptr || encoding == nullptr || encoding[0] == '\0') {
290+
napi_value nullValue;
291+
napi_get_null(env, &nullValue);
292+
return nullValue;
293+
}
294+
295+
if (isBlock) {
296+
napi_value cached = getCachedBlockJsFunction(env, function);
297+
if (cached != nullptr) {
298+
return cached;
299+
}
300+
}
301+
302+
FunctionPointer* ref = new FunctionPointer();
303+
ref->function = function;
304+
ref->offset = 0;
305+
ref->ownsCif = true;
306+
ref->cif = new Cif(env, encoding, isBlock ? 1 : 0);
307+
308+
napi_value result;
309+
napi_create_function(env, isBlock ? "objcBlockWrapper" : "cFunctionWrapper", NAPI_AUTO_LENGTH,
310+
isBlock ? jsCallAsBlock : jsCallAsCFunction, ref, &result);
311+
312+
// Allow fast pointer extraction when JS function wrappers are passed back to native.
313+
napi_ref nativePointerRef;
314+
napi_wrap(env, result, function, nullptr, nullptr, &nativePointerRef);
315+
(void)nativePointerRef;
316+
317+
// Keep raw pointer metadata without overriding the function callback data.
318+
// Overriding callback data breaks JS invocation for wrapped function pointers.
319+
napi_value ptrExternal;
320+
napi_create_external(env, function, nullptr, nullptr, &ptrExternal);
321+
napi_property_descriptor ptrProp = {
322+
.utf8name = "__ns_native_ptr",
323+
.method = nullptr,
324+
.getter = nullptr,
325+
.setter = nullptr,
326+
.value = ptrExternal,
327+
.attributes = napi_default,
328+
.data = nullptr,
329+
};
330+
napi_define_properties(env, result, 1, &ptrProp);
331+
332+
napi_ref jsRef;
333+
napi_add_finalizer(env, result, ref, FunctionPointer::finalize, nullptr, &jsRef);
334+
335+
return result;
336+
}
337+
237338
void FunctionPointer::finalize(napi_env env, void* finalize_data, void* finalize_hint) {
238339
auto ref = (FunctionPointer*)finalize_data;
340+
if (ref->ownsCif && ref->cif != nullptr) {
341+
delete ref->cif;
342+
ref->cif = nullptr;
343+
}
239344
delete ref;
240345
}
241346

NativeScript/ffi/Cif.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ class Cif {
3232
bool shouldFreeAny;
3333
bool* shouldFree;
3434

35-
Cif(napi_env env, std::string typeEncoding);
35+
Cif(napi_env env, std::string typeEncoding, unsigned int implicitArgc = 2);
3636
Cif(napi_env env, Method method);
3737
Cif(napi_env env, MDMetadataReader* reader, MDSectionOffset offset,
3838
bool isMethod = false, bool isBlock = false);
39-
39+
4040
~Cif();
4141
};
4242

NativeScript/ffi/Cif.mm

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
#include "Cif.h"
22
#include <Foundation/Foundation.h>
33
#include <algorithm>
4+
#include <cstring>
45
#include <iostream>
56
#include <vector>
67
#include "Metadata.h"
78
#include "MetadataReader.h"
89
#include "ObjCBridge.h"
910
#include "TypeConv.h"
1011
#include "Util.h"
11-
#include <cstring>
1212

1313
namespace nativescript {
1414

@@ -64,10 +64,11 @@
6464
return cif;
6565
}
6666

67-
Cif::Cif(napi_env env, std::string encoding) {
67+
Cif::Cif(napi_env env, std::string encoding, unsigned int implicitArgc) {
6868
auto signature = [NSMethodSignature signatureWithObjCTypes:encoding.c_str()];
6969
unsigned long numberOfArguments = signature.numberOfArguments;
70-
this->argc = (int)numberOfArguments - 2;
70+
unsigned long skippedArgs = std::min<unsigned long>(numberOfArguments, implicitArgc);
71+
this->argc = (int)(numberOfArguments - skippedArgs);
7172
this->argv = (napi_value*)malloc(sizeof(napi_value) * this->argc);
7273

7374
unsigned int totalArgc = (unsigned int)numberOfArguments;
@@ -101,7 +102,7 @@
101102
auto argTypeInfo = TypeConv::Make(env, &argenc);
102103
this->atypes[i] = argTypeInfo->type;
103104

104-
if (i >= 2) {
105+
if (i >= skippedArgs) {
105106
this->argTypes.push_back(argTypeInfo);
106107
}
107108
}
@@ -116,7 +117,7 @@
116117
}
117118

118119
for (unsigned int i = 0; i < this->argc; i++) {
119-
this->avalues[i] = malloc(cif.arg_types[i + 2]->size);
120+
this->avalues[i] = malloc(cif.arg_types[i + skippedArgs]->size);
120121
this->avaluesAllocCount++;
121122
}
122123
}

0 commit comments

Comments
 (0)