• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 <cstring>
17 
18 #include "native_engine/native_value.h"
19 #include "quickjs_headers.h"
20 #include "utils/log.h"
21 
22 struct JSObjectInfo {
23     JSContext* context = nullptr;
24     JSFinalizer finalizer = nullptr;
25     void* data = nullptr;
26     void* hint = nullptr;
27 };
28 
29 JSClassID g_baseClassId = 1;
30 
AddIntrinsicExternal(JSContext * context)31 void AddIntrinsicExternal(JSContext* context)
32 {
33     const char* className = "External";
34     JSValue global = JS_GetGlobalObject(context);
35     JSValue external = JS_NewCFunction2(
36         context,
37         [](JSContext* ctx, JSValueConst newTarget, int argc, JSValueConst* argv) {
38             JSValue proto = JS_GetPropertyStr(ctx, newTarget, "prototype");
39             JSValue result = JS_NewObjectProtoClass(ctx, proto, GetBaseClassID());
40             JS_FreeValue(ctx, proto);
41             return result;
42         },
43         className, strlen(className), JS_CFUNC_constructor, 0);
44     JSValue proto = JS_NewObject(context);
45 
46     JS_DefinePropertyValueStr(context, external, "prototype", JS_DupValue(context, proto), 0);
47     JS_DefinePropertyValueStr(
48         context, proto, "constructor", JS_DupValue(context, external), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
49 
50     JS_SetPropertyStr(context, global, "External", external);
51     JS_FreeValue(context, proto);
52     JS_FreeValue(context, global);
53 }
54 
GetBaseClassID()55 JSClassID GetBaseClassID()
56 {
57     return g_baseClassId;
58 }
59 
JS_NewExternal(JSContext * context,void * value,JSFinalizer finalizer,void * hint)60 JSValue JS_NewExternal(JSContext* context, void* value, JSFinalizer finalizer, void* hint)
61 {
62     JSValue global = JS_GetGlobalObject(context);
63     JSValue external = JS_GetPropertyStr(context, global, "External");
64     JSValue result = JS_CallConstructor(context, external, 0, nullptr);
65 
66     auto info = new JSObjectInfo();
67     if (info != nullptr) {
68         info->context = context;
69         info->finalizer = finalizer;
70         info->data = value;
71         info->hint = hint;
72         JS_SetOpaque(result, info);
73     }
74 
75     JS_FreeValue(context, external);
76     JS_FreeValue(context, global);
77     return result;
78 }
79 
JS_ExternalToNativeObject(JSContext * context,JSValue value)80 void* JS_ExternalToNativeObject(JSContext* context, JSValue value)
81 {
82     auto* info = reinterpret_cast<JSObjectInfo*>(JS_GetOpaque(value, GetBaseClassID()));
83     return (info != nullptr) ? info->data : nullptr;
84 }
85 
JS_IsExternal(JSContext * context,JSValue value)86 bool JS_IsExternal(JSContext* context, JSValue value)
87 {
88     bool result = false;
89     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
90     JSValue name = JS_GetPropertyStr(context, constructor, "name");
91     const char* cName = JS_ToCString(context, name);
92     result = !strcmp("External", cName ? cName : "");
93     JS_FreeCString(context, cName);
94     JS_FreeValue(context, name);
95     JS_FreeValue(context, constructor);
96     return result;
97 }
98 
AddIntrinsicBaseClass(JSContext * context)99 void AddIntrinsicBaseClass(JSContext* context)
100 {
101     const JSClassDef baseClassDef = {
102         .class_name = "BaseClass",
103         .finalizer =
104             [](JSRuntime* rt, JSValue val) {
105                 auto* info = reinterpret_cast<JSObjectInfo*>(JS_GetOpaque(val, GetBaseClassID()));
106                 if (info != nullptr) {
107                     info->finalizer(info->context, info->data, info->hint);
108                     delete info;
109                 }
110             },
111     };
112 
113     JS_NewClassID(&g_baseClassId);
114     JS_NewClass(JS_GetRuntime(context), g_baseClassId, &baseClassDef);
115 }
116 
JS_AddFinalizer(JSContext * context,JSValue value,void * pointer,JSFinalizer finalizer,void * hint)117 void JS_AddFinalizer(JSContext* context, JSValue value, void* pointer, JSFinalizer finalizer, void* hint)
118 {
119     auto* info = reinterpret_cast<JSObjectInfo*>(JS_GetOpaque(value, GetBaseClassID()));
120     if (info == nullptr) {
121         info = new JSObjectInfo();
122         if (info != nullptr) {
123             info->context = context;
124             info->finalizer = finalizer;
125             info->data = pointer;
126             info->hint = hint;
127         }
128     }
129 
130     if (info) {
131         JS_SetOpaque(value, info);
132     }
133 }
134 
JS_FreeFinalizer(JSValue value)135 void JS_FreeFinalizer(JSValue value)
136 {
137     auto* info = reinterpret_cast<JSObjectInfo*>(JS_GetOpaque(value, GetBaseClassID()));
138     if (info != nullptr) {
139         delete info;
140         info = nullptr;
141         JS_SetOpaque(value, info);
142     }
143 }
144 
JS_SetNativePointer(JSContext * context,JSValue value,void * pointer,JSFinalizer finalizer,void * hint)145 void JS_SetNativePointer(JSContext* context, JSValue value, void* pointer, JSFinalizer finalizer, void* hint)
146 {
147     auto* info = reinterpret_cast<JSObjectInfo*>(JS_GetOpaque(value, GetBaseClassID()));
148     if (info == nullptr) {
149         info = new JSObjectInfo();
150         if (info != nullptr) {
151             info->context = context;
152             info->finalizer = finalizer;
153             info->data = pointer;
154             info->hint = hint;
155         }
156     } else if (pointer == nullptr) {
157         delete info;
158         info = nullptr;
159     }
160 
161     JS_SetOpaque(value, info);
162 }
163 
JS_GetNativePointer(JSContext * context,JSValue value)164 void* JS_GetNativePointer(JSContext* context, JSValue value)
165 {
166     auto* info = reinterpret_cast<JSObjectInfo*>(JS_GetOpaque(value, GetBaseClassID()));
167     return (info != nullptr) ? info->data : nullptr;
168 }
169 
JS_IsPromise(JSContext * context,JSValue value)170 bool JS_IsPromise(JSContext* context, JSValue value)
171 {
172     bool result = false;
173     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
174     JSValue name = JS_GetPropertyStr(context, constructor, "name");
175     const char* cName = JS_ToCString(context, name);
176     result = !strcmp("Promise", cName ? cName : "");
177     JS_FreeCString(context, cName);
178     JS_FreeValue(context, name);
179     JS_FreeValue(context, constructor);
180     return result;
181 }
182 
JS_IsMapIterator(JSContext * context,JSValue value)183 bool JS_IsMapIterator(JSContext* context, JSValue value)
184 {
185     return false;
186 }
187 
JS_IsSetIterator(JSContext * context,JSValue value)188 bool JS_IsSetIterator(JSContext* context, JSValue value)
189 {
190     return false;
191 }
192 
JS_IsGeneratorObject(JSContext * context,JSValue value)193 bool JS_IsGeneratorObject(JSContext* context, JSValue value)
194 {
195     return false;
196 }
197 
JS_IsModuleNamespaceObject(JSContext * context,JSValue value)198 bool JS_IsModuleNamespaceObject(JSContext* context, JSValue value)
199 {
200     return false;
201 }
202 
JS_IsProxy(JSContext * context,JSValue value)203 bool JS_IsProxy(JSContext* context, JSValue value)
204 {
205     return false;
206 }
207 
JS_IsRegExp(JSContext * context,JSValue value)208 bool JS_IsRegExp(JSContext* context, JSValue value)
209 {
210     return false;
211 }
212 
JS_IsNumberObject(JSContext * context,JSValue value)213 bool JS_IsNumberObject(JSContext* context, JSValue value)
214 {
215     return false;
216 }
217 
JS_IsMap(JSContext * context,JSValue value)218 bool JS_IsMap(JSContext* context, JSValue value)
219 {
220     return false;
221 }
222 
JS_IsSet(JSContext * context,JSValue value)223 bool JS_IsSet(JSContext* context, JSValue value)
224 {
225     return false;
226 }
227 
JS_IsKeyObject(JSContext * context,JSValue value)228 bool JS_IsKeyObject(JSContext* context, JSValue value)
229 {
230     return false;
231 }
232 
JS_IsBigInt64Array(JSContext * context,JSValue value)233 bool JS_IsBigInt64Array(JSContext* context, JSValue value)
234 {
235     return false;
236 }
237 
JS_IsBigUint64Array(JSContext * context,JSValue value)238 bool JS_IsBigUint64Array(JSContext* context, JSValue value)
239 {
240     return false;
241 }
JS_IsSharedArrayBuffer(JSContext * context,JSValue value)242 bool JS_IsSharedArrayBuffer(JSContext* context, JSValue value)
243 {
244     return false;
245 }
246 
JS_IsArrayBuffer(JSContext * context,JSValue value)247 bool JS_IsArrayBuffer(JSContext* context, JSValue value)
248 {
249     bool result = false;
250     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
251     JSValue name = JS_GetPropertyStr(context, constructor, "name");
252     const char* cName = JS_ToCString(context, name);
253     result = !strcmp("ArrayBuffer", cName ? cName : "");
254     JS_FreeCString(context, cName);
255     JS_FreeValue(context, name);
256     JS_FreeValue(context, constructor);
257 
258     JSAtom key = JS_NewAtom(context, "napi_buffer");
259     bool hasPro = JS_HasProperty(context, value, key);
260     JS_FreeAtom(context, key);
261     return (result && !hasPro);
262 }
263 
JS_IsBuffer(JSContext * context,JSValue value)264 bool JS_IsBuffer(JSContext* context, JSValue value)
265 {
266     bool result = false;
267     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
268     JSValue name = JS_GetPropertyStr(context, constructor, "name");
269     const char* cName = JS_ToCString(context, name);
270     result = !strcmp("ArrayBuffer", cName ? cName : "");
271     JS_FreeValue(context, name);
272     JS_FreeCString(context, cName);
273     JS_FreeValue(context, constructor);
274 
275     JSAtom key = JS_NewAtom(context, "napi_buffer");
276     bool hasPro = JS_HasProperty(context, value, key);
277     JS_FreeAtom(context, key);
278 
279     return (result && hasPro);
280 }
281 
JS_IsDate(JSContext * context,JSValue value)282 bool JS_IsDate(JSContext* context, JSValue value)
283 {
284     bool result = false;
285     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
286     JSValue name = JS_GetPropertyStr(context, constructor, "name");
287     const char* cName = JS_ToCString(context, name);
288     result = !strcmp("Date", cName ? cName : "");
289     JS_FreeCString(context, cName);
290     JS_FreeValue(context, name);
291     JS_FreeValue(context, constructor);
292     return result;
293 }
294 
JS_IsDataView(JSContext * context,JSValue value)295 bool JS_IsDataView(JSContext* context, JSValue value)
296 {
297     bool result = false;
298     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
299     JSValue name = JS_GetPropertyStr(context, constructor, "name");
300     const char* cName = JS_ToCString(context, name);
301     result = !strcmp("DataView", cName ? cName : "");
302     JS_FreeCString(context, cName);
303     JS_FreeValue(context, name);
304     JS_FreeValue(context, constructor);
305     return result;
306 }
307 
JS_IsTypedArray(JSContext * context,JSValue value)308 bool JS_IsTypedArray(JSContext* context, JSValue value)
309 {
310     bool result = false;
311     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
312     JSValue name = JS_GetPropertyStr(context, constructor, "name");
313     const char* cName = JS_ToCString(context, name);
314     result = !strcmp("Uint8ClampedArray", cName ? cName : "") || !strcmp("Int8Array", cName ? cName : "") ||
315              !strcmp("Uint8Array", cName ? cName : "") || !strcmp("Int16Array", cName ? cName : "") ||
316              !strcmp("Uint16Array", cName ? cName : "") || !strcmp("Int32Array", cName ? cName : "") ||
317              !strcmp("Uint32Array", cName ? cName : "") || !strcmp("BigInt64Array", cName ? cName : "") ||
318              !strcmp("BigUint64Array", cName ? cName : "") || !strcmp("Float32Array", cName ? cName : "") ||
319              !strcmp("Float64Array", cName ? cName : "");
320 
321     JS_FreeCString(context, cName);
322     JS_FreeValue(context, name);
323     JS_FreeValue(context, constructor);
324     return result;
325 }
326 
JS_StrictEquals(JSContext * context,JSValue v1,JSValue v2)327 bool JS_StrictEquals(JSContext* context, JSValue v1, JSValue v2)
328 {
329     JSValue thisVar = JS_UNDEFINED;
330     JSValue argv[2] = { v1, v2 };
331     const char script[] = "(v1, v2) => v1 === v2;";
332     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
333     JSValue ret = JS_Call(context, func, thisVar, 2, (JSValue*)&argv);
334     bool result = JS_ToBool(context, ret);
335     JS_FreeValue(context, func);
336     JS_FreeValue(context, ret);
337     js_std_loop(context);
338 
339     return result;
340 }
341 
JS_StrictDate(JSContext * context,double time)342 JSValue JS_StrictDate(JSContext* context, double time)
343 {
344     JSValue thisVar = JS_UNDEFINED;
345     JSValue v1 = JS_NewInt64(context, (int64_t)time);
346     if (JS_IsException(v1)) {
347         JS_FreeValue(context, v1);
348         return JS_GetException(context);
349     }
350 
351     JSValue argv[1] = { v1 };
352     const char script[] = " (v1) => new Date(v1); ";
353     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
354     JSValue ret = JS_Call(context, func, thisVar, 1, (JSValue*)&argv);
355     JS_FreeValue(context, func);
356 
357     if (JS_IsException(ret)) {
358         JS_FreeValue(context, ret);
359         return JS_GetException(context);
360     }
361     return ret;
362 }
363 
JS_CreateBigIntWords(JSContext * context,int signBit,size_t wordCount,const uint64_t * words)364 JSValue JS_CreateBigIntWords(JSContext* context, int signBit, size_t wordCount, const uint64_t* words)
365 {
366     JSValue thisVar = JS_UNDEFINED;
367     constexpr size_t Count = 20;
368     constexpr size_t Two = 2;
369     if (wordCount <= 0 || wordCount > Count || words == nullptr) {
370         HILOG_INFO("%{public}s called. Params is invalid or the BigInt too big.", __func__);
371         return JS_EXCEPTION;
372     }
373 
374     JSValue signValue = JS_NewInt32(context, (signBit % Two));
375     if (JS_IsException(signValue)) {
376         HILOG_INFO("%{public}s called. Invoke JS_NewInt32 error.", __func__);
377         return JS_EXCEPTION;
378     }
379 
380     JSValue wordsValue = JS_NewArray(context);
381     if (JS_IsException(wordsValue)) {
382         HILOG_INFO("%{public}s called. Invoke JS_NewArray error.", __func__);
383         return JS_EXCEPTION;
384     }
385 
386     for (size_t i = 0; i < wordCount; i++) {
387         // shift 32 bits right to get high bit
388         JSValue idxValueHigh = JS_NewUint32(context, (uint32_t)(words[i] >> 32));
389         // gets lower 32 bits
390         JSValue idxValueLow = JS_NewUint32(context, (uint32_t)(words[i] & 0xFFFFFFFF));
391         if (!(JS_IsException(idxValueHigh)) && !(JS_IsException(idxValueLow))) {
392             JS_SetPropertyUint32(context, wordsValue, (i * Two), idxValueHigh);
393             JS_SetPropertyUint32(context, wordsValue, (i * Two + 1), idxValueLow);
394             JS_FreeValue(context, idxValueHigh);
395             JS_FreeValue(context, idxValueLow);
396         }
397     }
398 
399     JSValue argv[2] = { signValue, wordsValue };
400     const char script[] = "(sign, word) => { "
401                           " const max_v = BigInt(2 ** 64 - 1);"
402                           " var bg = 0n;"
403                           "  for (var i=0; i<word.length/2; i++) {"
404                           "      bg = bg + (BigInt(word[i*2]) * 2n**32n + BigInt(word[i*2 +1])) * (max_v ** BigInt(i));"
405                           "  }"
406                           "  if (sign  !=  0) {"
407                           "      bg = bg * (-1n);"
408                           "  }"
409                           "  return bg;"
410                           "};";
411 
412     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
413     JSValue ret = JS_Call(context, func, thisVar, 2, (JSValue*)&argv);
414     JS_FreeValue(context, func);
415     JS_FreeValue(context, signValue);
416     JS_FreeValue(context, wordsValue);
417     return ret;
418 }
419 
ParseBigIntWordsInternal(JSContext * context,JSValue value,int * signBit,size_t * wordCount,uint64_t * words)420 bool ParseBigIntWordsInternal(JSContext* context, JSValue value, int* signBit, size_t* wordCount, uint64_t* words)
421 {
422     int cntValue = 0;
423     if (wordCount == nullptr) {
424         return false;
425     }
426 
427     JSValue jsValue = JS_GetPropertyStr(context, value, "count");
428     if (!JS_IsException(jsValue)) {
429         JS_ToInt32(context, &cntValue, jsValue);
430         JS_FreeValue(context, jsValue);
431     } else {
432         return false;
433     }
434 
435     if (signBit == nullptr && words == nullptr) {
436         *wordCount = cntValue;
437         return true;
438     } else if (signBit != nullptr && words != nullptr) {
439         cntValue = (cntValue > static_cast<int>(*wordCount)) ? *wordCount : cntValue;
440         jsValue = JS_GetPropertyStr(context, value, "sign");
441         if (!JS_IsException(jsValue)) {
442             int sigValue = 0;
443             JS_ToInt32(context, &sigValue, jsValue);
444             *signBit = sigValue;
445             JS_FreeValue(context, jsValue);
446         }
447 
448         jsValue = JS_GetPropertyStr(context, value, "words");
449         if (!JS_IsException(jsValue)) {
450             JSValue element;
451             int64_t cValue = 0;
452             for (uint32_t i = 0; i < (uint32_t)cntValue; i++) {
453                 element = JS_GetPropertyUint32(context, jsValue, i);
454                 JS_ToInt64Ext(context, &cValue, element);
455                 JS_FreeValue(context, element);
456                 words[i] = (uint64_t)cValue;
457             }
458             JS_FreeValue(context, jsValue);
459             *wordCount = cntValue;
460             return true;
461         }
462     }
463     return false;
464 }
465 
JS_GetBigIntWords(JSContext * context,JSValue value,int * signBit,size_t * wordCount,uint64_t * words)466 bool JS_GetBigIntWords(JSContext* context, JSValue value, int* signBit, size_t* wordCount, uint64_t* words)
467 {
468     HILOG_INFO("%{public}s called. ", __func__);
469 
470     bool rev = false;
471     JSValue thisVar = JS_UNDEFINED;
472     if (wordCount == nullptr) {
473         HILOG_INFO("%{public}s called. Params are invalid.", __func__);
474         return false;
475     }
476 
477     const char script[] = "(big) => {"
478                           "const max_v = BigInt(2 ** 64 - 1);"
479                           "var rev = {};"
480                           "rev.sign = 0;"
481                           "rev.count = 0;"
482                           "rev.words = [];"
483                           "if (big < 0n) {"
484                           "	rev.sign = 1;"
485                           "	big = big * (-1n);"
486                           "}"
487                           "while (big >= max_v) {"
488                           "	rev.words[rev.count] = big % max_v;"
489                           "	big = big / max_v;"
490                           "	rev.count++;"
491                           "}"
492                           "rev.words[rev.count] = big % max_v;"
493                           "rev.count++;"
494                           "return rev;"
495                           "}";
496 
497     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
498     JSValue bigObj = JS_Call(context, func, thisVar, 1, &value);
499     if (!JS_IsException(bigObj)) {
500         if (JS_IsObject(bigObj)) {
501             rev = ParseBigIntWordsInternal(context, bigObj, signBit, wordCount, words);
502         } else {
503             HILOG_INFO("%{public}s called. JSValue is not Object. ", __func__);
504         }
505     }
506 
507     JS_FreeValue(context, func);
508     JS_FreeValue(context, bigObj);
509     return rev;
510 }
511 
JS_Freeze(JSContext * context,JSValue value)512 JSValue JS_Freeze(JSContext* context, JSValue value)
513 {
514     JSValue thisVar = JS_UNDEFINED;
515     const char script[] = "(obj) => Object.freeze(obj);";
516     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
517     JSValue ret = JS_Call(context, func, thisVar, 1, &value);
518     JS_FreeValue(context, func);
519     JS_FreeValue(context, ret);
520     return JS_DupValue(context, value);
521 }
522 
JS_Seal(JSContext * context,JSValue value)523 JSValue JS_Seal(JSContext* context, JSValue value)
524 {
525     JSValue thisVar = JS_UNDEFINED;
526     const char script[] = "(obj) => Object.seal(obj);";
527     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
528     JSValue ret = JS_Call(context, func, thisVar, 1, &value);
529     JS_FreeValue(context, func);
530     JS_FreeValue(context, ret);
531     return JS_DupValue(context, value);
532 }
533 
534 struct JS_BigFloatExt {
535     JSRefCountHeader header;
536     bf_t num;
537 };
538 
JS_ToInt64WithBigInt(JSContext * context,JSValueConst value,int64_t * pres,bool * lossless)539 bool JS_ToInt64WithBigInt(JSContext* context, JSValueConst value, int64_t* pres, bool* lossless)
540 {
541     if (pres == nullptr || lossless == nullptr) {
542         HILOG_INFO("%{public}s called. Params are invalid.", __func__);
543         return false;
544     }
545 
546     bool rev = false;
547     JSValue val = JS_DupValue(context, value);
548     JS_BigFloatExt* p = (JS_BigFloatExt*)JS_VALUE_GET_PTR(val);
549     if (p) {
550         int opFlag = bf_get_int64(pres, &p->num, 0);
551         if (lossless != nullptr) {
552             *lossless = (opFlag == 0);
553         }
554         rev = true;
555     }
556     JS_FreeValue(context, val);
557     return rev;
558 }
559 
JS_ToUInt64WithBigInt(JSContext * context,JSValueConst value,uint64_t * pres,bool * lossless)560 bool JS_ToUInt64WithBigInt(JSContext* context, JSValueConst value, uint64_t* pres, bool* lossless)
561 {
562     if (pres == nullptr || lossless == nullptr) {
563         HILOG_INFO("%{public}s called. Params are invalid.", __func__);
564         return false;
565     }
566 
567     bool rev = false;
568     JSValue val = JS_DupValue(context, value);
569     JS_BigFloatExt* p = (JS_BigFloatExt*)JS_VALUE_GET_PTR(val);
570     if (p) {
571         int opFlag = bf_get_uint64(pres, &p->num);
572         if (lossless != nullptr) {
573             *lossless = (opFlag == 0);
574         }
575         rev = true;
576     }
577     JS_FreeValue(context, val);
578     return rev;
579 }
580