• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 <sstream>
17 #include <string>
18 #include <string_view>
19 #include "include/coretypes/array.h"
20 #include "include/managed_thread.h"
21 #include "macros.h"
22 #include "mem/vm_handle.h"
23 #include "napi/ets_napi.h"
24 #include "runtime/handle_scope-inl.h"
25 #include "intrinsics.h"
26 #include "type.h"
27 #include "types/ets_array.h"
28 #include "types/ets_class.h"
29 #include "types/ets_primitives.h"
30 #include "types/ets_box_primitive-inl.h"
31 #include "types/ets_string.h"
32 #include "utils/json_builder.h"
33 
34 namespace {
35 panda::JsonObjectBuilder ObjectToJSON(panda::ets::EtsObject *d);
36 
EtsCharToString(ets_char data)37 std::string EtsCharToString(ets_char data)
38 {
39     constexpr auto HIGH_SURROGATE_MIN = 0xD800;
40     constexpr auto LOW_SURROGATE_MAX = 0xDFFF;
41     if ((data < HIGH_SURROGATE_MIN) || (data > LOW_SURROGATE_MAX)) {
42         return std::string(reinterpret_cast<const char *>(&data), 1);
43     }
44     return std::string(reinterpret_cast<const char *>(&data), 2);
45 }
46 
EtsStringToView(panda::ets::EtsString * s)47 std::string_view EtsStringToView(panda::ets::EtsString *s)
48 {
49     auto str = std::string_view();
50     if (s->IsUtf16()) {
51         str = std::string_view(reinterpret_cast<const char *>(s->GetDataUtf16()), s->GetUtf16Length() - 1);
52     } else {
53         str = std::string_view(reinterpret_cast<const char *>(s->GetDataMUtf8()), s->GetMUtf8Length() - 1);
54     }
55     return str;
56 }
57 
58 template <typename PrimArr>
EtsPrimitiveArrayToJSON(panda::JsonArrayBuilder & jsonBuilder,PrimArr * arrPtr)59 void EtsPrimitiveArrayToJSON(panda::JsonArrayBuilder &jsonBuilder, PrimArr *arrPtr)
60 {
61     ASSERT(arrPtr->GetClass()->IsArrayClass() && arrPtr->IsPrimitive());
62     auto len = arrPtr->GetLength();
63     for (size_t i = 0; i < len; ++i) {
64         jsonBuilder.Add(arrPtr->Get(i));
65     }
66 }
67 
68 template <>
EtsPrimitiveArrayToJSON(panda::JsonArrayBuilder & jsonBuilder,panda::ets::EtsBooleanArray * arrPtr)69 void EtsPrimitiveArrayToJSON(panda::JsonArrayBuilder &jsonBuilder, panda::ets::EtsBooleanArray *arrPtr)
70 {
71     ASSERT(arrPtr->IsPrimitive());
72     auto len = arrPtr->GetLength();
73     for (size_t i = 0; i < len; ++i) {
74         if (arrPtr->Get(i) != 0) {
75             jsonBuilder.Add(true);
76         } else {
77             jsonBuilder.Add(false);
78         }
79     }
80 }
81 
82 template <>
EtsPrimitiveArrayToJSON(panda::JsonArrayBuilder & jsonBuilder,panda::ets::EtsCharArray * arrPtr)83 void EtsPrimitiveArrayToJSON(panda::JsonArrayBuilder &jsonBuilder, panda::ets::EtsCharArray *arrPtr)
84 {
85     ASSERT(arrPtr->IsPrimitive());
86     auto len = arrPtr->GetLength();
87     for (size_t i = 0; i < len; ++i) {
88         auto data = arrPtr->Get(i);
89         jsonBuilder.Add(EtsCharToString(data));
90     }
91 }
92 
EtsArrayToJSON(panda::ets::EtsArray * arrPtr)93 panda::JsonArrayBuilder EtsArrayToJSON(panda::ets::EtsArray *arrPtr)
94 {
95     auto jsonBuilder = panda::JsonArrayBuilder();
96     if (arrPtr->IsPrimitive()) {
97         panda::panda_file::Type::TypeId componentType =
98             arrPtr->GetCoreType()->ClassAddr<panda::Class>()->GetComponentType()->GetType().GetId();
99         switch (componentType) {
100             case panda::panda_file::Type::TypeId::U1: {
101                 EtsPrimitiveArrayToJSON(jsonBuilder, reinterpret_cast<panda::ets::EtsBooleanArray *>(arrPtr));
102                 break;
103             }
104             case panda::panda_file::Type::TypeId::I8: {
105                 EtsPrimitiveArrayToJSON(jsonBuilder, reinterpret_cast<panda::ets::EtsByteArray *>(arrPtr));
106                 break;
107             }
108             case panda::panda_file::Type::TypeId::I16: {
109                 EtsPrimitiveArrayToJSON(jsonBuilder, reinterpret_cast<panda::ets::EtsShortArray *>(arrPtr));
110                 break;
111             }
112             case panda::panda_file::Type::TypeId::U16: {
113                 EtsPrimitiveArrayToJSON(jsonBuilder, reinterpret_cast<panda::ets::EtsCharArray *>(arrPtr));
114                 break;
115             }
116             case panda::panda_file::Type::TypeId::I32: {
117                 EtsPrimitiveArrayToJSON(jsonBuilder, reinterpret_cast<panda::ets::EtsIntArray *>(arrPtr));
118                 break;
119             }
120             case panda::panda_file::Type::TypeId::F32: {
121                 EtsPrimitiveArrayToJSON(jsonBuilder, reinterpret_cast<panda::ets::EtsFloatArray *>(arrPtr));
122                 break;
123             }
124             case panda::panda_file::Type::TypeId::F64: {
125                 EtsPrimitiveArrayToJSON(jsonBuilder, reinterpret_cast<panda::ets::EtsDoubleArray *>(arrPtr));
126                 break;
127             }
128             case panda::panda_file::Type::TypeId::I64: {
129                 EtsPrimitiveArrayToJSON(jsonBuilder, reinterpret_cast<panda::ets::EtsLongArray *>(arrPtr));
130                 break;
131             }
132             case panda::panda_file::Type::TypeId::U8:
133             case panda::panda_file::Type::TypeId::U32:
134             case panda::panda_file::Type::TypeId::U64:
135             case panda::panda_file::Type::TypeId::REFERENCE:
136             case panda::panda_file::Type::TypeId::TAGGED:
137             case panda::panda_file::Type::TypeId::INVALID:
138             case panda::panda_file::Type::TypeId::VOID:
139             default:
140                 UNREACHABLE();
141                 break;
142         }
143     } else {
144         auto arrObjPtr = reinterpret_cast<panda::ets::EtsObjectArray *>(arrPtr);
145         auto len = arrObjPtr->GetLength();
146         for (size_t i = 0; i < len; ++i) {
147             auto d = arrObjPtr->Get(i);
148             auto dCls = d->GetClass();
149             auto typeDesc = dCls->GetDescriptor();
150             if (dCls->IsStringClass()) {
151                 auto sPtr = reinterpret_cast<panda::ets::EtsString *>(d);
152                 jsonBuilder.Add(EtsStringToView(sPtr));
153             } else if (dCls->IsArrayClass()) {
154                 jsonBuilder.Add([d](panda::JsonArrayBuilder &x) {
155                     x = EtsArrayToJSON(reinterpret_cast<panda::ets::EtsArray *>(d));
156                 });
157             } else if (dCls->IsBoxedClass()) {
158                 if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_BOOLEAN) {
159                     jsonBuilder.Add(panda::ets::EtsBoxPrimitive<panda::ets::EtsBoolean>::FromCoreType(d)->GetValue());
160                 } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_BYTE) {
161                     jsonBuilder.Add(panda::ets::EtsBoxPrimitive<panda::ets::EtsByte>::FromCoreType(d)->GetValue());
162                 } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_CHAR) {
163                     jsonBuilder.Add(panda::ets::EtsBoxPrimitive<panda::ets::EtsChar>::FromCoreType(d)->GetValue());
164                 } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_SHORT) {
165                     jsonBuilder.Add(panda::ets::EtsBoxPrimitive<panda::ets::EtsShort>::FromCoreType(d)->GetValue());
166                 } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_INT) {
167                     jsonBuilder.Add(panda::ets::EtsBoxPrimitive<panda::ets::EtsInt>::FromCoreType(d)->GetValue());
168                 } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_LONG) {
169                     jsonBuilder.Add(panda::ets::EtsBoxPrimitive<panda::ets::EtsLong>::FromCoreType(d)->GetValue());
170                 } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_FLOAT) {
171                     jsonBuilder.Add(panda::ets::EtsBoxPrimitive<panda::ets::EtsFloat>::FromCoreType(d)->GetValue());
172                 } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_DOUBLE) {
173                     jsonBuilder.Add(panda::ets::EtsBoxPrimitive<panda::ets::EtsDouble>::FromCoreType(d)->GetValue());
174                 } else {
175                     UNREACHABLE();
176                 }
177             } else {
178                 jsonBuilder.Add([d](panda::JsonObjectBuilder &x) { x = ObjectToJSON(d); });
179             }
180         }
181     }
182     return jsonBuilder;
183 }
184 
AddFieldsToJSON(panda::JsonObjectBuilder & curJson,const panda::Span<panda::Field> & fields,panda::ets::EtsObject * d)185 void AddFieldsToJSON(panda::JsonObjectBuilder &curJson, const panda::Span<panda::Field> &fields,
186                      panda::ets::EtsObject *d)
187 {
188     for (const auto &f : fields) {
189         ASSERT(f.IsStatic() == false);
190         auto fieldName = reinterpret_cast<const char *>(f.GetName().data);
191 
192         switch (f.GetTypeId()) {
193             case panda::panda_file::Type::TypeId::U1:
194                 curJson.AddProperty(fieldName, d->GetFieldPrimitive<bool>(f.GetOffset()));
195                 break;
196             case panda::panda_file::Type::TypeId::I8:
197                 curJson.AddProperty(fieldName, d->GetFieldPrimitive<int8_t>(f.GetOffset()));
198                 break;
199             case panda::panda_file::Type::TypeId::I16:
200                 curJson.AddProperty(fieldName, d->GetFieldPrimitive<int16_t>(f.GetOffset()));
201                 break;
202             case panda::panda_file::Type::TypeId::U16:
203                 curJson.AddProperty(fieldName, EtsCharToString(d->GetFieldPrimitive<ets_char>(f.GetOffset())));
204                 break;
205             case panda::panda_file::Type::TypeId::I32:
206                 curJson.AddProperty(fieldName, d->GetFieldPrimitive<int32_t>(f.GetOffset()));
207                 break;
208             case panda::panda_file::Type::TypeId::F32:
209                 curJson.AddProperty(fieldName, d->GetFieldPrimitive<float>(f.GetOffset()));
210                 break;
211             case panda::panda_file::Type::TypeId::F64:
212                 curJson.AddProperty(fieldName, d->GetFieldPrimitive<double>(f.GetOffset()));
213                 break;
214             case panda::panda_file::Type::TypeId::I64:
215                 curJson.AddProperty(fieldName, d->GetFieldPrimitive<int64_t>(f.GetOffset()));
216                 break;
217             case panda::panda_file::Type::TypeId::REFERENCE: {
218                 auto *fPtr = d->GetFieldObject(f.GetOffset());
219                 if (fPtr != nullptr) {
220                     auto fCls = fPtr->GetClass();
221                     auto typeDesc = fCls->GetDescriptor();
222                     if (fCls->IsStringClass()) {
223                         auto sPtr = reinterpret_cast<panda::ets::EtsString *>(fPtr);
224                         curJson.AddProperty(fieldName, EtsStringToView(sPtr));
225                     } else if (fCls->IsArrayClass()) {
226                         auto aPtr = reinterpret_cast<panda::ets::EtsArray *>(fPtr);
227                         curJson.AddProperty(fieldName,
228                                             [aPtr](panda::JsonArrayBuilder &x) { x = EtsArrayToJSON(aPtr); });
229                     } else if (fCls->IsBoxedClass()) {
230                         if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_BOOLEAN) {
231                             curJson.AddProperty(
232                                 fieldName, static_cast<bool>(
233                                                panda::ets::EtsBoxPrimitive<panda::ets::EtsBoolean>::FromCoreType(fPtr)
234                                                    ->GetValue()));
235                         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_BYTE) {
236                             curJson.AddProperty(
237                                 fieldName,
238                                 panda::ets::EtsBoxPrimitive<panda::ets::EtsByte>::FromCoreType(fPtr)->GetValue());
239                         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_CHAR) {
240                             curJson.AddProperty(
241                                 fieldName,
242                                 panda::ets::EtsBoxPrimitive<panda::ets::EtsChar>::FromCoreType(fPtr)->GetValue());
243                         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_SHORT) {
244                             curJson.AddProperty(
245                                 fieldName,
246                                 panda::ets::EtsBoxPrimitive<panda::ets::EtsShort>::FromCoreType(fPtr)->GetValue());
247                         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_INT) {
248                             curJson.AddProperty(
249                                 fieldName,
250                                 panda::ets::EtsBoxPrimitive<panda::ets::EtsInt>::FromCoreType(fPtr)->GetValue());
251                         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_LONG) {
252                             curJson.AddProperty(
253                                 fieldName,
254                                 panda::ets::EtsBoxPrimitive<panda::ets::EtsLong>::FromCoreType(fPtr)->GetValue());
255                         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_FLOAT) {
256                             curJson.AddProperty(
257                                 fieldName,
258                                 panda::ets::EtsBoxPrimitive<panda::ets::EtsFloat>::FromCoreType(fPtr)->GetValue());
259                         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_DOUBLE) {
260                             curJson.AddProperty(
261                                 fieldName,
262                                 panda::ets::EtsBoxPrimitive<panda::ets::EtsDouble>::FromCoreType(fPtr)->GetValue());
263                         } else {
264                             UNREACHABLE();
265                         }
266                     } else {
267                         curJson.AddProperty(fieldName, [fPtr](panda::JsonObjectBuilder &x) { x = ObjectToJSON(fPtr); });
268                     }
269                 } else {
270                     curJson.AddProperty(fieldName, std::nullptr_t());
271                 }
272                 break;
273             }
274             case panda::panda_file::Type::TypeId::U8:
275             case panda::panda_file::Type::TypeId::U32:
276             case panda::panda_file::Type::TypeId::U64:
277             case panda::panda_file::Type::TypeId::INVALID:
278             case panda::panda_file::Type::TypeId::VOID:
279             case panda::panda_file::Type::TypeId::TAGGED:
280                 UNREACHABLE();
281                 break;
282         }
283     }
284 }
285 
ObjectToJSON(panda::ets::EtsObject * d)286 panda::JsonObjectBuilder ObjectToJSON(panda::ets::EtsObject *d)
287 {
288     ASSERT(d != nullptr);
289     panda::ets::EtsClass *kls = d->GetClass();
290     ASSERT(kls != nullptr);
291 
292     // Only instance fields are required according to JS/TS JSON.stringify behaviour
293     auto curJson = panda::JsonObjectBuilder();
294     kls->EnumerateBaseClasses([&](panda::ets::EtsClass *c) {
295         AddFieldsToJSON(curJson, c->GetRuntimeClass()->GetInstanceFields(), d);
296         return false;
297     });
298     return curJson;
299 }
300 }  // namespace
301 
302 namespace panda::ets::intrinsics {
EscompatJSONStringifyObj(EtsObject * d)303 EtsString *EscompatJSONStringifyObj(EtsObject *d)
304 {
305     ASSERT(d != nullptr);
306     auto thread = ManagedThread::GetCurrent();
307     [[maybe_unused]] auto _ = HandleScope<ObjectHeader *>(thread);
308     auto dHandle = VMHandle<EtsObject>(thread, d->GetCoreType());
309     auto cls = dHandle.GetPtr()->GetClass();
310     auto typeDesc = cls->GetDescriptor();
311 
312     auto resString = std::string();
313     if (cls->IsArrayClass()) {
314         auto arr = reinterpret_cast<panda::ets::EtsArray *>(dHandle.GetPtr());
315         resString = EtsArrayToJSON(reinterpret_cast<panda::ets::EtsArray *>(arr)).Build();
316     } else if (cls->IsBoxedClass()) {
317         std::stringstream ss;
318         ss.setf(std::stringstream::boolalpha);
319         if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_BOOLEAN) {
320             ss << static_cast<bool>(panda::ets::EtsBoxPrimitive<panda::ets::EtsBoolean>::FromCoreType(d)->GetValue());
321         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_BYTE) {
322             ss << panda::ets::EtsBoxPrimitive<panda::ets::EtsByte>::FromCoreType(d)->GetValue();
323         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_CHAR) {
324             ss << panda::ets::EtsBoxPrimitive<panda::ets::EtsChar>::FromCoreType(d)->GetValue();
325         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_SHORT) {
326             ss << panda::ets::EtsBoxPrimitive<panda::ets::EtsShort>::FromCoreType(d)->GetValue();
327         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_INT) {
328             ss << panda::ets::EtsBoxPrimitive<panda::ets::EtsInt>::FromCoreType(d)->GetValue();
329         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_LONG) {
330             ss << panda::ets::EtsBoxPrimitive<panda::ets::EtsLong>::FromCoreType(d)->GetValue();
331         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_FLOAT) {
332             ss << panda::ets::EtsBoxPrimitive<panda::ets::EtsFloat>::FromCoreType(d)->GetValue();
333         } else if (typeDesc == panda::ets::panda_file_items::class_descriptors::BOX_DOUBLE) {
334             ss << panda::ets::EtsBoxPrimitive<panda::ets::EtsDouble>::FromCoreType(d)->GetValue();
335         } else {
336             UNREACHABLE();
337         }
338         resString = ss.str();
339     } else {
340         resString = ObjectToJSON(dHandle.GetPtr()).Build();
341     }
342     auto etsResString = EtsString::CreateFromUtf8(resString.data(), resString.size());
343     return etsResString;
344 }
345 
346 }  // namespace panda::ets::intrinsics
347