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 "ecmascript/base/json_parser.h"
17#include "ecmascript/base/json_helper.h"
18#include "ecmascript/ecma_string.h"
19#include "ecmascript/tests/test_helper.h"
20
21using namespace panda::ecmascript;
22using namespace panda::ecmascript::base;
23
24namespace panda::test {
25class JsonParserTest : public BaseTestWithScope<false> {
26public:
27    using TransformType = base::JsonHelper::TransformType;
28
29    void CheckUnsupportedSendableJson(JSHandle<JSTaggedValue> &result) const
30    {
31        EXPECT_TRUE(result->IsException());
32        JSHandle<JSTaggedValue> exceptionObj(thread, thread->GetException());
33        auto messageValue =
34            JSTaggedValue::GetProperty(thread, exceptionObj, thread->GlobalConstants()->GetHandledMessageString())
35                .GetValue();
36        EXPECT_EQ(ConvertToString(EcmaString::Cast(messageValue->GetTaggedObject())),
37                  MessageString::GetMessageString(GET_MESSAGE_STRING_ID(SendableArrayForJson)).c_str());
38    }
39
40    bool CheckSendableConstraint(JSTaggedValue value) const
41    {
42        if (!value.IsHeapObject()) {
43            // tagged value always follow sendable constraint.
44            return true;
45        }
46        TaggedObject *obj = value.IsWeak() ? value.GetTaggedWeakRef() : value.GetTaggedObject();
47        auto *jsHClass = obj->GetClass();
48        if (!jsHClass->IsJSShared()) {
49            return false;
50        }
51        if (jsHClass->IsExtensible()) {
52            LOG_ECMA(ERROR) << "sendable check failed. obj is extensible";
53            value.D();
54            return false;
55        }
56        if (!CheckSendableProps(value, obj)) {
57            return false;
58        }
59        // trace proto chain
60        auto proto = jsHClass->GetPrototype();
61        if (!proto.IsNull() && !proto.IsJSShared()) {
62            LOG_ECMA(ERROR) << "sendable check failed. proto is not sendable.";
63            value.D();
64            return false;
65        }
66        return true;
67    }
68
69    bool CheckSendableProps(JSTaggedValue value, TaggedObject *obj) const
70    {
71        auto *jsHClass = obj->GetClass();
72        auto layoutValue = jsHClass->GetLayout();
73        if (layoutValue.IsNull()) {
74            return true;
75        }
76        auto *layoutInfo = LayoutInfo::Cast(layoutValue.GetTaggedObject());
77        auto *jsObj = JSObject::Cast(obj);
78        auto *propsValue = TaggedArray::Cast(jsObj->GetProperties());
79        if (propsValue->IsDictionaryMode()) {
80            for (int idx = 0; idx < static_cast<int>(jsHClass->NumberOfProps()); idx++) {
81                auto attr = layoutInfo->GetAttr(idx);
82                if (attr.IsInlinedProps()) {
83                    // Do not check inline props
84                    continue;
85                }
86                if (attr.IsWritable()) {
87                    LOG_ECMA(ERROR) << "sendable check failed. supposed to be un-writable. idx: " << idx;
88                    value.D();
89                    return false;
90                }
91                auto val = propsValue->Get(thread, idx - jsHClass->GetInlinedProperties());
92                if (!CheckSendableConstraint(val)) {
93                    LOG_ECMA(ERROR) << "sendable check failed. supposed to be sendable. idx: " << idx;
94                    value.D();
95                    return false;
96                }
97            }
98        }
99        return true;
100    }
101};
102
103/**
104 * @tc.name: Parser_001
105 * @tc.desc: Passing in a character of type "uint8_t" check whether the result returned through "ParserUtf8" function
106 *           Test without for no Nesting.
107 * @tc.type: FUNC
108 * @tc.require:
109 */
110HWTEST_F_L0(JsonParserTest, Parser_001)
111{
112    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
113    Utf8JsonParser parser(thread, TransformType::NORMAL);
114    // JSON Number
115    JSHandle<JSTaggedValue> handleMsg2(factory->NewFromASCII("1234"));
116    JSHandle<EcmaString> handleStr2(JSTaggedValue::ToString(thread, handleMsg2));
117    JSHandle<JSTaggedValue> result2 = parser.Parse(handleStr2);
118    EXPECT_EQ(result2->GetNumber(), 1234);
119    // JSON Literal
120    JSHandle<JSTaggedValue> handleMsg3(factory->NewFromASCII("true"));
121    JSHandle<EcmaString> handleStr3(JSTaggedValue::ToString(thread, handleMsg3));
122    JSHandle<JSTaggedValue> result3 = parser.Parse(handleStr3);
123    EXPECT_EQ(result3.GetTaggedValue(), JSTaggedValue::True());
124    // JSON Unexpected
125    JSHandle<JSTaggedValue> handleMsg4(factory->NewFromASCII("trus"));
126    JSHandle<EcmaString> handleStr4(JSTaggedValue::ToString(thread, handleMsg4));
127    JSHandle<JSTaggedValue> result4 = parser.Parse(handleStr4);
128    EXPECT_EQ(result4.GetTaggedValue(), JSTaggedValue::Exception());
129}
130
131/**
132 * @tc.name: Parser_002
133 * @tc.desc: Passing in a character of type "uint16_t" check whether the result returned through "ParseUtf16" function
134 *           Test without for no Nesting.
135 * @tc.type: FUNC
136 * @tc.require:
137 */
138HWTEST_F_L0(JsonParserTest, Parser_002)
139{
140    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
141    Utf16JsonParser parser(thread, TransformType::NORMAL);
142
143    // JSON Number
144    uint16_t array1Utf16[] = {0x31, 0x32, 0x33, 0x34}; // "1234"
145    uint32_t array1Utf16Len = sizeof(array1Utf16) / sizeof(array1Utf16[0]);
146    JSHandle<JSTaggedValue> handleMsg2(factory->NewFromUtf16(&array1Utf16[0], array1Utf16Len));
147    JSHandle<EcmaString> handleStr2(JSTaggedValue::ToString(thread, handleMsg2));
148    JSHandle<JSTaggedValue> result2 = parser.Parse(*handleStr2);
149    EXPECT_EQ(result2->GetNumber(), 1234);
150    // JSON Literal
151    uint16_t array2Utf16[] = {0x74, 0x72, 0x75, 0x65}; // "true"
152    uint32_t array2Utf16Len = sizeof(array2Utf16) / sizeof(array2Utf16[0]);
153    JSHandle<JSTaggedValue> handleMsg3(factory->NewFromUtf16(&array2Utf16[0], array2Utf16Len));
154    JSHandle<EcmaString> handleStr3(JSTaggedValue::ToString(thread, handleMsg3));
155    JSHandle<JSTaggedValue> result3 = parser.Parse(*handleStr3);
156    EXPECT_EQ(result3.GetTaggedValue(), JSTaggedValue::True());
157    // JSON String
158    uint16_t array3Utf16[] = {0x22, 0x73, 0x74, 0x72, 0x69, 0x6E, 0X67, 0x22}; // "string"
159    uint32_t array3Utf16Len = sizeof(array3Utf16) / sizeof(array3Utf16[0]);
160    JSHandle<JSTaggedValue> handleMsg4(factory->NewFromUtf16(&array3Utf16[0], array3Utf16Len));
161    JSHandle<EcmaString> handleStr4(JSTaggedValue::ToString(thread, handleMsg4));
162    JSHandle<JSTaggedValue> result4 = parser.Parse(*handleStr4);
163    JSHandle<EcmaString> handleEcmaStr(result4);
164    EXPECT_STREQ("string", EcmaStringAccessor(handleEcmaStr).ToCString().c_str());
165}
166
167/**
168 * @tc.name: Parser_003
169 * @tc.desc: Passing in a character of type "uint8_t" check whether the result returned through "ParserUtf8" function
170 *           Test with for Nesting of numbers, strings, objects, arrays, Booleans.
171 * @tc.type: FUNC
172 * @tc.require:
173 */
174HWTEST_F_L0(JsonParserTest, Parser_003)
175{
176    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
177    Utf8JsonParser parser(thread, TransformType::NORMAL);
178
179    JSHandle<JSTaggedValue> handleMsg(factory->NewFromASCII(
180        "\t\r \n{\t\r \n \"json\"\t\r\n:\t\r \n{\t\r \n}\t\r \n,\t\r \n \"prop2\"\t\r \n:\t\r \n [\t\r \nfalse\t\r"
181        "\n,\t\r \nnull\t\r, \ntrue\t\r,123.456\t\r \n]\t\r \n}\t\r \n"));
182    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg)); // JSON Object
183    JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
184    EXPECT_TRUE(result->IsECMAObject());
185}
186
187/**
188 * @tc.name: Parser_004
189 * @tc.desc: Passing in a character of type "uint8_t" check whether the result returned through "ParserUtf8" function
190 *           Test with for Nesting of numbers, strings, arrays.
191 * @tc.type: FUNC
192 * @tc.require:
193 */
194HWTEST_F_L0(JsonParserTest, Parser_004)
195{
196    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
197    JSHandle<JSTaggedValue> lengthKeyHandle = thread->GlobalConstants()->GetHandledLengthString();
198    Utf8JsonParser parser(thread, TransformType::NORMAL);
199
200    JSHandle<JSTaggedValue> handleMsg(factory->NewFromASCII("[100,2.5,\"abc\"]"));
201    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg)); // JSON Array
202    JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
203
204    JSTaggedValue resultValue(static_cast<JSTaggedType>(result->GetRawData()));
205    EXPECT_TRUE(resultValue.IsECMAObject());
206    JSHandle<JSObject> valueHandle(thread, resultValue);
207
208    JSHandle<JSTaggedValue> lenResult =
209        JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(valueHandle), lengthKeyHandle).GetValue();
210    uint32_t length = JSTaggedValue::ToLength(thread, lenResult).ToUint32();
211    EXPECT_EQ(length, 3U);
212}
213
214/**
215 * @tc.name: Parser_005
216 * @tc.desc: Passing in a character of type "uint8_t" check whether the result returned through "ParserUtf8" function
217 *           Test without for Nesting of numbers, strings, objects.
218 * @tc.type: FUNC
219 * @tc.require:
220 */
221HWTEST_F_L0(JsonParserTest, Parser_005)
222{
223    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
224    Utf8JsonParser parser(thread, TransformType::NORMAL);
225
226    JSHandle<JSTaggedValue> handleMsg(factory->NewFromASCII("{\"epf\":100,\"key1\":400}"));
227    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg)); // JSON Object
228
229    JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
230    JSTaggedValue resultValue(static_cast<JSTaggedType>(result->GetRawData()));
231    EXPECT_TRUE(resultValue.IsECMAObject());
232
233    JSHandle<JSObject> valueHandle(thread, resultValue);
234    JSHandle<TaggedArray> nameList(JSObject::EnumerableOwnNames(thread, valueHandle));
235    JSHandle<JSArray> nameResult = JSArray::CreateArrayFromList(thread, nameList);
236
237    JSHandle<JSTaggedValue> handleKey(nameResult);
238    JSHandle<JSTaggedValue> lengthKey(factory->NewFromASCII("length"));
239    JSHandle<JSTaggedValue> lenResult = JSObject::GetProperty(thread, handleKey, lengthKey).GetValue();
240    uint32_t length = JSTaggedValue::ToLength(thread, lenResult).ToUint32();
241    EXPECT_EQ(length, 2U);
242}
243
244/**
245 * @tc.name: Parser_006
246 * @tc.desc: Try to parse a empty string.
247 * @tc.type: FUNC
248 * @tc.require:
249 */
250HWTEST_F_L0(JsonParserTest, Parser_006)
251{
252    Utf8JsonParser parser(thread, TransformType::NORMAL);
253    JSHandle<EcmaString> emptyString(thread->GlobalConstants()->GetHandledEmptyString());
254    JSHandle<JSTaggedValue> result = parser.Parse(emptyString);
255    EXPECT_TRUE(result->IsException());
256}
257
258/**
259 * @tc.name: Parser_007
260 * @tc.desc: Try to parse a string containing an empty string.
261 * @tc.type: FUNC
262 * @tc.require:
263 */
264HWTEST_F_L0(JsonParserTest, Parser_007)
265{
266    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
267    Utf8JsonParser parser(thread, TransformType::NORMAL);
268
269    JSHandle<EcmaString> handleStr(factory->NewFromASCII("\"\""));
270    JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
271    EXPECT_FALSE(result->IsException());
272}
273
274/**
275 * @tc.name: Parser_008
276 * @tc.desc: Try to parse a string to sendable object.
277 * @tc.type: FUNC
278 * @tc.require:
279 */
280HWTEST_F_L0(JsonParserTest, Parser_008)
281{
282    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
283    Utf8JsonParser parser(thread, TransformType::SENDABLE);
284    JSHandle<JSTaggedValue> handleMsg(
285        factory->NewFromASCII(R"({"innerEntry": {"x":1, "y":"abc", "str": "innerStr"}, "x": 1, "str": "outerStr"})"));
286    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg));
287    JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
288    result->D();
289    EXPECT_FALSE(result->IsException());
290    EXPECT_TRUE(CheckSendableConstraint(result.GetTaggedValue()));
291}
292
293/**
294 * @tc.name: Parser_009
295 * @tc.desc: Try to parse a empty obj json string to sendable object.
296 * @tc.type: FUNC
297 * @tc.require:
298 */
299HWTEST_F_L0(JsonParserTest, Parser_009)
300{
301    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
302    Utf8JsonParser parser(thread, TransformType::SENDABLE);
303    JSHandle<JSTaggedValue> handleMsg(factory->NewFromASCII(R"({})"));
304    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg));
305    JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
306    result->D();
307    EXPECT_TRUE(CheckSendableConstraint(result.GetTaggedValue()));
308}
309
310/**
311 * @tc.name: Parser_010
312 * @tc.desc: Try to parse a empty array json string to sendable object.
313 * @tc.type: FUNC
314 * @tc.require:
315 */
316HWTEST_F_L0(JsonParserTest, Parser_010)
317{
318    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
319    Utf8JsonParser parser(thread, TransformType::SENDABLE);
320    JSHandle<JSTaggedValue> handleMsg(factory->NewFromASCII(R"([])"));
321    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg));
322    JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
323    result->D();
324    EXPECT_TRUE(CheckSendableConstraint(result.GetTaggedValue()));
325}
326
327/**
328 * @tc.name: Parser_011
329 * @tc.desc: Try to parse a simple array json string to sendable object.
330 * @tc.type: FUNC
331 * @tc.require:
332 */
333HWTEST_F_L0(JsonParserTest, Parser_011)
334{
335    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
336    Utf8JsonParser parser(thread, TransformType::SENDABLE);
337    JSHandle<JSTaggedValue> handleMsg(factory->NewFromASCII(R"([1, 2, 3])"));
338    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg));
339    JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
340    result->D();
341    EXPECT_TRUE(CheckSendableConstraint(result.GetTaggedValue()));
342}
343
344/**
345 * @tc.name: Parser_012
346 * @tc.desc: Try to parse a json string with array to sendable object.
347 * @tc.type: FUNC
348 * @tc.require:
349 */
350HWTEST_F_L0(JsonParserTest, Parser_012)
351{
352    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
353    Utf8JsonParser parser(thread, TransformType::SENDABLE);
354    JSHandle<JSTaggedValue> handleMsg(
355        factory->NewFromASCII(R"({"innerEntry": {"array": [1, 2, 3]}, "x": 1, "str": "outerStr"})"));
356    JSHandle<EcmaString> handleStr(JSTaggedValue::ToString(thread, handleMsg));
357    JSHandle<JSTaggedValue> result = parser.Parse(handleStr);
358    result->D();
359    EXPECT_TRUE(CheckSendableConstraint(result.GetTaggedValue()));
360}
361} // namespace panda::test
362