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