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 "quickjs_native_object.h"
17 #include "native_engine/native_engine.h"
18 #include "native_engine/native_property.h"
19 #include "quickjs_headers.h"
20 #include "quickjs_native_array.h"
21 #include "quickjs_native_big_int.h"
22 #include "quickjs_native_engine.h"
23 #include "quickjs_native_function.h"
24 #include "quickjs_native_string.h"
25 #include "utils/log.h"
26
QuickJSNativeObject(QuickJSNativeEngine * engine)27 QuickJSNativeObject::QuickJSNativeObject(QuickJSNativeEngine* engine)
28 : QuickJSNativeObject(engine, JS_NewObject(engine->GetContext()))
29 {
30 }
31
QuickJSNativeObject(QuickJSNativeEngine * engine,JSValue value)32 QuickJSNativeObject::QuickJSNativeObject(QuickJSNativeEngine* engine, JSValue value) : QuickJSNativeValue(engine, value)
33 {
34 }
35
~QuickJSNativeObject()36 QuickJSNativeObject::~QuickJSNativeObject() {}
37
SetNativePointer(void * pointer,NativeFinalize cb,void * hint)38 void QuickJSNativeObject::SetNativePointer(void* pointer, NativeFinalize cb, void* hint)
39 {
40 NativeObjectInfo* info = (NativeObjectInfo*)JS_GetNativePointer(engine_->GetContext(), value_);
41 if (info == nullptr) {
42 info = new NativeObjectInfo();
43 if (info != nullptr) {
44 info->callback = cb;
45 info->engine = engine_;
46 info->nativeObject = pointer;
47 info->hint = hint;
48 }
49 JS_SetNativePointer(
50 engine_->GetContext(), value_, info,
51 [](JSContext* context, void* data, void* hint) {
52 auto info = reinterpret_cast<NativeObjectInfo*>(data);
53 if (info) {
54 info->callback(info->engine, info->nativeObject, info->hint);
55 delete info;
56 }
57 },
58 hint);
59 } else if (pointer == nullptr) {
60 JS_SetNativePointer(engine_->GetContext(), value_, nullptr, nullptr, nullptr);
61 delete info;
62 }
63 }
64
GetNativePointer()65 void* QuickJSNativeObject::GetNativePointer()
66 {
67 NativeObjectInfo* info = (NativeObjectInfo*)JS_GetNativePointer(engine_->GetContext(), value_);
68 return info ? info->nativeObject : nullptr;
69 }
70
AddFinalizer(void * pointer,NativeFinalize cb,void * hint)71 void QuickJSNativeObject::AddFinalizer(void* pointer, NativeFinalize cb, void* hint)
72 {
73 NativeObjectInfo* info = (NativeObjectInfo*)JS_GetNativePointer(engine_->GetContext(), value_);
74 if (info == nullptr) {
75 info = new NativeObjectInfo();
76 if (info != nullptr) {
77 info->callback = cb;
78 info->engine = engine_;
79 info->nativeObject = pointer;
80 info->hint = hint;
81 }
82 }
83 if (info == nullptr) {
84 return;
85 }
86
87 JS_AddFinalizer(
88 engine_->GetContext(), value_, info,
89 [](JSContext* context, void* data, void* hint) {
90 auto info = reinterpret_cast<NativeObjectInfo*>(data);
91 if (info) {
92 info->callback(info->engine, info->nativeObject, info->hint);
93 delete info;
94 }
95 },
96 hint);
97 }
98
GetInterface(int interfaceId)99 void* QuickJSNativeObject::GetInterface(int interfaceId)
100 {
101 return (NativeObject::INTERFACE_ID == interfaceId) ? (NativeObject*)this : nullptr;
102 }
103
GetPropertyNames()104 NativeValue* QuickJSNativeObject::GetPropertyNames()
105 {
106 JSPropertyEnum* tab = nullptr;
107 uint32_t len = 0;
108
109 JS_GetOwnPropertyNames(engine_->GetContext(), &tab, &len, value_, JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY);
110
111 QuickJSNativeArray* propertyNames = new QuickJSNativeArray(engine_, (uint32_t)0);
112 if (propertyNames == nullptr) {
113 HILOG_ERROR("create property names failed");
114 return nullptr;
115 }
116
117 for (uint32_t i = 0; i < len; i++) {
118 QuickJSNativeString* name = new QuickJSNativeString(engine_, tab[i].atom);
119 propertyNames->SetElement(i, name);
120 }
121
122 return propertyNames;
123 }
124
GetPrototype()125 NativeValue* QuickJSNativeObject::GetPrototype()
126 {
127 JSValue value = JS_GetPrototype(engine_->GetContext(), value_);
128 return new QuickJSNativeObject(engine_, value);
129 }
130
DefineProperty(NativePropertyDescriptor propertyDescriptor)131 bool QuickJSNativeObject::DefineProperty(NativePropertyDescriptor propertyDescriptor)
132 {
133 JSAtom jKey = JS_NewAtom(engine_->GetContext(), propertyDescriptor.utf8name);
134
135 bool result = false;
136
137 if (propertyDescriptor.value) {
138 result = JS_DefinePropertyValue(engine_->GetContext(), value_, jKey,
139 JS_DupValue(engine_->GetContext(), *propertyDescriptor.value), JS_PROP_C_W_E);
140 } else if (propertyDescriptor.method) {
141 NativeValue* function = new QuickJSNativeFunction(engine_, propertyDescriptor.utf8name,
142 propertyDescriptor.method, propertyDescriptor.data);
143 if (function != nullptr) {
144 result = JS_DefinePropertyValue(engine_->GetContext(), value_, jKey,
145 JS_DupValue(engine_->GetContext(), *function),
146 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
147 }
148 } else if (propertyDescriptor.getter || propertyDescriptor.setter) {
149 NativeValue* getter =
150 new QuickJSNativeFunction(engine_, nullptr, propertyDescriptor.getter, propertyDescriptor.data);
151 NativeValue* setter =
152 new QuickJSNativeFunction(engine_, nullptr, propertyDescriptor.setter, propertyDescriptor.data);
153 if (getter != nullptr && setter != nullptr) {
154 result = JS_DefinePropertyGetSet(engine_->GetContext(), value_, jKey,
155 JS_DupValue(engine_->GetContext(), *getter),
156 JS_DupValue(engine_->GetContext(), *setter),
157 JS_PROP_C_W_E);
158 }
159 }
160
161 JS_FreeAtom(engine_->GetContext(), jKey);
162
163 return result;
164 }
165
SetProperty(NativeValue * key,NativeValue * value)166 bool QuickJSNativeObject::SetProperty(NativeValue* key, NativeValue* value)
167 {
168 bool result = false;
169 JSAtom jKey = JS_ValueToAtom(engine_->GetContext(), *key);
170 result = JS_SetProperty(engine_->GetContext(), value_, jKey, JS_DupValue(engine_->GetContext(), *value));
171 JS_FreeAtom(engine_->GetContext(), jKey);
172
173 return result;
174 }
175
GetProperty(NativeValue * key)176 NativeValue* QuickJSNativeObject::GetProperty(NativeValue* key)
177 {
178 JSValue value = JS_UNDEFINED;
179 JSAtom atomKey = JS_ValueToAtom(engine_->GetContext(), *key);
180 value = JS_GetProperty(engine_->GetContext(), value_, atomKey);
181 JS_FreeAtom(engine_->GetContext(), atomKey);
182 return QuickJSNativeEngine::JSValueToNativeValue(engine_, value);
183 }
184
HasProperty(NativeValue * key)185 bool QuickJSNativeObject::HasProperty(NativeValue* key)
186 {
187 bool result = false;
188 JSAtom jKey = JS_ValueToAtom(engine_->GetContext(), *key);
189 result = JS_HasProperty(engine_->GetContext(), value_, jKey);
190 JS_FreeAtom(engine_->GetContext(), jKey);
191 return result;
192 }
193
DeleteProperty(NativeValue * key)194 bool QuickJSNativeObject::DeleteProperty(NativeValue* key)
195 {
196 bool result = false;
197 JSAtom jKey = JS_ValueToAtom(engine_->GetContext(), *key);
198 result = JS_DeleteProperty(engine_->GetContext(), value_, jKey, JS_PROP_THROW);
199 JS_FreeAtom(engine_->GetContext(), jKey);
200 return result;
201 }
202
SetProperty(const char * name,NativeValue * value)203 bool QuickJSNativeObject::SetProperty(const char* name, NativeValue* value)
204 {
205 return JS_SetPropertyStr(engine_->GetContext(), value_, name, JS_DupValue(engine_->GetContext(), *value));
206 }
207
GetProperty(const char * name)208 NativeValue* QuickJSNativeObject::GetProperty(const char* name)
209 {
210 JSValue value = JS_UNDEFINED;
211 value = JS_GetPropertyStr(engine_->GetContext(), value_, name);
212 return QuickJSNativeEngine::JSValueToNativeValue(engine_, value);
213 }
214
HasProperty(const char * name)215 bool QuickJSNativeObject::HasProperty(const char* name)
216 {
217 bool result = false;
218 JSAtom key = JS_NewAtom(engine_->GetContext(), name);
219 result = JS_HasProperty(engine_->GetContext(), value_, key);
220 JS_FreeAtom(engine_->GetContext(), key);
221 return result;
222 }
223
DeleteProperty(const char * name)224 bool QuickJSNativeObject::DeleteProperty(const char* name)
225 {
226 bool result = false;
227 JSAtom key = JS_NewAtom(engine_->GetContext(), name);
228 result = JS_DeleteProperty(engine_->GetContext(), value_, key, JS_PROP_THROW);
229 JS_FreeAtom(engine_->GetContext(), key);
230 return result;
231 }
232
SetPrivateProperty(const char * name,NativeValue * value)233 bool QuickJSNativeObject::SetPrivateProperty(const char* name, NativeValue* value)
234 {
235 bool result = false;
236 JSAtom key = JS_NewAtom(engine_->GetContext(), name);
237 result = JS_SetPropertyInternal(engine_->GetContext(), value_, key, JS_DupValue(engine_->GetContext(), *value),
238 JS_PROP_C_W_E | JS_PROP_THROW);
239 JS_FreeAtom(engine_->GetContext(), key);
240 return result;
241 }
242
GetPrivateProperty(const char * name)243 NativeValue* QuickJSNativeObject::GetPrivateProperty(const char* name)
244 {
245 JSValue result = JS_UNDEFINED;
246 JSAtom key = JS_NewAtom(engine_->GetContext(), name);
247 result = JS_GetPropertyInternal(engine_->GetContext(), value_, key, value_, false);
248 JS_FreeAtom(engine_->GetContext(), key);
249 return QuickJSNativeEngine::JSValueToNativeValue(engine_, result);
250 }
251
HasPrivateProperty(const char * name)252 bool QuickJSNativeObject::HasPrivateProperty(const char* name)
253 {
254 bool result = false;
255 JSAtom key = JS_NewAtom(engine_->GetContext(), name);
256 result = JS_HasProperty(engine_->GetContext(), value_, key);
257 JS_FreeAtom(engine_->GetContext(), key);
258 return result;
259 }
260
DeletePrivateProperty(const char * name)261 bool QuickJSNativeObject::DeletePrivateProperty(const char* name)
262 {
263 bool result = false;
264 JSAtom key = JS_NewAtom(engine_->GetContext(), name);
265 result = JS_DeleteProperty(engine_->GetContext(), value_, key, JS_PROP_C_W_E | JS_PROP_THROW);
266 JS_FreeAtom(engine_->GetContext(), key);
267 return result;
268 }
269
GetAllPropertyNames(napi_key_collection_mode keyMode,napi_key_filter keyFilter,napi_key_conversion keyConversion)270 NativeValue* QuickJSNativeObject::GetAllPropertyNames(
271 napi_key_collection_mode keyMode, napi_key_filter keyFilter, napi_key_conversion keyConversion)
272 {
273 auto getPower = JS_PROP_CONFIGURABLE;
274 if (keyFilter == napi_key_all_properties) {
275 getPower = getPower | JS_PROP_GETSET | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE;
276 } else {
277 if (keyFilter == napi_key_writable) {
278 getPower = getPower | JS_PROP_WRITABLE | JS_PROP_GETSET;
279 }
280
281 if (keyFilter == napi_key_enumerable) {
282 getPower = getPower | JS_PROP_ENUMERABLE;
283 }
284
285 if (keyFilter == napi_key_configurable) {
286 getPower = getPower | JS_PROP_CONFIGURABLE;
287 }
288 }
289
290 JSPropertyEnum* tab = nullptr;
291 uint32_t len = 0;
292
293 JS_GetOwnPropertyNames(engine_->GetContext(), &tab, &len, value_, getPower);
294
295 QuickJSNativeArray* propertyNames = new QuickJSNativeArray(engine_, len);
296 if (propertyNames == nullptr) {
297 HILOG_ERROR("create property names failed");
298 return nullptr;
299 }
300
301 for (uint32_t i = 0; i < len; i++) {
302 QuickJSNativeString* name = new QuickJSNativeString(engine_, tab[i].atom);
303 propertyNames->SetElement(i, name);
304 }
305
306 return propertyNames;
307 }
308
AssociateTypeTag(NapiTypeTag * typeTag)309 bool QuickJSNativeObject::AssociateTypeTag(NapiTypeTag* typeTag)
310 {
311 const char name_lower[] = "ACENAPI_LOWER";
312 const char name_upper[] = "ACENAPI_UPPER";
313 bool result = false;
314 bool hasPribate = false;
315 hasPribate = HasPrivateProperty(name_lower);
316 if (!hasPribate) {
317 JSValue valueLower = JS_NewInt64(engine_->GetContext(), (uint64_t)(typeTag->lower));
318 JSValue valueUpper = JS_NewInt64(engine_->GetContext(), (uint64_t)(typeTag->upper));
319
320 JSAtom keyLower = JS_NewAtom(engine_->GetContext(), name_lower);
321 result = JS_SetPropertyInternal(engine_->GetContext(), value_, keyLower,
322 JS_DupValue(engine_->GetContext(), valueLower), JS_PROP_C_W_E | JS_PROP_THROW);
323 JS_FreeAtom(engine_->GetContext(), keyLower);
324
325 JSAtom keyUpper = JS_NewAtom(engine_->GetContext(), name_upper);
326 result = result && JS_SetPropertyInternal(engine_->GetContext(), value_, keyUpper,
327 JS_DupValue(engine_->GetContext(), valueUpper), JS_PROP_C_W_E | JS_PROP_THROW);
328 JS_FreeAtom(engine_->GetContext(), keyUpper);
329
330 JS_FreeValue(engine_->GetContext(), valueLower);
331 JS_FreeValue(engine_->GetContext(), valueUpper);
332 }
333 return result;
334 }
335
CheckTypeTag(NapiTypeTag * typeTag)336 bool QuickJSNativeObject::CheckTypeTag(NapiTypeTag* typeTag)
337 {
338 const char name_lower[] = "ACENAPI_LOWER";
339 const char name_upper[] = "ACENAPI_UPPER";
340 bool result = false;
341 result = HasPrivateProperty(name_lower);
342 if (result) {
343 JSValue valueLower = JS_UNDEFINED;
344 JSAtom key = JS_NewAtom(engine_->GetContext(), name_lower);
345 valueLower = JS_GetPropertyInternal(engine_->GetContext(), value_, key, value_, false);
346 JS_FreeAtom(engine_->GetContext(), key);
347
348 JSValue valueUpper = JS_UNDEFINED;
349 key = JS_NewAtom(engine_->GetContext(), name_upper);
350 valueUpper = JS_GetPropertyInternal(engine_->GetContext(), value_, key, value_, false);
351 JS_FreeAtom(engine_->GetContext(), key);
352
353 int64_t bigintLower = 0;
354 int64_t bigintUpper = 0;
355 JS_ToInt64(engine_->GetContext(), &bigintLower, valueLower);
356 JS_ToInt64(engine_->GetContext(), &bigintUpper, valueUpper);
357
358 if (((uint64_t)bigintLower != typeTag->lower) || ((uint64_t)bigintUpper != typeTag->upper)) {
359 result = false;
360 }
361 }
362 return result;
363 }
364
Freeze()365 void QuickJSNativeObject::Freeze()
366 {
367 JS_Freeze(engine_->GetContext(), value_);
368 }
369
Seal()370 void QuickJSNativeObject::Seal()
371 {
372 JS_Seal(engine_->GetContext(), value_);
373 }