1/*
2 * Copyright (c) 2024 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 "ecmascript/napi/jsnapi_helper.h"
17#include "ecmascript/tests/test_helper.h"
18#include "gtest/gtest.h"
19
20using namespace panda::ecmascript;
21
22namespace panda::test {
23class JSNApiTests : public testing::Test {
24public:
25    static void SetUpTestCase()
26    {
27        GTEST_LOG_(INFO) << "SetUpTestCase";
28    }
29
30    static void TearDownTestCase()
31    {
32        GTEST_LOG_(INFO) << "TearDownCase";
33    }
34
35    void SetUp() override
36    {
37        RuntimeOption option;
38        option.SetLogLevel(RuntimeOption::LOG_LEVEL::ERROR);
39        vm_ = JSNApi::CreateJSVM(option);
40        ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime";
41        thread_ = vm_->GetJSThread();
42        vm_->SetEnableForceGC(true);
43        thread_->ManagedCodeBegin();
44        staticKey = StringRef::NewFromUtf8(vm_, "static");
45        nonStaticKey = StringRef::NewFromUtf8(vm_, "nonStatic");
46        instanceKey = StringRef::NewFromUtf8(vm_, "instance");
47        getterSetterKey = StringRef::NewFromUtf8(vm_, "getterSetter");
48        invalidKey = StringRef::NewFromUtf8(vm_, "invalid");
49    }
50
51    void TearDown() override
52    {
53        thread_->ManagedCodeEnd();
54        vm_->SetEnableForceGC(false);
55        JSNApi::DestroyJSVM(vm_);
56    }
57
58    template <typename T>
59    void TestNumberRef(T val, TaggedType expected)
60    {
61        LocalScope scope(vm_);
62        Local<NumberRef> obj = NumberRef::New(vm_, val);
63        ASSERT_TRUE(obj->IsNumber());
64        JSTaggedType res = JSNApiHelper::ToJSTaggedValue(*obj).GetRawData();
65        ASSERT_EQ(res, expected);
66        if constexpr (std::is_floating_point_v<T>) {
67            if (std::isnan(val)) {
68                ASSERT_TRUE(std::isnan(obj->Value()));
69            } else {
70                ASSERT_EQ(obj->Value(), val);
71            }
72        } else if constexpr (sizeof(T) >= sizeof(int32_t)) {
73            ASSERT_EQ(obj->IntegerValue(vm_), val);
74        } else if constexpr (std::is_signed_v<T>) {
75            ASSERT_EQ(obj->Int32Value(vm_), val);
76        } else {
77            ASSERT_EQ(obj->Uint32Value(vm_), val);
78        }
79    }
80
81    TaggedType ConvertDouble(double val)
82    {
83        return base::bit_cast<JSTaggedType>(val) + JSTaggedValue::DOUBLE_ENCODE_OFFSET;
84    }
85
86protected:
87    JSThread *thread_ = nullptr;
88    EcmaVM *vm_ = nullptr;
89    Local<StringRef> staticKey;
90    Local<StringRef> nonStaticKey;
91    Local<StringRef> instanceKey;
92    Local<StringRef> getterSetterKey;
93    Local<StringRef> invalidKey;
94};
95
96panda::JSValueRef FunctionCallback(JsiRuntimeCallInfo *info)
97{
98    EcmaVM *vm = info->GetVM();
99    Local<JSValueRef> jsThisRef = info->GetThisRef();
100    Local<ObjectRef> thisRef = jsThisRef->ToObject(vm);
101    return **thisRef;
102}
103
104Local<FunctionRef> GetNewSendableClassFunction(
105    EcmaVM *vm, Local<FunctionRef> parent, bool isDict = false, bool duplicated = false, bool isParent = false)
106{
107    FunctionRef::SendablePropertiesInfos infos;
108
109    if (isDict) {
110        uint32_t maxInline = std::max(JSSharedFunction::MAX_INLINE, JSSharedObject::MAX_INLINE);
111        for (uint32_t i = 0; i < maxInline; ++i) {
112            Local<StringRef> tempStr = StringRef::NewFromUtf8(vm, std::to_string(i).c_str());
113            infos.instancePropertiesInfo.keys.push_back(tempStr);
114            infos.instancePropertiesInfo.types.push_back(FunctionRef::SendableType::NONE);
115            infos.instancePropertiesInfo.attributes.push_back(PropertyAttribute(tempStr, true, true, true));
116            infos.staticPropertiesInfo.keys.push_back(tempStr);
117            infos.staticPropertiesInfo.types.push_back(FunctionRef::SendableType::NONE);
118            infos.staticPropertiesInfo.attributes.push_back(PropertyAttribute(tempStr, true, true, true));
119            infos.nonStaticPropertiesInfo.keys.push_back(tempStr);
120            infos.nonStaticPropertiesInfo.types.push_back(FunctionRef::SendableType::NONE);
121            infos.nonStaticPropertiesInfo.attributes.push_back(PropertyAttribute(tempStr, true, true, true));
122        }
123    }
124
125    std::string instanceKey = "instance";
126    std::string staticKey = "static";
127    std::string nonStaticKey = "nonStatic";
128
129    if (isParent) {
130        instanceKey = "parentInstance";
131        staticKey = "parentStatic";
132        nonStaticKey = "parentNonStatic";
133    }
134
135    Local<StringRef> instanceStr = StringRef::NewFromUtf8(vm, instanceKey.c_str());
136    infos.instancePropertiesInfo.keys.push_back(instanceStr);
137    infos.instancePropertiesInfo.types.push_back(FunctionRef::SendableType::NONE);
138    infos.instancePropertiesInfo.attributes.push_back(PropertyAttribute(instanceStr, true, true, true));
139
140    Local<StringRef> staticStr = StringRef::NewFromUtf8(vm, staticKey.c_str());
141    infos.staticPropertiesInfo.keys.push_back(staticStr);
142    infos.staticPropertiesInfo.types.push_back(FunctionRef::SendableType::NONE);
143    infos.staticPropertiesInfo.attributes.push_back(PropertyAttribute(staticStr, true, true, true));
144
145    Local<StringRef> nonStaticStr = StringRef::NewFromUtf8(vm, nonStaticKey.c_str());
146    infos.nonStaticPropertiesInfo.keys.push_back(nonStaticStr);
147    infos.nonStaticPropertiesInfo.types.push_back(FunctionRef::SendableType::NONE);
148    infos.nonStaticPropertiesInfo.attributes.push_back(PropertyAttribute(nonStaticStr, true, true, true));
149
150    if (duplicated) {
151        Local<StringRef> duplicatedKey = StringRef::NewFromUtf8(vm, "parentInstance");
152        Local<NumberRef> duplicatedValue = NumberRef::New(vm, 0);
153        infos.instancePropertiesInfo.keys.push_back(duplicatedKey);
154        infos.instancePropertiesInfo.types.push_back(FunctionRef::SendableType::NONE);
155        infos.instancePropertiesInfo.attributes.push_back(PropertyAttribute(duplicatedValue, true, true, true));
156    }
157
158    Local<FunctionRef> constructor = FunctionRef::NewSendableClassFunction(
159        vm, FunctionCallback, nullptr, nullptr, StringRef::NewFromUtf8(vm, "name"), infos, parent);
160
161    return constructor;
162}
163
164HWTEST_F_L0(JSNApiTests, NewSendableClassFunction)
165{
166    LocalScope scope(vm_);
167    Local<FunctionRef> constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_));
168
169    ASSERT_EQ("name", constructor->GetName(vm_)->ToString(vm_));
170    ASSERT_TRUE(constructor->IsFunction(vm_));
171    JSHandle<JSTaggedValue> jsConstructor = JSNApiHelper::ToJSHandle(constructor);
172    ASSERT_TRUE(jsConstructor->IsClassConstructor());
173
174    Local<JSValueRef> functionPrototype = constructor->GetFunctionPrototype(vm_);
175    ASSERT_TRUE(functionPrototype->IsObject(vm_));
176    JSHandle<JSTaggedValue> jsPrototype = JSNApiHelper::ToJSHandle(functionPrototype);
177    ASSERT_TRUE(jsPrototype->IsClassPrototype());
178
179    const GlobalEnvConstants *globalConst = thread_->GlobalConstants();
180    auto prototype = JSTaggedValue::GetProperty(
181                         thread_, JSNApiHelper::ToJSHandle(constructor), globalConst->GetHandledPrototypeString())
182                         .GetValue();
183    ASSERT_FALSE(prototype->IsUndefined());
184    ASSERT_TRUE(prototype->IsECMAObject() || prototype->IsNull());
185}
186
187HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionProperties)
188{
189    LocalScope scope(vm_);
190    Local<FunctionRef> constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_));
191    Local<ObjectRef> prototype = constructor->GetFunctionPrototype(vm_);
192
193    ASSERT_EQ("static", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString(vm_));
194    ASSERT_EQ("nonStatic", prototype->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
195    ASSERT_EQ("undefined", constructor->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
196    ASSERT_EQ("undefined", prototype->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
197
198    // set static property on constructor
199    constructor->Set(vm_, staticKey, StringRef::NewFromUtf8(vm_, "static0"));
200    ASSERT_EQ("static0", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString(vm_));
201
202    // set non static property on prototype
203    prototype->Set(vm_, nonStaticKey, StringRef::NewFromUtf8(vm_, "nonStatic0"));
204    ASSERT_EQ("nonStatic0", prototype->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
205
206    // set invalid property on constructor
207    ASSERT_FALSE(vm_->GetJSThread()->HasPendingException());
208    constructor->Set(vm_, invalidKey, StringRef::NewFromUtf8(vm_, "invalid"));
209    ASSERT_TRUE(vm_->GetJSThread()->HasPendingException());
210    JSNApi::GetAndClearUncaughtException(vm_);
211    ASSERT_EQ("undefined", constructor->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
212
213    // set invalid property on prototype
214    ASSERT_FALSE(vm_->GetJSThread()->HasPendingException());
215    prototype->Set(vm_, invalidKey, StringRef::NewFromUtf8(vm_, "invalid"));
216    ASSERT_TRUE(vm_->GetJSThread()->HasPendingException());
217    JSNApi::GetAndClearUncaughtException(vm_);
218    ASSERT_EQ("undefined", prototype->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
219}
220
221HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictProperties)
222{
223    LocalScope scope(vm_);
224    Local<FunctionRef> constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_), true);
225    Local<ObjectRef> prototype = constructor->GetFunctionPrototype(vm_);
226
227    ASSERT_EQ("static", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString(vm_));
228    ASSERT_EQ("nonStatic", prototype->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
229    ASSERT_EQ("undefined", constructor->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
230    ASSERT_EQ("undefined", prototype->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
231
232    // set static property on constructor
233    constructor->Set(vm_, staticKey, StringRef::NewFromUtf8(vm_, "static0"));
234    ASSERT_EQ("static0", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString(vm_));
235
236    // set non static property on prototype
237    prototype->Set(vm_, nonStaticKey, StringRef::NewFromUtf8(vm_, "nonStatic0"));
238    ASSERT_EQ("nonStatic0", prototype->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
239
240    // set invalid property on constructor
241    ASSERT_FALSE(vm_->GetJSThread()->HasPendingException());
242    constructor->Set(vm_, invalidKey, StringRef::NewFromUtf8(vm_, "invalid"));
243    ASSERT_TRUE(vm_->GetJSThread()->HasPendingException());
244    JSNApi::GetAndClearUncaughtException(vm_);
245    ASSERT_EQ("undefined", constructor->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
246
247    // set invalid property on prototype
248    ASSERT_FALSE(vm_->GetJSThread()->HasPendingException());
249    prototype->Set(vm_, invalidKey, StringRef::NewFromUtf8(vm_, "invalid"));
250    ASSERT_TRUE(vm_->GetJSThread()->HasPendingException());
251    JSNApi::GetAndClearUncaughtException(vm_);
252    ASSERT_EQ("undefined", prototype->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
253}
254
255HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInstance)
256{
257    LocalScope scope(vm_);
258    Local<FunctionRef> constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_));
259    Local<JSValueRef> argv[1] = {NumberRef::New(vm_, 0)};
260    Local<ObjectRef> obj = constructor->Constructor(vm_, argv, 0);
261    Local<ObjectRef> obj0 = constructor->Constructor(vm_, argv, 0);
262
263    ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj), JSNApiHelper::ToJSHandle(constructor)));
264    ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj0), JSNApiHelper::ToJSHandle(constructor)));
265
266    // set instance property
267    ASSERT_EQ("undefined", obj->Get(vm_, instanceKey)->ToString(vm_)->ToString(vm_));
268    ASSERT_EQ("undefined", obj0->Get(vm_, instanceKey)->ToString(vm_)->ToString(vm_));
269    obj->Set(vm_, instanceKey, StringRef::NewFromUtf8(vm_, "instance"));
270    ASSERT_EQ("instance", obj->Get(vm_, instanceKey)->ToString(vm_)->ToString(vm_));
271    ASSERT_EQ("undefined", obj0->Get(vm_, instanceKey)->ToString(vm_)->ToString(vm_));
272
273    // set non static property on prototype and get from instance
274    ASSERT_EQ("nonStatic", obj->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
275    ASSERT_EQ("nonStatic", obj0->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
276    Local<ObjectRef> prototype = obj->GetPrototype(vm_);
277    prototype->Set(vm_, nonStaticKey, StringRef::NewFromUtf8(vm_, "nonStatic0"));
278    ASSERT_EQ("nonStatic0", obj->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
279    ASSERT_EQ("nonStatic0", obj0->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
280
281    // set non static property on instance
282    ASSERT_FALSE(vm_->GetJSThread()->HasPendingException());
283    obj->Set(vm_, nonStaticKey, StringRef::NewFromUtf8(vm_, "nonStatic1"));
284    ASSERT_TRUE(vm_->GetJSThread()->HasPendingException());
285    JSNApi::GetAndClearUncaughtException(vm_);
286    ASSERT_EQ("nonStatic0", obj->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
287    ASSERT_EQ("nonStatic0", obj0->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
288
289    // set invalid property on instance
290    ASSERT_EQ("undefined", obj->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
291    ASSERT_FALSE(vm_->GetJSThread()->HasPendingException());
292    obj->Set(vm_, invalidKey, StringRef::NewFromUtf8(vm_, "invalid"));
293    ASSERT_TRUE(vm_->GetJSThread()->HasPendingException());
294    JSNApi::GetAndClearUncaughtException(vm_);
295    ASSERT_EQ("undefined", obj->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
296}
297
298HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictInstance)
299{
300    LocalScope scope(vm_);
301    Local<FunctionRef> constructor = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_), true);
302    Local<JSValueRef> argv[1] = {NumberRef::New(vm_, 0)};
303    Local<ObjectRef> obj = constructor->Constructor(vm_, argv, 0);
304    Local<ObjectRef> obj0 = constructor->Constructor(vm_, argv, 0);
305
306    ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj), JSNApiHelper::ToJSHandle(constructor)));
307    ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj0), JSNApiHelper::ToJSHandle(constructor)));
308
309    // set instance property
310    ASSERT_EQ("undefined", obj->Get(vm_, instanceKey)->ToString(vm_)->ToString(vm_));
311    ASSERT_EQ("undefined", obj0->Get(vm_, instanceKey)->ToString(vm_)->ToString(vm_));
312    obj->Set(vm_, instanceKey, StringRef::NewFromUtf8(vm_, "instance"));
313    ASSERT_EQ("instance", obj->Get(vm_, instanceKey)->ToString(vm_)->ToString(vm_));
314    ASSERT_EQ("undefined", obj0->Get(vm_, instanceKey)->ToString(vm_)->ToString(vm_));
315
316    // set non static property on prototype and get from instance
317    ASSERT_EQ("nonStatic", obj->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
318    ASSERT_EQ("nonStatic", obj0->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
319    Local<ObjectRef> prototype = obj->GetPrototype(vm_);
320    prototype->Set(vm_, nonStaticKey, StringRef::NewFromUtf8(vm_, "nonStatic0"));
321    ASSERT_EQ("nonStatic0", obj->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
322    ASSERT_EQ("nonStatic0", obj0->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
323
324    // set non static property on instance
325    ASSERT_FALSE(vm_->GetJSThread()->HasPendingException());
326    obj->Set(vm_, nonStaticKey, StringRef::NewFromUtf8(vm_, "nonStatic1"));
327    ASSERT_TRUE(vm_->GetJSThread()->HasPendingException());
328    JSNApi::GetAndClearUncaughtException(vm_);
329    ASSERT_EQ("nonStatic0", obj->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
330    ASSERT_EQ("nonStatic0", obj0->Get(vm_, nonStaticKey)->ToString(vm_)->ToString(vm_));
331
332    // set invalid property on instance
333    ASSERT_EQ("undefined", obj->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
334    ASSERT_FALSE(vm_->GetJSThread()->HasPendingException());
335    obj->Set(vm_, invalidKey, StringRef::NewFromUtf8(vm_, "invalid"));
336    ASSERT_TRUE(vm_->GetJSThread()->HasPendingException());
337    JSNApi::GetAndClearUncaughtException(vm_);
338    ASSERT_EQ("undefined", obj->Get(vm_, invalidKey)->ToString(vm_)->ToString(vm_));
339}
340
341HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInherit)
342{
343    LocalScope scope(vm_);
344    Local<FunctionRef> parent = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_), false, false, true);
345    Local<FunctionRef> constructor = GetNewSendableClassFunction(vm_, parent);
346    Local<JSValueRef> argv[1] = {NumberRef::New(vm_, 0)};
347    Local<ObjectRef> obj = constructor->Constructor(vm_, argv, 0);
348    Local<ObjectRef> obj0 = constructor->Constructor(vm_, argv, 0);
349
350    ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj), JSNApiHelper::ToJSHandle(parent)));
351    ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj0), JSNApiHelper::ToJSHandle(parent)));
352
353    // set parent instance property on instance
354    Local<StringRef> parentInstanceKey = StringRef::NewFromUtf8(vm_, "parentInstance");
355    ASSERT_EQ("undefined", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString(vm_));
356    obj->Set(vm_, parentInstanceKey, StringRef::NewFromUtf8(vm_, "parentInstance"));
357    ASSERT_EQ("parentInstance", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString(vm_));
358
359    // get parent static property from constructor
360    Local<StringRef> parentStaticKey = StringRef::NewFromUtf8(vm_, "parentStatic");
361    ASSERT_EQ("parentStatic", constructor->Get(vm_, parentStaticKey)->ToString(vm_)->ToString(vm_));
362
363    // get parent non static property form instance
364    Local<StringRef> parentNonStaticKey = StringRef::NewFromUtf8(vm_, "parentNonStatic");
365    ASSERT_EQ("parentNonStatic", obj->Get(vm_, parentNonStaticKey)->ToString(vm_)->ToString(vm_));
366}
367
368HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionDictInherit)
369{
370    LocalScope scope(vm_);
371    Local<FunctionRef> parent = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_), true, false, true);
372    Local<FunctionRef> constructor = GetNewSendableClassFunction(vm_, parent);
373    Local<JSValueRef> argv[1] = {NumberRef::New(vm_, 0)};
374    Local<ObjectRef> obj = constructor->Constructor(vm_, argv, 0);
375    Local<ObjectRef> obj0 = constructor->Constructor(vm_, argv, 0);
376
377    ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj), JSNApiHelper::ToJSHandle(parent)));
378    ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj0), JSNApiHelper::ToJSHandle(parent)));
379
380    // set parent instance property on instance
381    Local<StringRef> parentInstanceKey = StringRef::NewFromUtf8(vm_, "parentInstance");
382    ASSERT_EQ("undefined", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString(vm_));
383    obj->Set(vm_, parentInstanceKey, StringRef::NewFromUtf8(vm_, "parentInstance"));
384    ASSERT_EQ("parentInstance", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString(vm_));
385
386    // get parent static property from constructor
387    Local<StringRef> parentStaticKey = StringRef::NewFromUtf8(vm_, "parentStatic");
388    ASSERT_EQ("parentStatic", constructor->Get(vm_, parentStaticKey)->ToString(vm_)->ToString(vm_));
389
390    // get parent non static property form instance
391    Local<StringRef> parentNonStaticKey = StringRef::NewFromUtf8(vm_, "parentNonStatic");
392    ASSERT_EQ("parentNonStatic", obj->Get(vm_, parentNonStaticKey)->ToString(vm_)->ToString(vm_));
393}
394
395HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionInheritWithDuplicatedKey)
396{
397    LocalScope scope(vm_);
398    Local<FunctionRef> parent = GetNewSendableClassFunction(vm_, FunctionRef::Null(vm_), false, false, true);
399    Local<FunctionRef> constructor = GetNewSendableClassFunction(vm_, parent, false, true);
400    Local<JSValueRef> argv[1] = {NumberRef::New(vm_, 0)};
401    Local<ObjectRef> obj = constructor->Constructor(vm_, argv, 0);
402
403    ASSERT_TRUE(JSFunction::InstanceOf(thread_, JSNApiHelper::ToJSHandle(obj), JSNApiHelper::ToJSHandle(parent)));
404
405    // set duplicated instance property on instance
406    Local<StringRef> parentInstanceKey = StringRef::NewFromUtf8(vm_, "parentInstance");
407    ASSERT_EQ("undefined", obj->Get(vm_, parentInstanceKey)->ToString(vm_)->ToString(vm_));
408    obj->Set(vm_, parentInstanceKey, NumberRef::New(vm_, 0));
409    EXPECT_TRUE(NumberRef::New(vm_, 0)->IsStrictEquals(vm_, obj->Get(vm_, parentInstanceKey)));
410}
411
412HWTEST_F_L0(JSNApiTests, NewSendable)
413{
414    LocalScope scope(vm_);
415    Local<FunctionRef> func = FunctionRef::NewSendable(
416        vm_,
417        [](JsiRuntimeCallInfo *runtimeInfo) -> JSValueRef {
418            EcmaVM *vm = runtimeInfo->GetVM();
419            LocalScope scope(vm);
420            return **StringRef::NewFromUtf8(vm, "funcResult");
421        },
422        nullptr);
423    Local<JSValueRef> res = func->Call(vm_, JSValueRef::Undefined(vm_), nullptr, 0);
424    ASSERT_EQ("funcResult", res->ToString(vm_)->ToString(vm_));
425}
426
427HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionFunction)
428{
429    LocalScope scope(vm_);
430
431    FunctionRef::SendablePropertiesInfos infos;
432
433    Local<FunctionRef> func = FunctionRef::NewSendable(
434        vm_,
435        [](JsiRuntimeCallInfo *runtimeInfo) -> JSValueRef {
436            EcmaVM *vm = runtimeInfo->GetVM();
437            LocalScope scope(vm);
438            return **StringRef::NewFromUtf8(vm, "funcResult");
439        },
440        nullptr);
441    infos.staticPropertiesInfo.keys.push_back(staticKey);
442    infos.staticPropertiesInfo.types.push_back(FunctionRef::SendableType::OBJECT);
443    infos.staticPropertiesInfo.attributes.push_back(PropertyAttribute(func, true, true, true));
444
445    Local<FunctionRef> constructor = FunctionRef::NewSendableClassFunction(
446        vm_, FunctionCallback, nullptr, nullptr, StringRef::NewFromUtf8(vm_, "name"), infos, FunctionRef::Null(vm_));
447
448    Local<FunctionRef> staticValue = constructor->Get(vm_, staticKey);
449    ASSERT_TRUE(staticValue->IsFunction(vm_));
450    Local<JSValueRef> res = staticValue->Call(vm_, JSValueRef::Undefined(vm_), nullptr, 0);
451    ASSERT_EQ("funcResult", res->ToString(vm_)->ToString(vm_));
452}
453
454HWTEST_F_L0(JSNApiTests, NewSendableClassFunctionGetterSetter)
455{
456    LocalScope scope(vm_);
457
458    FunctionRef::SendablePropertiesInfos infos;
459
460    Local<StringRef> getterSetter = StringRef::NewFromUtf8(vm_, "getterSetter");
461    infos.staticPropertiesInfo.keys.push_back(getterSetter);
462    infos.staticPropertiesInfo.types.push_back(FunctionRef::SendableType::NONE);
463    infos.staticPropertiesInfo.attributes.push_back(PropertyAttribute(getterSetter, true, true, true));
464    Local<FunctionRef> staticGetter = FunctionRef::NewSendable(
465        vm_,
466        [](JsiRuntimeCallInfo *info) -> JSValueRef {
467            Local<JSValueRef> value = info->GetThisRef();
468            Local<ObjectRef> obj = value->ToObject(info->GetVM());
469            Local<JSValueRef> temp = obj->Get(info->GetVM(), StringRef::NewFromUtf8(info->GetVM(), "getterSetter"));
470            return **temp->ToString(info->GetVM());
471        },
472        nullptr);
473    Local<FunctionRef> staticSetter = FunctionRef::NewSendable(
474        vm_,
475        [](JsiRuntimeCallInfo *info) -> JSValueRef {
476            Local<JSValueRef> arg = info->GetCallArgRef(0);
477            Local<JSValueRef> value = info->GetThisRef();
478            Local<ObjectRef> obj = value->ToObject(info->GetVM());
479            obj->Set(info->GetVM(), StringRef::NewFromUtf8(info->GetVM(), "getterSetter"), arg);
480            return **JSValueRef::Undefined(info->GetVM());
481        },
482        nullptr);
483    Local<JSValueRef> staticValue = panda::ObjectRef::CreateSendableAccessorData(vm_, staticGetter, staticSetter);
484    infos.staticPropertiesInfo.keys.push_back(staticKey);
485    infos.staticPropertiesInfo.types.push_back(FunctionRef::SendableType::OBJECT);
486    infos.staticPropertiesInfo.attributes.push_back(PropertyAttribute(staticValue, true, true, true));
487
488    Local<FunctionRef> constructor = FunctionRef::NewSendableClassFunction(
489        vm_, FunctionCallback, nullptr, nullptr, StringRef::NewFromUtf8(vm_, "name"), infos, FunctionRef::Null(vm_));
490
491    ASSERT_EQ("getterSetter", constructor->Get(vm_, getterSetter)->ToString(vm_)->ToString(vm_));
492    ASSERT_EQ("getterSetter", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString(vm_));
493    constructor->Set(vm_, staticKey, StringRef::NewFromUtf8(vm_, "getterSetter0"));
494    ASSERT_EQ("getterSetter0", constructor->Get(vm_, getterSetter)->ToString(vm_)->ToString(vm_));
495    ASSERT_EQ("getterSetter0", constructor->Get(vm_, staticKey)->ToString(vm_)->ToString(vm_));
496}
497
498HWTEST_F_L0(JSNApiTests, NewObjectWithProperties)
499{
500    LocalScope scope(vm_);
501
502    FunctionRef::SendablePropertiesInfo info;
503    Local<StringRef> str = StringRef::NewFromUtf8(vm_, "str");
504    info.keys.push_back(str);
505    info.types.push_back(FunctionRef::SendableType::NONE);
506    info.attributes.push_back(PropertyAttribute(str, true, true, true));
507
508    Local<ObjectRef> object = ObjectRef::NewSWithProperties(vm_, info);
509    Local<JSValueRef> value = object->Get(vm_, str);
510    EXPECT_TRUE(str->IsStrictEquals(vm_, value));
511}
512
513HWTEST_F_L0(JSNApiTests, SendableMapRef_GetSize_GetTotalElements_Get_GetKey_GetValue)
514{
515    LocalScope scope(vm_);
516    Local<SendableMapRef> map = SendableMapRef::New(vm_);
517    Local<JSValueRef> key = StringRef::NewFromUtf8(vm_, "TestKey");
518    Local<JSValueRef> value = StringRef::NewFromUtf8(vm_, "TestValue");
519    map->Set(vm_, key, value);
520    Local<JSValueRef> res = map->Get(vm_, key);
521    ASSERT_EQ(res->ToString(vm_)->ToString(vm_), value->ToString(vm_)->ToString(vm_));
522    int32_t num = map->GetSize(vm_);
523    int32_t num1 = map->GetTotalElements(vm_);
524    ASSERT_EQ(num, 1);
525    ASSERT_EQ(num1, 1);
526    Local<JSValueRef> res1 = map->GetKey(vm_, 0);
527    ASSERT_EQ(res1->ToString(vm_)->ToString(vm_), key->ToString(vm_)->ToString(vm_));
528    Local<JSValueRef> res2 = map->GetValue(vm_, 0);
529    ASSERT_EQ(res2->ToString(vm_)->ToString(vm_), value->ToString(vm_)->ToString(vm_));
530}
531
532HWTEST_F_L0(JSNApiTests, SendableSetRef_GetSize_GetTotalElements_GetValue)
533{
534    LocalScope scope(vm_);
535    Local<SendableSetRef> set = SendableSetRef::New(vm_);
536    EXPECT_TRUE(set->IsSharedSet(vm_));
537    Local<JSValueRef> value = StringRef::NewFromUtf8(vm_, "TestValue");
538    set->Add(vm_, value);
539    int32_t num = set->GetSize(vm_);
540    int32_t num1 = set->GetTotalElements(vm_);
541    ASSERT_EQ(num, 1);
542    ASSERT_EQ(num1, 1);
543    Local<JSValueRef> res = set->GetValue(vm_, 0);
544    ASSERT_EQ(res->ToString(vm_)->ToString(vm_), value->ToString(vm_)->ToString(vm_));
545}
546
547}  // namespace panda::test
548