1 /*
2 * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <MacTypes.h>
17 #include <_types/_uint32_t.h>
18 #include <_types/_uint8_t.h>
19 #include <cstdint>
20
21 #include "convertors-jsc.h"
22
23 #include "interop-logging.h"
24
25 // See https://github.com/BabylonJS/BabylonNative/blob/master/Dependencies/napi/napi-direct/source/js_native_api_javascriptcore.cc
26 // for convertors logic.
27
28
getInt32Elements(JSContextRef context,const JSValueRef arguments)29 KInt* getInt32Elements(JSContextRef context, const JSValueRef arguments) {
30 return getTypedElements<KInt>(context, arguments);
31 }
32
getUInt32Elements(JSContextRef context,const JSValueRef arguments)33 uint32_t* getUInt32Elements(JSContextRef context, const JSValueRef arguments) {
34 return getTypedElements<uint32_t>(context, arguments);
35 }
36
getFloat32Elements(JSContextRef context,const JSValueRef arguments)37 float* getFloat32Elements(JSContextRef context, const JSValueRef arguments) {
38 return getTypedElements<float>(context, arguments);
39 }
40
getByteElements(JSContextRef context,const JSValueRef arguments)41 KByte* getByteElements(JSContextRef context, const JSValueRef arguments) {
42 return getTypedElements<KByte>(context, arguments);
43 }
44
getKStringArray(JSContextRef context,const JSValueRef arguments)45 KStringArray getKStringArray(JSContextRef context, const JSValueRef arguments) {
46 return getTypedElements<uint8_t>(context, arguments);
47 }
48
getUShortElements(JSContextRef context,const JSValueRef arguments)49 KUShort* getUShortElements(JSContextRef context, const JSValueRef arguments) {
50 return getTypedElements<KUShort>(context, arguments);
51 }
52
getShortElements(JSContextRef context,const JSValueRef arguments)53 KShort* getShortElements(JSContextRef context, const JSValueRef arguments) {
54 return getTypedElements<KShort>(context, arguments);
55 }
56
getInt32(JSContextRef context,JSValueRef value)57 int32_t getInt32(JSContextRef context, JSValueRef value) {
58 JSValueRef exception {};
59 if (JSValueIsNull(context, value)) {
60 return 0;
61 }
62 if (JSValueIsUndefined(context, value)) {
63 ASSERT(false);
64 return 0;
65 }
66 double result = JSValueToNumber(context, value, &exception);
67 return static_cast<int32_t>(result);
68 }
69
getUInt32(JSContextRef context,JSValueRef value)70 uint32_t getUInt32(JSContextRef context, JSValueRef value) {
71 JSValueRef exception {};
72 if (JSValueIsNull(context, value)) {
73 return 0;
74 }
75 if (JSValueIsUndefined(context, value)) {
76 ASSERT(false);
77 return 0;
78 }
79 double result = JSValueToNumber(context, value, &exception);
80 return static_cast<uint32_t>(result);
81 }
82
getUInt8(JSContextRef context,JSValueRef value)83 uint8_t getUInt8(JSContextRef context, JSValueRef value) {
84 JSValueRef exception {};
85 if (JSValueIsNull(context, value)) {
86 return 0;
87 }
88 if (JSValueIsUndefined(context, value)) {
89 ASSERT(false);
90 return 0;
91 }
92 double result = JSValueToNumber(context, value, &exception);
93 return static_cast<uint8_t>(result);
94 }
95
96 /*
97 static JSStringRef bigintToArrayCastFuncName = JSStringCreateWithUTF8CString("__JSC__castFromBigInt");
98 static JSStringRef bigintToArrayCastFuncParams[] = { JSStringCreateWithUTF8CString("ptr") };
99 static JSStringRef bigintToArrayCastFuncBody = JSStringCreateWithUTF8CString(
100 "return new Uint32Array([ptr & 0xFFFFFFFFn, (ptr >> 32n) & 0xFFFFFFFFn]);"
101 );
102
103 static JSStringRef arrayToBigintCastFuncName = JSStringCreateWithUTF8CString("__JSC__castToBigInt");
104 static JSStringRef arrayToBigintCastFuncParams[] = { JSStringCreateWithUTF8CString("ptr") };
105 static JSStringRef arrayToBigintCastFuncBody = JSStringCreateWithUTF8CString(
106 "return BigInt(ptr[1]) << 32n | BigInt(ptr[0])"
107 );
108 */
109
110 #ifdef KOALA_JSC_USE_CALLBACK_CAST
111
112 static JSStringRef bigIntFromPartsFuncName = JSStringCreateWithUTF8CString("__JSC__bigIntFromParts");
113 static JSStringRef bigIntFromPartsFuncParams[] = { JSStringCreateWithUTF8CString("hi"), JSStringCreateWithUTF8CString("lo") };
114 static JSStringRef bigIntFromPartsFuncBody = JSStringCreateWithUTF8CString(
115 "return BigInt(hi) << 32n | BigInt(lo);"
116 );
117
getGlobalCallback(JSContextRef context,JSStringRef name,JSStringRef params[],JSStringRef body)118 static JSObjectRef getGlobalCallback(JSContextRef context, JSStringRef name, JSStringRef params[], JSStringRef body) {
119 JSObjectRef globalThis = JSContextGetGlobalObject(context);
120 JSValueRef propname = JSValueMakeString(context, name);
121 JSValueRef castFunc = JSObjectGetPropertyForKey(context, globalThis, propname, nullptr);
122 if (JSValueIsUndefined(context, castFunc)) {
123 JSObjectRef castFuncObj = JSObjectMakeFunction(context, name, 1, params, body, nullptr, 0, nullptr);
124 JSPropertyAttributes attributes = kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum;
125 JSObjectSetPropertyForKey(context, globalThis, propname, castFuncObj, attributes, nullptr);
126 return castFuncObj;
127 }
128 return JSValueToObject(context, castFunc, nullptr);
129 }
130
getBigIntFromParts(JSContextRef context)131 static JSObjectRef getBigIntFromParts(JSContextRef context) {
132 return getGlobalCallback(context, bigIntFromPartsFuncName, bigIntFromPartsFuncParams, bigIntFromPartsFuncBody);
133 }
134
135 #endif
136
u64ToBigInt(JSContextRef context,uint64_t value)137 static JSValueRef u64ToBigInt(JSContextRef context, uint64_t value) {
138 JSValueRef bigint;
139 #ifdef KOALA_JSC_USE_CALLBACK_CAST
140 // TODO benchmark this
141 JSObjectRef bigIntFromParts = getBigIntFromParts(context);
142 JSValueRef parts[2] = {
143 JSValueMakeNumber(context, (double) (value >> 32)),
144 JSValueMakeNumber(context, (double) (value & 0xFFFFFFFF)),
145 };
146 bigint = JSObjectCallAsFunction(context, bigIntFromParts, nullptr, 2, parts, nullptr);
147 #else
148 char buffer[128] = {0};
149 #ifdef __STDC_LIB_EXT1__
150 errno_t res = std::snprintf_s(buffer, sizeof(buffer) - 1, "%zun", static_cast<size_t>(value));
151 if (res != EOK) {
152 return bigint;
153 }
154 #else
155 std::snprintf(buffer, sizeof(buffer) - 1, "%zun", static_cast<size_t>(value));
156 #endif
157 JSStringRef script = JSStringCreateWithUTF8CString(buffer);
158 bigint = JSEvaluateScript(context, script, nullptr, nullptr, 0, nullptr);
159 JSStringRelease(script);
160 #endif
161 return bigint;
162 }
163
bigIntToU64(JSContextRef ctx,JSValueRef value)164 static uint64_t bigIntToU64(JSContextRef ctx, JSValueRef value) {
165 char buf[128];
166 JSStringRef strRef = JSValueToStringCopy(ctx, value, nullptr);
167 size_t len = JSStringGetUTF8CString(strRef, buf, sizeof(buf));
168 JSStringRelease(strRef);
169 ASSERT(len < sizeof(buf));
170 char* suf;
171 uint64_t numValue = std::strtoull(buf, &suf, 10);
172 ASSERT(*suf == '\0');
173 return numValue;
174 }
175
getPointer(JSContextRef context,JSValueRef value)176 KNativePointer getPointer(JSContextRef context, JSValueRef value) {
177 uint64_t raw = bigIntToU64(context, value);
178 return reinterpret_cast<void*>(static_cast<uintptr_t>(raw));
179 }
180
getPointerElements(JSContextRef context,JSValueRef value)181 KNativePointerArray getPointerElements(JSContextRef context, JSValueRef value) {
182 if (JSValueIsNull(context, value) || JSValueIsUndefined(context, value)) {
183 return nullptr;
184 }
185
186 ASSERT(JSValueIsObject(context, value));
187 ASSERT(JSValueGetTypedArrayType(context, value, nullptr) == kJSTypedArrayTypeBigUint64Array);
188
189 JSObjectRef typedArray = JSValueToObject(context, value, nullptr);
190 return reinterpret_cast<KNativePointerArray>(JSObjectGetTypedArrayBytesPtr(context, typedArray, nullptr));
191 }
192
getFloat(JSContextRef context,JSValueRef value)193 KFloat getFloat(JSContextRef context, JSValueRef value) {
194 JSValueRef exception {};
195 if (JSValueIsNull(context, value)) {
196 return 0;
197 }
198 if (JSValueIsUndefined(context, value)) {
199 ASSERT(false);
200 return 0;
201 }
202 double result = JSValueToNumber(context, value, &exception);
203 return static_cast<KFloat>(result);
204 }
205
getShort(JSContextRef context,JSValueRef value)206 KShort getShort(JSContextRef context, JSValueRef value) {
207 JSValueRef exception {};
208 if (JSValueIsNull(context, value)) {
209 return 0;
210 }
211 if (JSValueIsUndefined(context, value)) {
212 ASSERT(false);
213 return 0;
214 }
215 double result = JSValueToNumber(context, value, &exception);
216 return static_cast<KShort>(result);
217 }
218
getUShort(JSContextRef context,JSValueRef value)219 KUShort getUShort(JSContextRef context, JSValueRef value) {
220 JSValueRef exception {};
221 if (JSValueIsNull(context, value)) {
222 return 0;
223 }
224 if (JSValueIsUndefined(context, value)) {
225 ASSERT(false);
226 return 0;
227 }
228 double result = JSValueToNumber(context, value, &exception);
229 return static_cast<KUShort>(result);
230 }
231
getString(JSContextRef context,JSValueRef value)232 KStringPtr getString(JSContextRef context, JSValueRef value) {
233 if (JSValueIsNull(context, value)) {
234 return KStringPtr();
235 }
236 if (JSValueIsUndefined(context, value)) {
237 return KStringPtr();
238 }
239 KStringPtr result;
240 JSStringRef valueString = JSValueToStringCopy(context, value, NULL);
241 size_t size = JSStringGetMaximumUTF8CStringSize(valueString);
242 result.resize(size);
243 JSStringGetUTF8CString(valueString, result.data(), size);
244 JSStringRelease(valueString);
245 return result;
246 }
247
getBoolean(JSContextRef context,JSValueRef value)248 KBoolean getBoolean(JSContextRef context, JSValueRef value) {
249 bool result = JSValueToBoolean(context, value);
250 return static_cast<KBoolean>(result);
251 }
252
makeInt32(JSContextRef context,int32_t value)253 JSValueRef makeInt32(JSContextRef context, int32_t value) {
254 return JSValueMakeNumber(context, value);
255 }
256
makeUInt32(JSContextRef context,uint32_t value)257 JSValueRef makeUInt32(JSContextRef context, uint32_t value) {
258 return JSValueMakeNumber(context, value);
259 }
260
makePointer(JSContextRef context,KNativePointer value)261 JSValueRef makePointer(JSContextRef context, KNativePointer value) {
262 return u64ToBigInt(context, static_cast<uint64_t>(reinterpret_cast<uintptr_t>(value)));
263 }
264
makeFloat(JSContextRef context,KFloat value)265 JSValueRef makeFloat(JSContextRef context, KFloat value) {
266 return JSValueMakeNumber(context, value);
267 }
268
makeBoolean(JSContextRef context,KBoolean value)269 JSValueRef makeBoolean(JSContextRef context, KBoolean value) {
270 return JSValueMakeBoolean(context, value);
271 }
272
makeVoid(JSContextRef context)273 JSValueRef makeVoid(JSContextRef context) {
274 return JSValueMakeUndefined(context);
275 }
276
getInstance()277 Exports* Exports::getInstance() {
278 static Exports *instance = nullptr;
279 if (instance == nullptr) {
280 instance = new Exports();
281 }
282 return instance;
283 }
284
InitExports(JSGlobalContextRef globalContext)285 void InitExports(JSGlobalContextRef globalContext) {
286 JSObjectRef globalObject = JSContextGetGlobalObject(globalContext);
287 for (auto impl: Exports::getInstance()->getImpls()) {
288 JSStringRef functionName = JSStringCreateWithUTF8CString(impl.first.c_str());
289 JSObjectRef functionObject = JSObjectMakeFunctionWithCallback(globalContext, functionName, impl.second);
290 JSObjectSetProperty(globalContext, globalObject, functionName, functionObject, kJSPropertyAttributeNone, nullptr);
291 JSStringRelease(functionName);
292 }
293 }
294