Skip to content

Commit 2e97941

Browse files
committed
feat: align runtime parity across engines
1 parent 6d93051 commit 2e97941

53 files changed

Lines changed: 6428 additions & 4713 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

NativeScript/ffi/hermes/NativeApiJsi.mm

Lines changed: 3 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -89,175 +89,8 @@ void SetNativeApiObjectPrototype(Runtime& runtime, Object& object,
8989
#include "../shared/bridge/HostObject.mm"
9090
// clang-format on
9191

92-
// --- GSD (Generated Signature Dispatch) for Hermes/JSI ---
93-
// GsdObjCContext is the engine-neutral interface the generated invokers use:
94-
// it reads jsi::Value arguments and writes the jsi::Value return value using
95-
// the shared engine-neutral conversion helpers (which already operate on the
96-
// jsi value type). Readers require the fast representation; anything else
97-
// makes a reader return false so the invoker falls back to the generic path.
98-
struct GsdObjCContext;
99-
using ObjCGsdInvoker = bool (*)(GsdObjCContext&);
100-
struct ObjCGsdDispatchEntry {
101-
uint64_t dispatchId;
102-
ObjCGsdInvoker invoker;
103-
};
104-
105-
struct GsdObjCContext {
106-
Runtime& runtime;
107-
const std::shared_ptr<NativeApiBridge>& bridge;
108-
id self;
109-
SEL selector;
110-
const Value* arguments;
111-
const NativeApiType& returnType;
112-
Value result = Value::undefined();
113-
114-
template <typename Invocation>
115-
void invokeNative(Invocation&& invocation) {
116-
performGeneratedObjCInvocation(runtime, bridge, [&]() { invocation(); });
117-
}
118-
119-
bool readNumber(size_t i, double* out) {
120-
const Value& v = arguments[i];
121-
if (!v.isNumber()) return false;
122-
*out = v.asNumber();
123-
return true;
124-
}
125-
bool readBool(size_t i, uint8_t* out) {
126-
const Value& v = arguments[i];
127-
if (!v.isBool()) return false;
128-
*out = v.getBool() ? 1 : 0;
129-
return true;
130-
}
131-
template <class T>
132-
bool readSigned(size_t i, T* out) {
133-
double tmp = 0;
134-
if (!readNumber(i, &tmp)) return false;
135-
*out = static_cast<T>(tmp);
136-
return true;
137-
}
138-
template <class T>
139-
bool readUnsigned(size_t i, T* out) {
140-
double tmp = 0;
141-
if (!readNumber(i, &tmp)) return false;
142-
*out = static_cast<T>(tmp);
143-
return true;
144-
}
145-
bool readFloat(size_t i, float* out) {
146-
double tmp = 0;
147-
if (!readNumber(i, &tmp)) return false;
148-
*out = static_cast<float>(tmp);
149-
return true;
150-
}
151-
bool readDouble(size_t i, double* out) { return readNumber(i, out); }
152-
bool readSelector(size_t i, SEL* out) {
153-
return readFastEngineSelectorArgument(runtime, arguments[i], out);
154-
}
155-
bool readClass(size_t i, Class* out) {
156-
Class cls = classFromEngineValue(runtime, arguments[i]);
157-
if (cls == Nil) return false;
158-
*out = cls;
159-
return true;
160-
}
161-
bool readObject(size_t i, id* out) {
162-
const Value& v = arguments[i];
163-
if (v.isNull() || v.isUndefined()) {
164-
*out = nil;
165-
return true;
166-
}
167-
if (!v.isObject()) return false;
168-
Object o = v.getObject(runtime);
169-
if (o.isHostObject<NativeApiObjectHostObject>(runtime)) {
170-
*out = o.getHostObject<NativeApiObjectHostObject>(runtime)->object();
171-
return true;
172-
}
173-
if (o.isHostObject<NativeApiClassHostObject>(runtime)) {
174-
*out = static_cast<id>(
175-
o.getHostObject<NativeApiClassHostObject>(runtime)->nativeClass());
176-
return true;
177-
}
178-
Class cls = classFromEngineValue(runtime, v);
179-
if (cls != Nil) {
180-
*out = static_cast<id>(cls);
181-
return true;
182-
}
183-
if (o.isHostObject<NativeApiProtocolHostObject>(runtime)) {
184-
*out = static_cast<id>(
185-
o.getHostObject<NativeApiProtocolHostObject>(runtime)
186-
->nativeProtocol());
187-
return true;
188-
}
189-
return false;
190-
}
191-
192-
void setVoid() { result = Value::undefined(); }
193-
void setBool(bool v) { result = Value(v); }
194-
void setInt32(int32_t v) { result = Value(static_cast<double>(v)); }
195-
void setUInt32(uint32_t v) { result = Value(static_cast<double>(v)); }
196-
void setUInt16(uint16_t v) {
197-
if (v >= 32 && v <= 126) {
198-
result = makeString(runtime, std::string(1, static_cast<char>(v)));
199-
} else {
200-
result = Value(static_cast<double>(v));
201-
}
202-
}
203-
void setInt64(int64_t v) { result = signedInteger64ToEngineValue(runtime, v); }
204-
void setUInt64(uint64_t v) {
205-
result = unsignedInteger64ToEngineValue(runtime, v);
206-
}
207-
void setDouble(double v) { result = Value(v); }
208-
void setSelector(SEL v) {
209-
const char* name = v != nullptr ? sel_getName(v) : nullptr;
210-
result = name != nullptr ? makeString(runtime, name) : Value::null();
211-
}
212-
void setClass(Class v) {
213-
if (v == nil) {
214-
result = Value::null();
215-
return;
216-
}
217-
const char* name = class_getName(v);
218-
NativeApiSymbol symbol{
219-
.kind = NativeApiSymbolKind::Class,
220-
.offset = MD_SECTION_OFFSET_NULL,
221-
.name = name != nullptr ? name : "",
222-
.runtimeName = name != nullptr ? name : "",
223-
};
224-
if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) {
225-
symbol = *found;
226-
}
227-
result = makeNativeClassValue(runtime, bridge, std::move(symbol));
228-
}
229-
void setObject(id obj) {
230-
result = convertNativeReturnValue(runtime, bridge, returnType, &obj);
231-
}
232-
};
233-
234-
// Close the anonymous namespace so the generated dispatch table lives in
235-
// namespace nativescript; GsdObjCContext/ObjCGsdDispatchEntry stay reachable
236-
// via the unnamed namespace's implicit using-directive.
237-
} // namespace (temporary close for GSD .inc)
238-
239-
#if defined(__has_include)
240-
#if __has_include("GeneratedGsdSignatureDispatch.inc")
241-
#include "GeneratedGsdSignatureDispatch.inc"
242-
#endif
243-
#endif
244-
245-
#ifndef NS_HAS_GENERATED_SIGNATURE_GSD_DISPATCH
246-
inline constexpr ObjCGsdDispatchEntry kGeneratedObjCGsdDispatchEntries[] = {
247-
{0, nullptr}};
248-
#endif
249-
250-
ObjCGsdInvoker lookupObjCGsdInvoker(uint64_t dispatchId) {
251-
if (!isGeneratedDispatchEnabled()) {
252-
return nullptr;
253-
}
254-
return lookupDispatchInvoker<ObjCGsdDispatchEntry, ObjCGsdInvoker>(
255-
kGeneratedObjCGsdDispatchEntries, dispatchId);
256-
}
257-
258-
namespace { // reopen anonymous namespace
92+
#include "NativeApiJsiGsd.mm"
25993

260-
// --- End GSD ---
26194

26295
void* lookupGeneratedEngineObjCGsdInvoker(uint64_t dispatchId) {
26396
return reinterpret_cast<void*>(lookupObjCGsdInvoker(dispatchId));
@@ -267,10 +100,9 @@ bool tryCallGeneratedEngineObjCSelector(
267100
Runtime& runtime, const std::shared_ptr<NativeApiBridge>& bridge,
268101
id receiver, const NativeApiPreparedObjCInvocation& prepared,
269102
const Value* args, size_t count, Class dispatchSuperClass, Value* result) {
270-
const bool dispatchingNativeCallToUI = shouldDispatchNativeCallToUI();
271103
if (result == nullptr || receiver == nil ||
272104
!prepared.gsdEngineCallable || dispatchSuperClass != Nil ||
273-
count != prepared.gsdEngineArgumentCount || dispatchingNativeCallToUI) {
105+
count != prepared.gsdEngineArgumentCount) {
274106
return false;
275107
}
276108

@@ -426,11 +258,9 @@ throw JSError(runtime,
426258
// typed cast, produce the jsi return value — bypassing all generic
427259
// marshalling. Only engages for plain calls (no super dispatch, init
428260
// disown handling, or implicit NSError-out argument).
429-
const bool dispatchingNativeCallToUI = shouldDispatchNativeCallToUI();
430261
if (prepared->gsdEngineCallable && gsdDispatchClass == Nil &&
431262
count == prepared->gsdEngineArgumentCount &&
432-
!(!receiverIsClass && prepared->isInitMethod) &&
433-
!dispatchingNativeCallToUI) {
263+
!(!receiverIsClass && prepared->isInitMethod)) {
434264
auto invoker =
435265
reinterpret_cast<ObjCGsdInvoker>(prepared->engineInvoker);
436266
GsdObjCContext ctx{runtime, bridge, receiver, prepared->selector,
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// --- GSD (Generated Signature Dispatch) for Hermes/JSI ---
2+
// GsdObjCContext is the engine-neutral interface the generated invokers use:
3+
// it reads jsi::Value arguments and writes the jsi::Value return value using
4+
// the shared engine-neutral conversion helpers (which already operate on the
5+
// jsi value type). Readers require the fast representation; anything else
6+
// makes a reader return false so the invoker falls back to the generic path.
7+
struct GsdObjCContext;
8+
using ObjCGsdInvoker = bool (*)(GsdObjCContext&);
9+
struct ObjCGsdDispatchEntry {
10+
uint64_t dispatchId;
11+
ObjCGsdInvoker invoker;
12+
};
13+
14+
struct GsdObjCContext {
15+
Runtime& runtime;
16+
const std::shared_ptr<NativeApiBridge>& bridge;
17+
id self;
18+
SEL selector;
19+
const Value* arguments;
20+
const NativeApiType& returnType;
21+
Value result = Value::undefined();
22+
23+
template <typename Invocation>
24+
void invokeNative(Invocation&& invocation) {
25+
performGeneratedObjCInvocation(runtime, bridge, [&]() { invocation(); });
26+
}
27+
28+
bool readNumber(size_t i, double* out) {
29+
const Value& v = arguments[i];
30+
if (!v.isNumber()) return false;
31+
*out = v.asNumber();
32+
return true;
33+
}
34+
bool readBool(size_t i, uint8_t* out) {
35+
const Value& v = arguments[i];
36+
if (!v.isBool()) return false;
37+
*out = v.getBool() ? 1 : 0;
38+
return true;
39+
}
40+
template <class T>
41+
bool readSigned(size_t i, T* out) {
42+
double tmp = 0;
43+
if (!readNumber(i, &tmp)) return false;
44+
*out = static_cast<T>(tmp);
45+
return true;
46+
}
47+
template <class T>
48+
bool readUnsigned(size_t i, T* out) {
49+
double tmp = 0;
50+
if (!readNumber(i, &tmp)) return false;
51+
*out = static_cast<T>(tmp);
52+
return true;
53+
}
54+
bool readFloat(size_t i, float* out) {
55+
double tmp = 0;
56+
if (!readNumber(i, &tmp)) return false;
57+
*out = static_cast<float>(tmp);
58+
return true;
59+
}
60+
bool readDouble(size_t i, double* out) { return readNumber(i, out); }
61+
bool readSelector(size_t i, SEL* out) {
62+
return readFastEngineSelectorArgument(runtime, arguments[i], out);
63+
}
64+
bool readClass(size_t i, Class* out) {
65+
Class cls = classFromEngineValue(runtime, arguments[i]);
66+
if (cls == Nil) return false;
67+
*out = cls;
68+
return true;
69+
}
70+
bool readObject(size_t i, id* out) {
71+
const Value& v = arguments[i];
72+
if (v.isNull() || v.isUndefined()) {
73+
*out = nil;
74+
return true;
75+
}
76+
if (!v.isObject()) return false;
77+
Object o = v.getObject(runtime);
78+
if (o.isHostObject<NativeApiObjectHostObject>(runtime)) {
79+
*out = o.getHostObject<NativeApiObjectHostObject>(runtime)->object();
80+
return true;
81+
}
82+
if (o.isHostObject<NativeApiClassHostObject>(runtime)) {
83+
*out = static_cast<id>(
84+
o.getHostObject<NativeApiClassHostObject>(runtime)->nativeClass());
85+
return true;
86+
}
87+
Class cls = classFromEngineValue(runtime, v);
88+
if (cls != Nil) {
89+
*out = static_cast<id>(cls);
90+
return true;
91+
}
92+
if (o.isHostObject<NativeApiProtocolHostObject>(runtime)) {
93+
*out = static_cast<id>(
94+
o.getHostObject<NativeApiProtocolHostObject>(runtime)
95+
->nativeProtocol());
96+
return true;
97+
}
98+
return false;
99+
}
100+
101+
void setVoid() { result = Value::undefined(); }
102+
void setBool(bool v) { result = Value(v); }
103+
void setInt32(int32_t v) { result = Value(static_cast<double>(v)); }
104+
void setUInt32(uint32_t v) { result = Value(static_cast<double>(v)); }
105+
void setUInt16(uint16_t v) {
106+
if (v >= 32 && v <= 126) {
107+
result = makeString(runtime, std::string(1, static_cast<char>(v)));
108+
} else {
109+
result = Value(static_cast<double>(v));
110+
}
111+
}
112+
void setInt64(int64_t v) { result = signedInteger64ToEngineValue(runtime, v); }
113+
void setUInt64(uint64_t v) {
114+
result = unsignedInteger64ToEngineValue(runtime, v);
115+
}
116+
void setDouble(double v) { result = Value(v); }
117+
void setSelector(SEL v) {
118+
const char* name = v != nullptr ? sel_getName(v) : nullptr;
119+
result = name != nullptr ? makeString(runtime, name) : Value::null();
120+
}
121+
void setClass(Class v) {
122+
if (v == nil) {
123+
result = Value::null();
124+
return;
125+
}
126+
const char* name = class_getName(v);
127+
NativeApiSymbol symbol{
128+
.kind = NativeApiSymbolKind::Class,
129+
.offset = MD_SECTION_OFFSET_NULL,
130+
.name = name != nullptr ? name : "",
131+
.runtimeName = name != nullptr ? name : "",
132+
};
133+
if (const NativeApiSymbol* found = bridge->findClass(symbol.name)) {
134+
symbol = *found;
135+
}
136+
result = makeNativeClassValue(runtime, bridge, std::move(symbol));
137+
}
138+
void setObject(id obj) {
139+
result = convertNativeReturnValue(runtime, bridge, returnType, &obj);
140+
}
141+
};
142+
143+
// Close the anonymous namespace so the generated dispatch table lives in
144+
// namespace nativescript; GsdObjCContext/ObjCGsdDispatchEntry stay reachable
145+
// via the unnamed namespace's implicit using-directive.
146+
} // namespace (temporary close for GSD .inc)
147+
148+
#if defined(__has_include)
149+
#if __has_include("GeneratedGsdSignatureDispatch.inc")
150+
#include "GeneratedGsdSignatureDispatch.inc"
151+
#endif
152+
#endif
153+
154+
#ifndef NS_HAS_GENERATED_SIGNATURE_GSD_DISPATCH
155+
inline constexpr ObjCGsdDispatchEntry kGeneratedObjCGsdDispatchEntries[] = {
156+
{0, nullptr}};
157+
#endif
158+
159+
ObjCGsdInvoker lookupObjCGsdInvoker(uint64_t dispatchId) {
160+
if (!isGeneratedDispatchEnabled()) {
161+
return nullptr;
162+
}
163+
return lookupDispatchInvoker<ObjCGsdDispatchEntry, ObjCGsdInvoker>(
164+
kGeneratedObjCGsdDispatchEntries, dispatchId);
165+
}
166+
167+
namespace { // reopen anonymous namespace
168+
169+
// --- End GSD ---

0 commit comments

Comments
 (0)