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