• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 "ets_exceptions.h"
17 #include "ets_handle.h"
18 #include "include/thread_scopes.h"
19 #include "macros.h"
20 #include "types/ets_escompat_array.h"
21 #include "types/ets_object.h"
22 #include "json_helper.h"
23 #include "ets_to_string_cache.h"
24 namespace ark::ets::intrinsics::helpers {
25 
26 static constexpr int RECORD_HEAD_ENTRY_INDEX = 3;
27 static constexpr int RECORD_NEXT_FIELD_INDEX = 1;
28 static constexpr int RECORD_KEY_FIELD_INDEX = 2;
29 static constexpr int RECORD_VAL_FIELD_INDEX = 3;
30 
AppendJSONString(EtsHandle<EtsObject> & value,bool hasContent)31 bool JSONStringifier::AppendJSONString(EtsHandle<EtsObject> &value, bool hasContent)
32 {
33     if (hasContent) {
34         buffer_ += ",";
35     }
36     buffer_ += "\"";
37     buffer_ += key_;
38     buffer_ += "\":";
39     return SerializeObject(value);
40 }
41 
AppendJSONPrimitive(const PandaString & value,bool hasContent)42 void JSONStringifier::AppendJSONPrimitive(const PandaString &value, bool hasContent)
43 {
44     if (hasContent) {
45         buffer_ += ",";
46     }
47 
48     buffer_ += "\"";
49     buffer_ += key_;
50     buffer_ += "\":";
51 
52     if ((value == "NaN") || (value == "Infinity") || (value == "-Infinity")) {
53         buffer_ += "null";
54     } else {
55         buffer_ += value;
56     }
57 }
58 
AppendJSONPointPrimitive(const PandaString & value,bool hasContent)59 void JSONStringifier::AppendJSONPointPrimitive(const PandaString &value, bool hasContent)
60 {
61     if (hasContent) {
62         buffer_ += ",";
63     }
64 
65     buffer_ += "\"";
66     buffer_ += key_;
67     buffer_ += "\":";
68 
69     if ((value == "NaN") || (value == "Infinity") || (value == "-Infinity")) {
70         buffer_ += "null";
71     } else {
72         buffer_ += value;
73     }
74 }
75 
SerializeFields(EtsHandle<EtsObject> & value)76 bool JSONStringifier::SerializeFields(EtsHandle<EtsObject> &value)
77 {
78     ASSERT(value.GetPtr() != nullptr);
79     bool hasContent = false;
80     auto keys = PandaUnorderedSet<PandaString>();
81     bool isSuccessful = false;
82 
83     value->GetClass()->EnumerateBaseClasses([&](EtsClass *c) {
84         auto fields = c->GetRuntimeClass()->GetFields();
85         for (auto &field : fields) {
86             if (!HandleField(value, EtsField::FromRuntimeField(&field), hasContent, keys)) {
87                 isSuccessful = false;
88                 return true;
89             }
90             isSuccessful = true;
91         }
92         return false;
93     });
94     return isSuccessful;
95 }
96 
SerializeJSONObject(EtsHandle<EtsObject> & value)97 bool JSONStringifier::SerializeJSONObject(EtsHandle<EtsObject> &value)
98 {
99     bool isContain = PushValue(value);
100     if (isContain) {
101         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::TYPE_ERROR,
102                           "cyclic object value");
103         return false;
104     }
105     buffer_ += "{";
106     if (!SerializeFields(value)) {
107         return false;
108     };
109     buffer_ += "}";
110     return true;
111 }
112 
SerializeJSONObjectArray(EtsHandle<EtsObject> & value)113 bool JSONStringifier::SerializeJSONObjectArray(EtsHandle<EtsObject> &value)
114 {
115     auto coro = EtsCoroutine::GetCurrent();
116     EtsHandleScope scope(coro);
117 
118     bool isSuccessful = false;
119     buffer_ += "[";
120 
121     auto array = EtsHandle<EtsArrayObject<EtsObject>>(coro, EtsArrayObject<EtsObject>::FromEtsObject(value.GetPtr()));
122     auto realArray = EtsHandle<EtsObjectArray>(coro, array->GetData());
123     ASSERT(realArray.GetPtr() != nullptr);
124     auto length = array->GetActualLength();
125 
126     for (size_t i = 0; i < length; ++i) {
127         auto elem = EtsHandle<EtsObject>(coro, realArray->Get(i));
128         if (elem.GetPtr() == nullptr || elem->GetClass()->IsFunction()) {
129             buffer_ += "null";
130             isSuccessful = true;
131         } else {
132             isSuccessful = SerializeObject(elem);
133         }
134         if (!isSuccessful) {
135             return false;
136         }
137         if (i != length - 1) {
138             buffer_ += ",";
139         }
140     }
141 
142     buffer_ += "]";
143     return isSuccessful;
144 }
145 
DealSpecialCharacter(uint8_t ch)146 bool JSONStringifier::DealSpecialCharacter(uint8_t ch)
147 {
148     bool isSpecialCharacter = true;
149     switch (ch) {
150         case '\"':
151             buffer_ += "\\\"";
152             break;
153         case '\\':
154             buffer_ += "\\\\";
155             break;
156         case '\b':
157             buffer_ += "\\b";
158             break;
159         case '\f':
160             buffer_ += "\\f";
161             break;
162         case '\n':
163             buffer_ += "\\n";
164             break;
165         case '\r':
166             buffer_ += "\\r";
167             break;
168         case '\t':
169             buffer_ += "\\t";
170             break;
171         case '\0':
172             buffer_ += "\\u0000";
173             break;
174         default:
175             if (ch < CODE_SPACE) {
176                 AppendUnicodeEscape(static_cast<uint32_t>(ch));
177             } else {
178                 isSpecialCharacter = false;
179             }
180     }
181     return isSpecialCharacter;
182 }
183 
DealUtf16Character(uint16_t ch0,uint16_t ch1)184 void JSONStringifier::DealUtf16Character(uint16_t ch0, uint16_t ch1)
185 {
186     if ((IsHighSurrogate(ch0) && !IsLowSurrogate(ch1)) || IsLowSurrogate(ch0)) {
187         AppendUnicodeEscape(static_cast<uint32_t>(ch0));
188         return;
189     }
190 
191     utf::Utf8Char uc = utf::ConvertUtf16ToUtf8(ch0, ch1, modify_);
192     for (size_t j = 0; j < uc.n; ++j) {
193         if (uc.n == utf::UtfLength::ONE && DealSpecialCharacter(uc.ch[j])) {
194             break;
195         }
196         buffer_ += uc.ch[j];
197     }
198 }
199 
AppendUtf16ToQuotedString(const Span<const uint16_t> & sp)200 void JSONStringifier::AppendUtf16ToQuotedString(const Span<const uint16_t> &sp)
201 {
202     buffer_ += "\"";
203     uint16_t next16Code = 0;
204     for (uint32_t i = 0; i < sp.size(); ++i) {
205         const auto ch = sp[i];
206         if (IsHighSurrogate(ch) && (i + 1 < sp.size()) && IsLowSurrogate(sp[i + 1])) {
207             next16Code = sp[i + 1];
208             i++;
209         } else {
210             next16Code = 0;
211         }
212         DealUtf16Character(ch, next16Code);
213     }
214     buffer_ += "\"";
215 }
216 
AppendUtf8Character(const Span<const uint8_t> & sp,uint32_t index)217 uint32_t JSONStringifier::AppendUtf8Character(const Span<const uint8_t> &sp, uint32_t index)
218 {
219     uint8_t len {0};
220     const auto ch = sp[index];
221     if (ch < utf::BIT_MASK_1) {
222         len = utf::UtfLength::ONE;
223     } else if ((ch & utf::BIT_MASK_3) == utf::BIT_MASK_2) {
224         len = utf::UtfLength::TWO;
225     } else if ((ch & utf::BIT_MASK_4) == utf::BIT_MASK_3) {
226         len = utf::UtfLength::THREE;
227     } else if ((ch & utf::BIT_MASK_5) == utf::BIT_MASK_4) {
228         len = utf::UtfLength::FOUR;
229     } else {
230         UNREACHABLE();
231     }
232     ASSERT(index + len <= sp.size());
233     for (uint32_t i = 0; i < len; i++) {
234         buffer_ += sp[index + i];
235     }
236     return len;
237 }
238 
AppendUtf8ToQuotedString(const Span<const uint8_t> & sp)239 void JSONStringifier::AppendUtf8ToQuotedString(const Span<const uint8_t> &sp)
240 {
241     buffer_ += "\"";
242     for (uint32_t i = 0; i < sp.size();) {
243         const auto ch = sp[i];
244         if (DealSpecialCharacter(ch)) {
245             i++;
246             continue;
247         }
248         i += AppendUtf8Character(sp, i);
249     }
250     buffer_ += "\"";
251 }
252 
SerializeJSONString(EtsHandle<EtsObject> & value)253 bool JSONStringifier::SerializeJSONString(EtsHandle<EtsObject> &value)
254 {
255     EtsHandleScope scope(EtsCoroutine::GetCurrent());
256     auto stringHandle = EtsHandle<EtsString>(EtsCoroutine::GetCurrent(), EtsString::FromEtsObject(value.GetPtr()));
257     ASSERT(stringHandle.GetPtr() != nullptr);
258     if (stringHandle->IsEmpty()) {
259         buffer_ += "\"\"";
260     } else if (stringHandle->IsUtf16()) {
261         Span<const uint16_t> sp(stringHandle->GetDataUtf16(), stringHandle->GetLength());
262         AppendUtf16ToQuotedString(sp);
263     } else {
264         Span<const uint8_t> sp(stringHandle->GetDataMUtf8(), stringHandle->GetLength());
265         AppendUtf8ToQuotedString(sp);
266     }
267     return true;
268 }
269 
SerializeJSONBoxedBoolean(EtsHandle<EtsObject> & value)270 bool JSONStringifier::SerializeJSONBoxedBoolean(EtsHandle<EtsObject> &value)
271 {
272     auto val = EtsBoxPrimitive<EtsBoolean>::Unbox(value.GetPtr());
273     if (val == 0) {
274         buffer_ += "false";
275     } else {
276         buffer_ += "true";
277     }
278     return true;
279 }
280 
SerializeJSONBoxedDouble(EtsHandle<EtsObject> & value)281 bool JSONStringifier::SerializeJSONBoxedDouble(EtsHandle<EtsObject> &value)
282 {
283     auto val = EtsBoxPrimitive<EtsDouble>::Unbox(value.GetPtr());
284     auto cache = PandaEtsVM::GetCurrent()->GetDoubleToStringCache();
285     auto coro = EtsCoroutine::GetCurrent();
286     PandaString v = cache->GetOrCache(coro, val)->GetMutf8();
287     if ((v == "NaN") || (v == "Infinity") || (v == "-Infinity")) {
288         buffer_ += "null";
289     } else {
290         buffer_ += v;
291     }
292     return true;
293 }
294 
SerializeJSONBoxedFloat(EtsHandle<EtsObject> & value)295 bool JSONStringifier::SerializeJSONBoxedFloat(EtsHandle<EtsObject> &value)
296 {
297     auto val = EtsBoxPrimitive<EtsFloat>::Unbox(value.GetPtr());
298     auto cache = PandaEtsVM::GetCurrent()->GetFloatToStringCache();
299     auto coro = EtsCoroutine::GetCurrent();
300     PandaString v = cache->GetOrCache(coro, val)->GetMutf8();
301     if ((v == "NaN") || (v == "Infinity") || (v == "-Infinity")) {
302         buffer_ += "null";
303     } else {
304         buffer_ += v;
305     }
306     return true;
307 }
308 
SerializeJSONBoxedLong(EtsHandle<EtsObject> & value)309 bool JSONStringifier::SerializeJSONBoxedLong(EtsHandle<EtsObject> &value)
310 {
311     auto val = EtsBoxPrimitive<EtsLong>::Unbox(value.GetPtr());
312     auto cache = PandaEtsVM::GetCurrent()->GetLongToStringCache();
313     auto coro = EtsCoroutine::GetCurrent();
314     PandaString v = cache->GetOrCache(coro, val)->GetMutf8();
315     if ((v == "NaN") || (v == "Infinity") || (v == "-Infinity")) {
316         buffer_ += "null";
317     } else {
318         buffer_ += v;
319     }
320     return true;
321 }
322 
323 template <typename T>
SerializeJSONBoxedPrimitiveNoCache(EtsHandle<EtsObject> & value)324 bool JSONStringifier::SerializeJSONBoxedPrimitiveNoCache(EtsHandle<EtsObject> &value)
325 {
326     T val = EtsBoxPrimitive<T>::Unbox(value.GetPtr());
327     if constexpr (std::is_same_v<T, EtsChar>) {
328         buffer_ += "\"";
329         buffer_ += static_cast<char>(val);
330         buffer_ += "\"";
331     } else {
332         buffer_ += std::to_string(val);
333     }
334     return true;
335 }
336 
SerializeEmptyObject()337 bool JSONStringifier::SerializeEmptyObject()
338 {
339     buffer_ += "{}";
340     return true;
341 }
342 
SerializeJSONNullValue()343 bool JSONStringifier::SerializeJSONNullValue()
344 {
345     buffer_ += "null";
346     return true;
347 }
348 
349 template <typename T>
HandleNumeric(EtsHandle<EtsObject> & value)350 PandaString JSONStringifier::HandleNumeric(EtsHandle<EtsObject> &value)
351 {
352     PandaString result;
353     auto coro = EtsCoroutine::GetCurrent();
354     if constexpr (std::is_same_v<T, EtsDouble>) {
355         auto val = EtsBoxPrimitive<EtsDouble>::Unbox(value.GetPtr());
356         auto cache = PandaEtsVM::GetCurrent()->GetDoubleToStringCache();
357         auto v = cache->GetOrCache(coro, val)->GetMutf8();
358         if ((v == "NaN") || (v == "Infinity") || (v == "-Infinity")) {
359             result = "null";
360         } else {
361             result = v;
362         }
363     } else if constexpr (std::is_same_v<T, EtsFloat>) {
364         auto val = EtsBoxPrimitive<EtsFloat>::Unbox(value.GetPtr());
365         auto cache = PandaEtsVM::GetCurrent()->GetFloatToStringCache();
366         auto v = cache->GetOrCache(coro, val)->GetMutf8();
367         if ((v == "NaN") || (v == "Infinity") || (v == "-Infinity")) {
368             result = "null";
369         } else {
370             result = v;
371         }
372     } else if constexpr (std::is_same_v<T, EtsLong>) {
373         auto val = EtsBoxPrimitive<EtsLong>::Unbox(value.GetPtr());
374         auto cache = PandaEtsVM::GetCurrent()->GetLongToStringCache();
375         auto v = cache->GetOrCache(coro, val)->GetMutf8();
376         if ((v == "NaN") || (v == "Infinity") || (v == "-Infinity")) {
377             result = "null";
378         } else {
379             result = v;
380         }
381     } else if constexpr (std::is_same<T, EtsBoolean>()) {
382         auto val = EtsBoxPrimitive<EtsBoolean>::Unbox(value.GetPtr());
383         result = val == 0 ? "false" : "true";
384     } else if constexpr (std::is_same<T, EtsChar>()) {
385         auto val = EtsBoxPrimitive<EtsChar>::Unbox(value.GetPtr());
386         result = static_cast<char>(val);
387     } else {
388         T val = EtsBoxPrimitive<T>::Unbox(value.GetPtr());
389         result = std::to_string(val);
390     }
391     return result;
392 }
393 
HandleRecordKey(EtsHandle<EtsObject> & key)394 bool JSONStringifier::HandleRecordKey(EtsHandle<EtsObject> &key)
395 {
396     ASSERT(key.GetPtr() != nullptr);
397     if (key->IsStringClass()) {
398         key_ = EtsString::FromEtsObject(key.GetPtr())->GetMutf8();
399     } else {
400         auto desc = key->GetClass()->GetDescriptor();
401         if (desc == panda_file_items::class_descriptors::BOX_BOOLEAN) {
402             key_ = HandleNumeric<EtsBoolean>(key);
403         } else if (desc == panda_file_items::class_descriptors::BOX_DOUBLE) {
404             key_ = HandleNumeric<EtsDouble>(key);
405         } else if (desc == panda_file_items::class_descriptors::BOX_FLOAT) {
406             key_ = HandleNumeric<EtsFloat>(key);
407         } else if (desc == panda_file_items::class_descriptors::BOX_LONG) {
408             key_ = HandleNumeric<EtsLong>(key);
409         } else if (desc == panda_file_items::class_descriptors::BOX_BYTE) {
410             key_ = HandleNumeric<EtsByte>(key);
411         } else if (desc == panda_file_items::class_descriptors::BOX_SHORT) {
412             key_ = HandleNumeric<EtsShort>(key);
413         } else if (desc == panda_file_items::class_descriptors::BOX_CHAR) {
414             key_ = HandleNumeric<EtsChar>(key);
415         } else if (desc == panda_file_items::class_descriptors::BOX_INT) {
416             key_ = HandleNumeric<EtsInt>(key);
417         }
418     }
419     return true;
420 }
421 
SerializeJSONRecord(EtsHandle<EtsObject> & value)422 bool JSONStringifier::SerializeJSONRecord(EtsHandle<EtsObject> &value)
423 {
424     bool isContain = PushValue(value);
425     if (isContain) {
426         ThrowEtsException(EtsCoroutine::GetCurrent(), panda_file_items::class_descriptors::TYPE_ERROR,
427                           "cyclic object value");
428         return false;
429     }
430     buffer_ += "{";
431     ASSERT(value.GetPtr() != nullptr);
432     auto cls = value->GetClass();
433     auto headEntry = cls->GetFieldByIndex(RECORD_HEAD_ENTRY_INDEX);
434     auto coro = EtsCoroutine::GetCurrent();
435 
436     EtsHandleScope scope(coro);
437     EtsHandle<EtsObject> head(coro, value->GetFieldObject(headEntry));
438     if (head.GetPtr() == nullptr) {
439         buffer_ += "}";
440         return true;
441     }
442     auto hasContent = false;
443     EtsHandle<EtsObject> next(coro, head->GetFieldObject(head->GetClass()->GetFieldByIndex(RECORD_NEXT_FIELD_INDEX)));
444     if (next.GetPtr() == nullptr) {
445         buffer_ += "}";
446         return true;
447     }
448     do {
449         EtsHandle<EtsObject> key(coro, next->GetFieldObject(next->GetClass()->GetFieldByIndex(RECORD_KEY_FIELD_INDEX)));
450         EtsHandle<EtsObject> val(coro, next->GetFieldObject(next->GetClass()->GetFieldByIndex(RECORD_VAL_FIELD_INDEX)));
451         next = EtsHandle<EtsObject>(coro,
452                                     next->GetFieldObject(next->GetClass()->GetFieldByIndex(RECORD_NEXT_FIELD_INDEX)));
453         if (key.GetPtr() == nullptr || val.GetPtr() == nullptr) {
454             continue;
455         }
456         if (val->GetClass()->IsFunction()) {
457             continue;
458         }
459         HandleRecordKey(key);
460         AppendJSONString(val, hasContent);
461         hasContent = true;
462     } while (head.GetPtr() != next.GetPtr() && next.GetPtr() != nullptr);
463     buffer_ += "}";
464     return true;
465 }
466 
PushValue(EtsHandle<EtsObject> & value)467 bool JSONStringifier::PushValue(EtsHandle<EtsObject> &value)
468 {
469     ASSERT(value.GetPtr() != nullptr);
470     if (path_.find(value->GetHashCode()) != path_.end()) {
471         return true;
472     }
473     path_.insert(value->GetHashCode());
474     return false;
475 }
476 
ResolveDisplayName(const EtsField * field)477 PandaString JSONStringifier::ResolveDisplayName(const EtsField *field)
478 {
479     auto fieldNameData = field->GetCoreType()->GetName();
480     auto fieldNameLength = fieldNameData.utf16Length;
481     std::string_view fieldName(utf::Mutf8AsCString(fieldNameData.data), fieldNameLength);
482     if (fieldName.rfind(PROPERTY, 0) == 0) {
483         ASSERT(fieldNameLength > PROPERTY_PREFIX_LENGTH);
484         return PandaString(reinterpret_cast<const char *>(
485                                fieldNameData.data +  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
486                                PROPERTY_PREFIX_LENGTH),
487                            fieldNameLength - PROPERTY_PREFIX_LENGTH);
488     }
489     return PandaString(reinterpret_cast<const char *>(fieldNameData.data), fieldNameLength);
490 }
491 
CheckUnsupportedAnnotation(EtsField * field)492 bool JSONStringifier::CheckUnsupportedAnnotation(EtsField *field)
493 {
494     auto *runtimeClass = field->GetDeclaringClass()->GetRuntimeClass();
495     const panda_file::File &pf = *runtimeClass->GetPandaFile();
496     panda_file::FieldDataAccessor fda(pf, field->GetRuntimeField()->GetFileId());
497     bool hasAnnotation = false;
498     fda.EnumerateAnnotations([&hasAnnotation]([[maybe_unused]] panda_file::File::EntityId annId) {
499         hasAnnotation = true;
500         return;
501     });
502     return hasAnnotation;
503 }
504 
505 // CC-OFFNXT(huge_method[C++], huge_cyclomatic_complexity[C++], G.FUN.01-CPP) solid logic
506 // CC-OFFNXT(huge_cca_cyclomatic_complexity[C++]) solid logic
SerializeObject(EtsHandle<EtsObject> & value)507 bool JSONStringifier::SerializeObject(EtsHandle<EtsObject> &value)
508 {
509     if (value.GetPtr() == nullptr) {
510         return true;
511     }
512 
513     auto coro = EtsCoroutine::GetCurrent();
514     auto platformTypes = PlatformTypes(coro);
515     bool isSuccessful = false;
516 
517     auto desc = value->GetClass()->GetDescriptor();
518     if (desc == panda_file_items::class_descriptors::BOX_BOOLEAN) {
519         buffer_ += HandleNumeric<EtsBoolean>(value);
520         isSuccessful = true;
521     } else if (desc == panda_file_items::class_descriptors::BOX_DOUBLE) {
522         buffer_ += HandleNumeric<EtsDouble>(value);
523         isSuccessful = true;
524     } else if (desc == panda_file_items::class_descriptors::BOX_FLOAT) {
525         buffer_ += HandleNumeric<EtsFloat>(value);
526         isSuccessful = true;
527     } else if (desc == panda_file_items::class_descriptors::BOX_LONG) {
528         buffer_ += HandleNumeric<EtsLong>(value);
529         isSuccessful = true;
530     } else if (desc == panda_file_items::class_descriptors::BOX_BYTE) {
531         buffer_ += HandleNumeric<EtsByte>(value);
532         isSuccessful = true;
533     } else if (desc == panda_file_items::class_descriptors::BOX_SHORT) {
534         buffer_ += HandleNumeric<EtsShort>(value);
535         isSuccessful = true;
536     } else if (desc == panda_file_items::class_descriptors::BOX_CHAR) {
537         buffer_ += "\"" + HandleNumeric<EtsChar>(value) + "\"";
538         isSuccessful = true;
539     } else if (desc == panda_file_items::class_descriptors::BOX_INT) {
540         buffer_ += HandleNumeric<EtsInt>(value);
541         isSuccessful = true;
542     } else if (desc == panda_file_items::class_descriptors::STRING) {
543         isSuccessful = SerializeJSONString(value);
544     } else if (desc == panda_file_items::class_descriptors::RECORD) {
545         coro->ManagedCodeEnd();
546         {
547             ScopedManagedCodeThread v(coro);
548             isSuccessful = SerializeJSONRecord(value);
549         }
550         coro->ManagedCodeBegin();
551     } else if (desc == panda_file_items::class_descriptors::DATE) {
552         isSuccessful = false;
553     } else if (desc == panda_file_items::class_descriptors::ARRAY) {
554         coro->ManagedCodeEnd();
555         {
556             ScopedManagedCodeThread v(coro);
557             isSuccessful = SerializeJSONObjectArray(value);
558         }
559         coro->ManagedCodeBegin();
560     } else if (desc == panda_file_items::class_descriptors::PROMISE ||
561                desc == panda_file_items::class_descriptors::SET || desc == panda_file_items::class_descriptors::MAP ||
562                desc == panda_file_items::class_descriptors::MAPENTRY) {
563         isSuccessful = SerializeEmptyObject();
564     } else if (desc == panda_file_items::class_descriptors::NULL_VALUE) {
565         isSuccessful = SerializeJSONNullValue();
566     } else if (desc == panda_file_items::class_descriptors::JS_VALUE) {
567         isSuccessful = false;
568     } else {
569         if (value->IsInstanceOf(platformTypes->escompatRegExpExecArray)) {
570             coro->ManagedCodeEnd();
571             {
572                 ScopedManagedCodeThread v(coro);
573                 auto result = EtsHandle<EtsObject>(coro, value->GetFieldObject(value->GetClass()->GetFieldByIndex(1)));
574                 isSuccessful = SerializeJSONObjectArray(result);
575             }
576             coro->ManagedCodeBegin();
577         } else if (value->IsInstanceOf(platformTypes->escompatJsonReplacer)) {
578             PandaVector<Value> args {Value(value->GetCoreType())};
579             auto jsonReplacerMethod = value->GetClass()->GetInstanceMethod("jsonReplacer", nullptr)->GetPandaMethod();
580             auto ret = jsonReplacerMethod->Invoke(coro, args.data());
581             if (UNLIKELY(coro->HasPendingException())) {
582                 return false;
583             }
584             auto retobj = EtsHandle<EtsObject>(coro, EtsObject::FromCoreType(ret.GetAs<ObjectHeader *>()));
585             coro->ManagedCodeEnd();
586             {
587                 ScopedManagedCodeThread v(coro);
588                 isSuccessful = SerializeJSONRecord(retobj);
589             }
590             coro->ManagedCodeBegin();
591         } else if (value->IsArrayClass()) {
592             coro->ManagedCodeEnd();
593             {
594                 ScopedManagedCodeThread v(coro);
595                 isSuccessful = SerializeJSONObjectArray(value);
596             }
597             coro->ManagedCodeBegin();
598         } else if (value->GetClass()->IsFunction()) {
599             buffer_ += "undefined";
600         } else if (value->IsInstanceOf(platformTypes->coreTuple)) {
601             isSuccessful = false;
602         } else if (value->GetClass()->IsClass()) {
603             coro->ManagedCodeEnd();
604             {
605                 ScopedManagedCodeThread v(coro);
606                 isSuccessful = SerializeJSONObject(value);
607             }
608             coro->ManagedCodeBegin();
609         } else {
610             LOG(ERROR, ETS) << "Unsupported type: " << value->GetClass()->GetDescriptor();
611             return false;
612         }
613     }
614     return isSuccessful;
615 }
616 
617 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
HandleField(EtsHandle<EtsObject> & obj,EtsField * etsField,bool & hasContent,PandaUnorderedSet<PandaString> & keys)618 bool JSONStringifier::HandleField(EtsHandle<EtsObject> &obj, EtsField *etsField, bool &hasContent,
619                                   PandaUnorderedSet<PandaString> &keys)
620 {
621     if (etsField->IsStatic()) {
622         return true;
623     }
624 
625     if (CheckUnsupportedAnnotation(etsField)) {
626         return false;
627     }
628     key_ = ResolveDisplayName(etsField);
629     if (keys.find(key_) != keys.end()) {
630         return false;
631     }
632     keys.insert(key_);
633     auto coro = EtsCoroutine::GetCurrent();
634     auto etsType = etsField->GetEtsType();
635 
636     switch (etsType) {
637         case EtsType::BOOLEAN: {
638             auto val = obj->GetFieldPrimitive<EtsBoolean>(etsField);
639             PandaString value = val == 0 ? "false" : "true";
640             AppendJSONPrimitive(value, hasContent);
641             break;
642         }
643         case EtsType::BYTE: {
644             auto val = obj->GetFieldPrimitive<EtsByte>(etsField);
645             AppendJSONPrimitive(PandaString(std::to_string(val)), hasContent);
646             break;
647         }
648         case EtsType::CHAR: {
649             auto val = obj->GetFieldPrimitive<EtsChar>(etsField);
650             AppendJSONPrimitive(PandaString(std::to_string(val)), hasContent);
651             break;
652         }
653         case EtsType::SHORT: {
654             auto val = obj->GetFieldPrimitive<EtsShort>(etsField);
655             AppendJSONPrimitive(PandaString(std::to_string(val)), hasContent);
656             break;
657         }
658         case EtsType::INT: {
659             auto val = obj->GetFieldPrimitive<EtsInt>(etsField);
660             AppendJSONPrimitive(PandaString(std::to_string(val)), hasContent);
661             break;
662         }
663         case EtsType::LONG: {
664             ASSERT(obj.GetPtr() != nullptr);
665             auto val = obj->GetFieldPrimitive<EtsLong>(etsField);
666             auto cache = PandaEtsVM::GetCurrent()->GetLongToStringCache();
667             PandaString v = cache->GetOrCache(coro, val)->GetMutf8();
668             AppendJSONPrimitive(v, hasContent);
669             break;
670         }
671         case EtsType::FLOAT: {
672             auto val = obj->GetFieldPrimitive<EtsFloat>(etsField);
673             auto cache = PandaEtsVM::GetCurrent()->GetFloatToStringCache();
674             PandaString v = cache->GetOrCache(coro, val)->GetMutf8();
675             AppendJSONPrimitive(v, hasContent);
676             break;
677         }
678         case EtsType::DOUBLE: {
679             auto val = obj->GetFieldPrimitive<EtsDouble>(etsField);
680             auto cache = PandaEtsVM::GetCurrent()->GetDoubleToStringCache();
681             PandaString v = cache->GetOrCache(coro, val)->GetMutf8();
682             AppendJSONPrimitive(v, hasContent);
683             break;
684         }
685         default:
686             auto fieldObj = EtsHandle<EtsObject>(EtsCoroutine::GetCurrent(), obj->GetFieldObject(etsField));
687             if (fieldObj.GetPtr() == nullptr || fieldObj->GetClass()->IsFunction()) {
688                 return true;
689             }
690             if (!AppendJSONString(fieldObj, hasContent)) {
691                 return false;
692             }
693             break;
694     }
695     hasContent = true;
696     return true;
697 }
698 
Stringify(EtsHandle<EtsObject> & value)699 EtsString *JSONStringifier::Stringify(EtsHandle<EtsObject> &value)
700 {
701     bool result = false;
702     auto coro = EtsCoroutine::GetCurrent();
703     ASSERT(coro != nullptr);
704     result = SerializeObject(value);
705     if (!result || coro->HasPendingException()) {
706         return nullptr;
707     }
708     return EtsString::CreateFromUtf8(buffer_.c_str(), buffer_.length());
709 }
710 
711 }  // namespace ark::ets::intrinsics::helpers
712