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 
21 using namespace panda::ecmascript;
22 using namespace panda::ecmascript::base;
23 
24 namespace panda::test {
25 class JsonParserTest : public BaseTestWithScope<false> {
26 public:
27     using TransformType = base::JsonHelper::TransformType;
28 
CheckUnsupportedSendableJson(JSHandle<JSTaggedValue> &result) const29     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 
CheckSendableConstraint(JSTaggedValue value) const40     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 
CheckSendableProps(JSTaggedValue value, TaggedObject *obj) const69     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  */
HWTEST_F_L0(JsonParserTest, Parser_001)110 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_002)138 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_003)174 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_004)194 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_005)221 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_006)250 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_007)264 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_008)280 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_009)299 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_010)316 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_011)333 HWTEST_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  */
HWTEST_F_L0(JsonParserTest, Parser_012)350 HWTEST_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