• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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