• 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_IsArrayBuffer(JSContext * context,JSValue value)233 bool JS_IsArrayBuffer(JSContext* context, JSValue value)
234 {
235     bool result = false;
236     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
237     JSValue name = JS_GetPropertyStr(context, constructor, "name");
238     const char* cName = JS_ToCString(context, name);
239     result = !strcmp("ArrayBuffer", cName ? cName : "");
240     JS_FreeCString(context, cName);
241     JS_FreeValue(context, name);
242     JS_FreeValue(context, constructor);
243 
244     JSAtom key = JS_NewAtom(context, "napi_buffer");
245     bool hasPro = JS_HasProperty(context, value, key);
246     JS_FreeAtom(context, key);
247     return (result && !hasPro);
248 }
249 
JS_IsBuffer(JSContext * context,JSValue value)250 bool JS_IsBuffer(JSContext* context, JSValue value)
251 {
252     bool result = false;
253     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
254     JSValue name = JS_GetPropertyStr(context, constructor, "name");
255     const char* cName = JS_ToCString(context, name);
256     result = !strcmp("ArrayBuffer", cName ? cName : "");
257     JS_FreeValue(context, name);
258     JS_FreeCString(context, cName);
259     JS_FreeValue(context, constructor);
260 
261     JSAtom key = JS_NewAtom(context, "napi_buffer");
262     bool hasPro = JS_HasProperty(context, value, key);
263     JS_FreeAtom(context, key);
264 
265     return (result && hasPro);
266 }
267 
JS_IsDate(JSContext * context,JSValue value)268 bool JS_IsDate(JSContext* context, JSValue value)
269 {
270     bool result = false;
271     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
272     JSValue name = JS_GetPropertyStr(context, constructor, "name");
273     const char* cName = JS_ToCString(context, name);
274     result = !strcmp("Date", cName ? cName : "");
275     JS_FreeCString(context, cName);
276     JS_FreeValue(context, name);
277     JS_FreeValue(context, constructor);
278     return result;
279 }
280 
JS_IsDataView(JSContext * context,JSValue value)281 bool JS_IsDataView(JSContext* context, JSValue value)
282 {
283     bool result = false;
284     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
285     JSValue name = JS_GetPropertyStr(context, constructor, "name");
286     const char* cName = JS_ToCString(context, name);
287     result = !strcmp("DataView", cName ? cName : "");
288     JS_FreeCString(context, cName);
289     JS_FreeValue(context, name);
290     JS_FreeValue(context, constructor);
291     return result;
292 }
293 
JS_IsTypedArray(JSContext * context,JSValue value)294 bool JS_IsTypedArray(JSContext* context, JSValue value)
295 {
296     bool result = false;
297     JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
298     JSValue name = JS_GetPropertyStr(context, constructor, "name");
299     const char* cName = JS_ToCString(context, name);
300     result = !strcmp("Uint8ClampedArray", cName ? cName : "") || !strcmp("Int8Array", cName ? cName : "") ||
301              !strcmp("Uint8Array", cName ? cName : "") || !strcmp("Int16Array", cName ? cName : "") ||
302              !strcmp("Uint16Array", cName ? cName : "") || !strcmp("Int32Array", cName ? cName : "") ||
303              !strcmp("Uint32Array", cName ? cName : "") || !strcmp("BigInt64Array", cName ? cName : "") ||
304              !strcmp("BigUint64Array", cName ? cName : "") || !strcmp("Float32Array", cName ? cName : "") ||
305              !strcmp("Float64Array", cName ? cName : "");
306 
307     JS_FreeCString(context, cName);
308     JS_FreeValue(context, name);
309     JS_FreeValue(context, constructor);
310     return result;
311 }
312 
JS_StrictEquals(JSContext * context,JSValue v1,JSValue v2)313 bool JS_StrictEquals(JSContext* context, JSValue v1, JSValue v2)
314 {
315     JSValue thisVar = JS_UNDEFINED;
316     JSValue argv[2] = { v1, v2 };
317     const char script[] = "(v1, v2) => v1 === v2;";
318     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
319     JSValue ret = JS_Call(context, func, thisVar, 2, (JSValue*)&argv);
320     bool result = JS_ToBool(context, ret);
321     JS_FreeValue(context, func);
322     JS_FreeValue(context, ret);
323     js_std_loop(context);
324 
325     return result;
326 }
327 
JS_StrictDate(JSContext * context,double time)328 JSValue JS_StrictDate(JSContext* context, double time)
329 {
330     JSValue thisVar = JS_UNDEFINED;
331     JSValue v1 = JS_NewInt64(context, (int64_t)time);
332     if (JS_IsException(v1)) {
333         JS_FreeValue(context, v1);
334         return JS_GetException(context);
335     }
336 
337     JSValue argv[1] = { v1 };
338     const char script[] = " (v1) => new Date(v1); ";
339     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
340     JSValue ret = JS_Call(context, func, thisVar, 1, (JSValue*)&argv);
341     JS_FreeValue(context, func);
342 
343     if (JS_IsException(ret)) {
344         JS_FreeValue(context, ret);
345         return JS_GetException(context);
346     }
347     return ret;
348 }
349 
JS_CreateBigIntWords(JSContext * context,int signBit,size_t wordCount,const uint64_t * words)350 JSValue JS_CreateBigIntWords(JSContext* context, int signBit, size_t wordCount, const uint64_t* words)
351 {
352     JSValue thisVar = JS_UNDEFINED;
353     constexpr size_t Count = 20;
354     constexpr size_t Two = 2;
355     if (wordCount <= 0 || wordCount > Count || words == nullptr) {
356         HILOG_INFO("%{public}s called. Params is invalid or the BigInt too big.", __func__);
357         return JS_EXCEPTION;
358     }
359 
360     JSValue signValue = JS_NewInt32(context, (signBit % Two));
361     if (JS_IsException(signValue)) {
362         HILOG_INFO("%{public}s called. Invoke JS_NewInt32 error.", __func__);
363         return JS_EXCEPTION;
364     }
365 
366     JSValue wordsValue = JS_NewArray(context);
367     if (JS_IsException(wordsValue)) {
368         HILOG_INFO("%{public}s called. Invoke JS_NewArray error.", __func__);
369         return JS_EXCEPTION;
370     }
371 
372     for (size_t i = 0; i < wordCount; i++) {
373         // shift 32 bits right to get high bit
374         JSValue idxValueHigh = JS_NewUint32(context, (uint32_t)(words[i] >> 32));
375         // gets lower 32 bits
376         JSValue idxValueLow = JS_NewUint32(context, (uint32_t)(words[i] & 0xFFFFFFFF));
377         if (!(JS_IsException(idxValueHigh)) && !(JS_IsException(idxValueLow))) {
378             JS_SetPropertyUint32(context, wordsValue, (i * Two), idxValueHigh);
379             JS_SetPropertyUint32(context, wordsValue, (i * Two + 1), idxValueLow);
380             JS_FreeValue(context, idxValueHigh);
381             JS_FreeValue(context, idxValueLow);
382         }
383     }
384 
385     JSValue argv[2] = { signValue, wordsValue };
386     const char script[] = "(sign, word) => { "
387                           " const max_v = BigInt(2 ** 64 - 1);"
388                           " var bg = 0n;"
389                           "  for (var i=0; i<word.length/2; i++) {"
390                           "      bg = bg + (BigInt(word[i*2]) * 2n**32n + BigInt(word[i*2 +1])) * (max_v ** BigInt(i));"
391                           "  }"
392                           "  if (sign  !=  0) {"
393                           "      bg = bg * (-1n);"
394                           "  }"
395                           "  return bg;"
396                           "};";
397 
398     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
399     JSValue ret = JS_Call(context, func, thisVar, 2, (JSValue*)&argv);
400     JS_FreeValue(context, func);
401     JS_FreeValue(context, signValue);
402     JS_FreeValue(context, wordsValue);
403     return ret;
404 }
405 
ParseBigIntWordsInternal(JSContext * context,JSValue value,int * signBit,size_t * wordCount,uint64_t * words)406 bool ParseBigIntWordsInternal(JSContext* context, JSValue value, int* signBit, size_t* wordCount, uint64_t* words)
407 {
408     int cntValue = 0;
409     if (wordCount == nullptr) {
410         return false;
411     }
412 
413     JSValue jsValue = JS_GetPropertyStr(context, value, "count");
414     if (!JS_IsException(jsValue)) {
415         JS_ToInt32(context, &cntValue, jsValue);
416         JS_FreeValue(context, jsValue);
417     } else {
418         return false;
419     }
420 
421     if (signBit == nullptr && words == nullptr) {
422         *wordCount = cntValue;
423         return true;
424     } else if (signBit != nullptr && words != nullptr) {
425         cntValue = (cntValue > *wordCount) ? *wordCount : cntValue;
426         jsValue = JS_GetPropertyStr(context, value, "sign");
427         if (!JS_IsException(jsValue)) {
428             int sigValue = 0;
429             JS_ToInt32(context, &sigValue, jsValue);
430             *signBit = sigValue;
431             JS_FreeValue(context, jsValue);
432         }
433 
434         jsValue = JS_GetPropertyStr(context, value, "words");
435         if (!JS_IsException(jsValue)) {
436             JSValue element;
437             int64_t cValue = 0;
438             for (uint32_t i = 0; i < (uint32_t)cntValue; i++) {
439                 element = JS_GetPropertyUint32(context, jsValue, i);
440                 JS_ToInt64Ext(context, &cValue, element);
441                 JS_FreeValue(context, element);
442                 words[i] = (uint64_t)cValue;
443             }
444             JS_FreeValue(context, jsValue);
445             *wordCount = cntValue;
446             return true;
447         }
448     }
449     return false;
450 }
451 
JS_GetBigIntWords(JSContext * context,JSValue value,int * signBit,size_t * wordCount,uint64_t * words)452 bool JS_GetBigIntWords(JSContext* context, JSValue value, int* signBit, size_t* wordCount, uint64_t* words)
453 {
454     HILOG_INFO("%{public}s called. ", __func__);
455 
456     bool rev = false;
457     JSValue thisVar = JS_UNDEFINED;
458     if (wordCount == nullptr) {
459         HILOG_INFO("%{public}s called. Params are invalid.", __func__);
460         return false;
461     }
462 
463     const char script[] = "(big) => {"
464                           "const max_v = BigInt(2 ** 64 - 1);"
465                           "var rev = {};"
466                           "rev.sign = 0;"
467                           "rev.count = 0;"
468                           "rev.words = [];"
469                           "if (big < 0n) {"
470                           "	rev.sign = 1;"
471                           "	big = big * (-1n);"
472                           "}"
473                           "while (big >= max_v) {"
474                           "	rev.words[rev.count] = big % max_v;"
475                           "	big = big / max_v;"
476                           "	rev.count++;"
477                           "}"
478                           "rev.words[rev.count] = big % max_v;"
479                           "rev.count++;"
480                           "return rev;"
481                           "}";
482 
483     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
484     JSValue bigObj = JS_Call(context, func, thisVar, 1, &value);
485     if (!JS_IsException(bigObj)) {
486         if (JS_IsObject(bigObj)) {
487             rev = ParseBigIntWordsInternal(context, bigObj, signBit, wordCount, words);
488         } else {
489             HILOG_INFO("%{public}s called. JSValue is not Object. ", __func__);
490         }
491     }
492 
493     JS_FreeValue(context, func);
494     JS_FreeValue(context, bigObj);
495     return rev;
496 }
497 
JS_Freeze(JSContext * context,JSValue value)498 JSValue JS_Freeze(JSContext* context, JSValue value)
499 {
500     JSValue thisVar = JS_UNDEFINED;
501     const char script[] = "(obj) => Object.freeze(obj);";
502     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
503     JSValue ret = JS_Call(context, func, thisVar, 1, &value);
504     JS_FreeValue(context, func);
505     JS_FreeValue(context, ret);
506     return JS_DupValue(context, value);
507 }
508 
JS_Seal(JSContext * context,JSValue value)509 JSValue JS_Seal(JSContext* context, JSValue value)
510 {
511     JSValue thisVar = JS_UNDEFINED;
512     const char script[] = "(obj) => Object.seal(obj);";
513     JSValue func = JS_Eval(context, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);
514     JSValue ret = JS_Call(context, func, thisVar, 1, &value);
515     JS_FreeValue(context, func);
516     JS_FreeValue(context, ret);
517     return JS_DupValue(context, value);
518 }
519 
520 struct JS_BigFloatExt {
521     JSRefCountHeader header;
522     bf_t num;
523 };
524 
JS_ToInt64WithBigInt(JSContext * context,JSValueConst value,int64_t * pres,bool * lossless)525 bool JS_ToInt64WithBigInt(JSContext* context, JSValueConst value, int64_t* pres, bool* lossless)
526 {
527     if (pres == nullptr || lossless == nullptr) {
528         HILOG_INFO("%{public}s called. Params are invalid.", __func__);
529         return false;
530     }
531 
532     bool rev = false;
533     JSValue val = JS_DupValue(context, value);
534     JS_BigFloatExt* p = (JS_BigFloatExt*)JS_VALUE_GET_PTR(val);
535     if (p) {
536         int opFlag = bf_get_int64(pres, &p->num, BF_GET_INT_MOD);
537         if (lossless != nullptr) {
538             *lossless = (opFlag == 0);
539         }
540         rev = true;
541     }
542     JS_FreeValue(context, val);
543     return rev;
544 }
545 
JS_ToUInt64WithBigInt(JSContext * context,JSValueConst value,uint64_t * pres,bool * lossless)546 bool JS_ToUInt64WithBigInt(JSContext* context, JSValueConst value, uint64_t* pres, bool* lossless)
547 {
548     if (pres == nullptr || lossless == nullptr) {
549         HILOG_INFO("%{public}s called. Params are invalid.", __func__);
550         return false;
551     }
552 
553     bool rev = false;
554     JSValue val = JS_DupValue(context, value);
555     JS_BigFloatExt* p = (JS_BigFloatExt*)JS_VALUE_GET_PTR(val);
556     if (p) {
557         int opFlag = bf_get_uint64(pres, &p->num);
558         if (lossless != nullptr) {
559             *lossless = (opFlag == 0);
560         }
561         rev = true;
562     }
563     JS_FreeValue(context, val);
564     return rev;
565 }