Skip to content

Commit da6e518

Browse files
committed
Snapshot current runtime investigation
1 parent f8e8d55 commit da6e518

File tree

13 files changed

+771
-141
lines changed

13 files changed

+771
-141
lines changed

NativeScript/ffi/CFunction.mm

Lines changed: 181 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,187 @@
11
#include "CFunction.h"
2+
#include <dispatch/dispatch.h>
3+
#include <cstring>
4+
#include <vector>
5+
#include "Block.h"
26
#include "ClassMember.h"
7+
#include "Interop.h"
38
#include "ObjCBridge.h"
49
#include "SignatureDispatch.h"
510
#include "ffi/NativeScriptException.h"
611
#include "ffi/Tasks.h"
7-
#include <cstring>
8-
#include <vector>
912
#ifdef ENABLE_JS_RUNTIME
1013
#include "jsr.h"
1114
#endif
1215

1316
namespace nativescript {
1417

18+
namespace {
19+
20+
inline bool unwrapCompatNativeHandleForCFunction(napi_env env, napi_value value, void** out) {
21+
if (value == nullptr || out == nullptr) {
22+
return false;
23+
}
24+
25+
if (Pointer::isInstance(env, value)) {
26+
Pointer* ptr = Pointer::unwrap(env, value);
27+
*out = ptr != nullptr ? ptr->data : nullptr;
28+
return ptr != nullptr;
29+
}
30+
31+
if (Reference::isInstance(env, value)) {
32+
Reference* ref = Reference::unwrap(env, value);
33+
*out = ref != nullptr ? ref->data : nullptr;
34+
return ref != nullptr;
35+
}
36+
37+
napi_valuetype valueType = napi_undefined;
38+
if (napi_typeof(env, value, &valueType) != napi_ok) {
39+
return false;
40+
}
41+
42+
if (valueType == napi_bigint) {
43+
uint64_t raw = 0;
44+
bool lossless = false;
45+
if (napi_get_value_bigint_uint64(env, value, &raw, &lossless) != napi_ok) {
46+
return false;
47+
}
48+
*out = reinterpret_cast<void*>(static_cast<uintptr_t>(raw));
49+
return true;
50+
}
51+
52+
if (valueType == napi_external) {
53+
return napi_get_value_external(env, value, out) == napi_ok;
54+
}
55+
56+
if (valueType != napi_object && valueType != napi_function) {
57+
return false;
58+
}
59+
60+
bool hasNativePointer = false;
61+
if (napi_has_named_property(env, value, "__ns_native_ptr", &hasNativePointer) == napi_ok &&
62+
hasNativePointer) {
63+
napi_value nativePointerValue = nullptr;
64+
if (napi_get_named_property(env, value, "__ns_native_ptr", &nativePointerValue) == napi_ok &&
65+
napi_get_value_external(env, nativePointerValue, out) == napi_ok && *out != nullptr) {
66+
return true;
67+
}
68+
}
69+
70+
return napi_unwrap(env, value, out) == napi_ok && *out != nullptr;
71+
}
72+
73+
inline napi_value createCompatDispatchQueueWrapperForCFunction(napi_env env,
74+
dispatch_queue_t queue) {
75+
if (queue == nullptr) {
76+
napi_value nullValue = nullptr;
77+
napi_get_null(env, &nullValue);
78+
return nullValue;
79+
}
80+
81+
return Pointer::create(env, reinterpret_cast<void*>(queue));
82+
}
83+
84+
inline napi_value tryCallCompatLibdispatchFunction(napi_env env, napi_callback_info cbinfo,
85+
const char* functionName) {
86+
if (strcmp(functionName, "dispatch_get_global_queue") == 0) {
87+
size_t argc = 2;
88+
napi_value argv[2] = {nullptr, nullptr};
89+
napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, nullptr);
90+
91+
int64_t identifier = 0;
92+
if (argc > 0) {
93+
napi_valuetype identifierType = napi_undefined;
94+
if (napi_typeof(env, argv[0], &identifierType) == napi_ok && identifierType == napi_bigint) {
95+
bool lossless = false;
96+
if (napi_get_value_bigint_int64(env, argv[0], &identifier, &lossless) != napi_ok) {
97+
napi_throw_type_error(env, nullptr,
98+
"dispatch_get_global_queue expects a numeric identifier.");
99+
return nullptr;
100+
}
101+
} else {
102+
napi_value coercedIdentifier = nullptr;
103+
if (napi_coerce_to_number(env, argv[0], &coercedIdentifier) != napi_ok ||
104+
napi_get_value_int64(env, coercedIdentifier, &identifier) != napi_ok) {
105+
napi_throw_type_error(env, nullptr,
106+
"dispatch_get_global_queue expects a numeric identifier.");
107+
return nullptr;
108+
}
109+
}
110+
}
111+
112+
uint64_t flags = 0;
113+
if (argc > 1) {
114+
napi_valuetype flagsType = napi_undefined;
115+
if (napi_typeof(env, argv[1], &flagsType) == napi_ok && flagsType == napi_bigint) {
116+
bool lossless = false;
117+
if (napi_get_value_bigint_uint64(env, argv[1], &flags, &lossless) != napi_ok) {
118+
napi_throw_type_error(env, nullptr, "dispatch_get_global_queue expects numeric flags.");
119+
return nullptr;
120+
}
121+
} else {
122+
napi_value coercedFlags = nullptr;
123+
int64_t signedFlags = 0;
124+
if (napi_coerce_to_number(env, argv[1], &coercedFlags) != napi_ok ||
125+
napi_get_value_int64(env, coercedFlags, &signedFlags) != napi_ok) {
126+
napi_throw_type_error(env, nullptr, "dispatch_get_global_queue expects numeric flags.");
127+
return nullptr;
128+
}
129+
flags = static_cast<uint64_t>(signedFlags);
130+
}
131+
}
132+
133+
return createCompatDispatchQueueWrapperForCFunction(
134+
env, dispatch_get_global_queue(identifier, flags));
135+
}
136+
137+
if (strcmp(functionName, "dispatch_get_current_queue") == 0) {
138+
#pragma clang diagnostic push
139+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
140+
return createCompatDispatchQueueWrapperForCFunction(env, dispatch_get_current_queue());
141+
#pragma clang diagnostic pop
142+
}
143+
144+
if (strcmp(functionName, "dispatch_async") == 0) {
145+
size_t argc = 2;
146+
napi_value argv[2] = {nullptr, nullptr};
147+
napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, nullptr);
148+
149+
if (argc < 2) {
150+
napi_throw_type_error(env, nullptr, "dispatch_async expects a queue and callback.");
151+
return nullptr;
152+
}
153+
154+
void* queueHandle = nullptr;
155+
if (!unwrapCompatNativeHandleForCFunction(env, argv[0], &queueHandle) ||
156+
queueHandle == nullptr) {
157+
napi_throw_type_error(env, nullptr, "dispatch_async expects a native queue handle.");
158+
return nullptr;
159+
}
160+
161+
napi_valuetype callbackType = napi_undefined;
162+
if (napi_typeof(env, argv[1], &callbackType) != napi_ok || callbackType != napi_function) {
163+
napi_throw_type_error(env, nullptr, "dispatch_async expects a function callback.");
164+
return nullptr;
165+
}
166+
167+
auto closure = new Closure(std::string("v"), true);
168+
closure->env = env;
169+
id block = registerBlock(env, closure, argv[1]);
170+
dispatch_block_t dispatchBlock = (dispatch_block_t)block;
171+
172+
dispatch_async(reinterpret_cast<dispatch_queue_t>(queueHandle), dispatchBlock);
173+
[block release];
174+
175+
napi_value undefinedValue = nullptr;
176+
napi_get_undefined(env, &undefinedValue);
177+
return undefinedValue;
178+
}
179+
180+
return nullptr;
181+
}
182+
183+
} // namespace
184+
15185
inline void ensureCFunctionDispatchLookup(CFunction* function, Cif* cif) {
16186
if (function == nullptr || cif == nullptr || cif->signatureHash == 0) {
17187
if (function != nullptr) {
@@ -34,8 +204,7 @@ inline void ensureCFunctionDispatchLookup(CFunction* function, Cif* cif) {
34204
cif->signatureHash, SignatureCallKind::CFunction, function->dispatchFlags);
35205
function->preparedInvoker =
36206
reinterpret_cast<void*>(lookupCFunctionPreparedInvoker(function->dispatchId));
37-
function->napiInvoker =
38-
reinterpret_cast<void*>(lookupCFunctionNapiInvoker(function->dispatchId));
207+
function->napiInvoker = reinterpret_cast<void*>(lookupCFunctionNapiInvoker(function->dispatchId));
39208
function->dispatchLookupCached = true;
40209
}
41210

@@ -90,15 +259,20 @@ inline void ensureCFunctionDispatchLookup(CFunction* function, Cif* cif) {
90259

91260
auto name = bridgeState->metadata->getString(offset);
92261

262+
if (strcmp(name, "dispatch_async") == 0 || strcmp(name, "dispatch_get_current_queue") == 0 ||
263+
strcmp(name, "dispatch_get_global_queue") == 0) {
264+
return tryCallCompatLibdispatchFunction(env, cbinfo, name);
265+
}
266+
93267
auto func = bridgeState->getCFunction(env, offset);
94268

95269
auto cif = func->cif;
96270
ensureCFunctionDispatchLookup(func, cif);
97271
auto preparedInvoker = reinterpret_cast<CFunctionPreparedInvoker>(func->preparedInvoker);
98272
auto napiInvoker = reinterpret_cast<CFunctionNapiInvoker>(func->napiInvoker);
99273

100-
MDFunctionFlag functionFlags = bridgeState->metadata->getFunctionFlag(
101-
offset + sizeof(MDSectionOffset) * 2);
274+
MDFunctionFlag functionFlags =
275+
bridgeState->metadata->getFunctionFlag(offset + sizeof(MDSectionOffset) * 2);
102276

103277
const napi_value* invocationArgs = nullptr;
104278
std::vector<napi_value> dynamicArgs;
@@ -139,7 +313,7 @@ inline void ensureCFunctionDispatchLookup(CFunction* function, Cif* cif) {
139313
const bool isMainEntrypoint =
140314
strcmp(name, "UIApplicationMain") == 0 || strcmp(name, "NSApplicationMain") == 0;
141315

142-
if (napiInvoker != nullptr && !isMainEntrypoint) {
316+
if (napiInvoker != nullptr && !cif->skipGeneratedNapiDispatch && !isMainEntrypoint) {
143317
@try {
144318
if (!napiInvoker(env, cif, func->fnptr, invocationArgs, cif->rvalue)) {
145319
return nullptr;

NativeScript/ffi/Cif.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Cif {
2020
size_t rvalueLength;
2121
bool isVariadic = false;
2222
uint64_t signatureHash = 0;
23+
bool skipGeneratedNapiDispatch = false;
2324

2425
void* rvalue;
2526
void** avalues;

NativeScript/ffi/Cif.mm

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
constexpr uint64_t kFNV64OffsetBasis = 14695981039346656037ull;
1919
constexpr uint64_t kFNV64Prime = 1099511628211ull;
2020

21-
uint64_t hashBytesFnv1a(const void* data, size_t size,
22-
uint64_t seed = kFNV64OffsetBasis) {
21+
uint64_t hashBytesFnv1a(const void* data, size_t size, uint64_t seed = kFNV64OffsetBasis) {
2322
const auto* bytes = static_cast<const uint8_t*>(data);
2423
uint64_t hash = seed;
2524
for (size_t i = 0; i < size; i++) {
@@ -53,24 +52,52 @@ void appendIntegralToHash(uint64_t* hash, T value) {
5352
}
5453
}
5554

56-
bool appendMetadataSignatureHash(MDMetadataReader* reader,
57-
MDSectionOffset signatureOffset,
55+
bool appendMetadataSignatureHash(MDMetadataReader* reader, MDSectionOffset signatureOffset,
5856
std::unordered_set<MDSectionOffset>* activeSignatures,
5957
uint64_t* hash);
6058

59+
inline bool typeRequiresSlowGeneratedNapiDispatch(const std::shared_ptr<TypeConv>& type) {
60+
if (type == nullptr) {
61+
return false;
62+
}
63+
64+
switch (type->kind) {
65+
case mdTypeUChar:
66+
case mdTypeUInt8:
67+
return true;
68+
default:
69+
return false;
70+
}
71+
}
72+
73+
inline void updateGeneratedNapiDispatchCompatibility(Cif* cif) {
74+
if (cif == nullptr) {
75+
return;
76+
}
77+
78+
cif->skipGeneratedNapiDispatch = typeRequiresSlowGeneratedNapiDispatch(cif->returnType);
79+
if (cif->skipGeneratedNapiDispatch) {
80+
return;
81+
}
82+
83+
for (const auto& argType : cif->argTypes) {
84+
if (typeRequiresSlowGeneratedNapiDispatch(argType)) {
85+
cif->skipGeneratedNapiDispatch = true;
86+
return;
87+
}
88+
}
89+
}
90+
6191
bool appendMetadataTypeHash(MDMetadataReader* reader, MDSectionOffset* offset,
62-
std::unordered_set<MDSectionOffset>* activeSignatures,
63-
uint64_t* hash) {
64-
if (reader == nullptr || offset == nullptr || hash == nullptr ||
65-
activeSignatures == nullptr) {
92+
std::unordered_set<MDSectionOffset>* activeSignatures, uint64_t* hash) {
93+
if (reader == nullptr || offset == nullptr || hash == nullptr || activeSignatures == nullptr) {
6694
return false;
6795
}
6896

6997
const MDTypeKind kindWithFlags = reader->getTypeKind(*offset);
7098
*offset += sizeof(MDTypeKind);
7199
const MDTypeKind rawKind =
72-
static_cast<MDTypeKind>((kindWithFlags & ~mdTypeFlagNext) &
73-
~mdTypeFlagVariadic);
100+
static_cast<MDTypeKind>((kindWithFlags & ~mdTypeFlagNext) & ~mdTypeFlagVariadic);
74101

75102
appendIntegralToHash<uint8_t>(hash, 0xB0);
76103
const MDTypeKind canonicalKind = canonicalizeSignatureTypeKind(rawKind);
@@ -130,10 +157,8 @@ bool appendMetadataTypeHash(MDMetadataReader* reader, MDSectionOffset* offset,
130157
const auto nestedSignatureOffset = reader->getOffset(*offset);
131158
*offset += sizeof(MDSectionOffset);
132159
if (nestedSignatureOffset != MD_SECTION_OFFSET_NULL) {
133-
const auto nestedAbsoluteOffset =
134-
reader->signaturesOffset + nestedSignatureOffset;
135-
if (!appendMetadataSignatureHash(reader, nestedAbsoluteOffset,
136-
activeSignatures, hash)) {
160+
const auto nestedAbsoluteOffset = reader->signaturesOffset + nestedSignatureOffset;
161+
if (!appendMetadataSignatureHash(reader, nestedAbsoluteOffset, activeSignatures, hash)) {
137162
return false;
138163
}
139164
}
@@ -148,8 +173,7 @@ bool appendMetadataTypeHash(MDMetadataReader* reader, MDSectionOffset* offset,
148173
return true;
149174
}
150175

151-
bool appendMetadataSignatureHash(MDMetadataReader* reader,
152-
MDSectionOffset signatureOffset,
176+
bool appendMetadataSignatureHash(MDMetadataReader* reader, MDSectionOffset signatureOffset,
153177
std::unordered_set<MDSectionOffset>* activeSignatures,
154178
uint64_t* hash) {
155179
if (reader == nullptr || hash == nullptr || activeSignatures == nullptr) {
@@ -301,6 +325,8 @@ bool appendMetadataSignatureHash(MDMetadataReader* reader,
301325
this->avalues[i] = malloc(cif.arg_types[i + skippedArgs]->size);
302326
this->avaluesAllocCount++;
303327
}
328+
329+
updateGeneratedNapiDispatchCompatibility(this);
304330
}
305331

306332
Cif::Cif(napi_env env, Method method) {
@@ -359,6 +385,8 @@ bool appendMetadataSignatureHash(MDMetadataReader* reader,
359385
this->avalues[i] = malloc(cif.arg_types[i + 2]->size);
360386
this->avaluesAllocCount++;
361387
}
388+
389+
updateGeneratedNapiDispatchCompatibility(this);
362390
}
363391

364392
Cif::Cif(napi_env env, MDMetadataReader* reader, MDSectionOffset offset, bool isMethod,
@@ -442,6 +470,8 @@ bool appendMetadataSignatureHash(MDMetadataReader* reader,
442470
signatureHash = canonicalSignatureHash;
443471
}
444472
}
473+
474+
updateGeneratedNapiDispatchCompatibility(this);
445475
}
446476

447477
Cif::~Cif() {

NativeScript/ffi/Class.mm

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -304,14 +304,15 @@ void setupObjCClassDecorator(napi_env env) {
304304
if (argc == 1 && argv[0] != nullptr) {
305305
void* rawPointer = nullptr;
306306
if (tryGetInteropPointerArg(env, argv[0], &rawPointer) && rawPointer != nullptr) {
307+
if (napi_value cached = bridgeState->getCachedHandleObject(env, rawPointer);
308+
cached != nullptr) {
309+
return cached;
310+
}
311+
307312
object = (id)rawPointer;
308-
napi_value constructor = nullptr;
309-
if (napi_get_named_property(env, jsThis, "constructor", &constructor) == napi_ok &&
310-
constructor != nullptr) {
311-
napi_value existing = bridgeState->getObject(env, object, constructor, kUnownedObject);
312-
if (existing != nullptr) {
313-
return existing;
314-
}
313+
if (napi_value existing = bridgeState->findCachedObjectWrapper(env, object);
314+
existing != nullptr) {
315+
return existing;
315316
}
316317

317318
jsThis = bridgeState->proxyNativeObject(env, jsThis, object);

0 commit comments

Comments
 (0)