1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 29 авг. 2019 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <core/debug.h>
23 #include <core/files/java/defs.h>
24 #include <core/files/java/Object.h>
25 #include <core/files/java/String.h>
26 #include <core/files/java/RawArray.h>
27 #include <core/files/java/Enum.h>
28 #include <core/files/java/wrappers.h>
29 #include <core/files/java/ObjectStreamClass.h>
30 
31 namespace lsp
32 {
33     namespace java
34     {
35         const char *Object::CLASS_NAME       = "java.lang.Object";
36 
Object(const char * class_name)37         Object::Object(const char *class_name)
38         {
39             pClass      = class_name;
40             vSlots      = NULL;
41             nSlots      = 0;
42             vData       = NULL;
43         }
44 
~Object()45         Object::~Object()
46         {
47             if (vSlots != NULL)
48                 ::free(vSlots);
49             if (vData != NULL)
50                 ::free(vData);
51 
52             pClass      = NULL;
53         }
54 
instanceof(const char * name) const55         bool Object::instanceof(const char *name) const
56         {
57             if (name == pClass)
58                 return true;
59             return ::strcmp(name, pClass) == 0;
60         }
61 
62         template <class type_t, class cast_t>
read_reference(const char * field,type_t * item,ftype_t type) const63             inline status_t Object::read_reference(const char *field, type_t *item, ftype_t type) const
64             {
65                 bool found = false;
66 
67                 // Lookup slot
68                 for (ssize_t i=nSlots-1; i>=0; --i)
69                 {
70                     const object_slot_t *s = &vSlots[i];
71                     const ObjectStreamClass *os = s->desc;
72 
73                     // Lookup field
74                     uint8_t *data       = &vData[s->offset];
75                     for (size_t j=0, m=os->fields(); j<m; ++j)
76                     {
77                         // Align base of the current field
78                         const ObjectStreamField *f = os->field(j);
79 
80                         // Check field match
81                         if (::strcmp(f->raw_name(), field) == 0)
82                         {
83                             found = true;
84                             if (f->is_reference())
85                             {
86                                 // Null value?
87                                 const Object *obj = *reinterpret_cast<const Object **>(&data[f->offset()]);
88                                 if (obj == NULL)
89                                     return STATUS_NULL;
90 
91                                 // Can be cast?
92                                 const cast_t *w = obj->cast<cast_t>();
93                                 if (w != NULL)
94                                 {
95                                     // Return the value
96                                     if (item != NULL)
97                                         *item = w;
98                                     return STATUS_OK;
99                                 }
100                             } // is_reference
101                         } // strcmp
102                     } // for
103                 }
104 
105                 return (found) ? STATUS_BAD_TYPE : STATUS_NOT_FOUND;
106             }
107 
108         template <class type_t, class wrapper_t>
read_prim_item(const char * field,type_t * item,ftype_t type) const109             inline status_t Object::read_prim_item(const char *field, type_t *item, ftype_t type) const
110             {
111                 bool found = false;
112 
113                 // Lookup slot
114                 for (ssize_t i=nSlots-1; i>=0; --i)
115                 {
116                     const object_slot_t *s = &vSlots[i];
117                     const ObjectStreamClass *os = s->desc;
118 
119                     // Lookup field
120                     uint8_t *data       = &vData[s->offset];
121                     for (size_t j=0, m=os->fields(); j<m; ++j)
122                     {
123                         // Align base of the current field
124                         const ObjectStreamField *f = os->field(j);
125 
126                         // Check field match
127                         if (::strcmp(f->raw_name(), field) == 0)
128                         {
129                             found = true;
130 
131                             // Type match?
132                             if (f->type() == type)
133                             {
134                                 if (item != NULL)
135                                     *item           = *reinterpret_cast<const type_t *>(&data[f->offset()]);
136                                 return STATUS_OK;
137                             }
138                             else if (f->is_reference()) // Reference type?
139                             {
140                                 // Need to check for wrappers (e.g. java.lang.Long -> long)
141                                 // Null value?
142                                 const Object *obj = *reinterpret_cast<const Object **>(&data[f->offset()]);
143                                 if (obj == NULL)
144                                     return STATUS_NULL;
145 
146                                 // Can be cast to wrapper?
147                                 const wrapper_t *w = obj->cast<const wrapper_t>();
148                                 if (w != NULL)
149                                     return w->get_value(item);
150                             }
151                         }
152                     }
153                 }
154 
155                 return (found) ? STATUS_BAD_TYPE : STATUS_NOT_FOUND;
156             }
157 
get_byte(const char * field,byte_t * dst) const158         status_t Object::get_byte(const char *field, byte_t *dst) const
159         {
160             return read_prim_item<byte_t, Byte>(field, dst, JFT_BYTE);
161         }
162 
get_short(const char * field,short_t * dst) const163         status_t Object::get_short(const char *field, short_t *dst) const
164         {
165             return read_prim_item<short_t, Short>(field, dst, JFT_SHORT);
166         }
167 
get_int(const char * field,int_t * dst) const168         status_t Object::get_int(const char *field, int_t *dst) const
169         {
170             return read_prim_item<int_t, Integer>(field, dst, JFT_INTEGER);
171         }
172 
get_long(const char * field,long_t * dst) const173         status_t Object::get_long(const char *field, long_t *dst) const
174         {
175             return read_prim_item<long_t, Long>(field, dst, JFT_LONG);
176         }
177 
get_float(const char * field,float_t * dst) const178         status_t Object::get_float(const char *field, float_t *dst) const
179         {
180             return read_prim_item<float_t, Float>(field, dst, JFT_FLOAT);
181         }
182 
get_double(const char * field,double_t * dst) const183         status_t Object::get_double(const char *field, double_t *dst) const
184         {
185             return read_prim_item<double_t, Double>(field, dst, JFT_DOUBLE);
186         }
187 
get_char(const char * field,char_t * dst) const188         status_t Object::get_char(const char *field, char_t *dst) const
189         {
190             return read_prim_item<char_t, Character>(field, dst, JFT_CHAR);
191         }
192 
get_bool(const char * field,bool_t * dst) const193         status_t Object::get_bool(const char *field, bool_t *dst) const
194         {
195             return read_prim_item<bool_t, Boolean>(field, dst, JFT_BOOL);
196         }
197 
get_object(const char * field,const Object ** dst) const198         status_t Object::get_object(const char *field, const Object **dst) const
199         {
200             return read_reference<const Object *, Object>(field, dst, JFT_OBJECT);
201         }
202 
get_array(const char * field,const RawArray ** dst) const203         status_t Object::get_array(const char *field, const RawArray **dst) const
204         {
205             return read_reference<const RawArray *, RawArray>(field, dst, JFT_ARRAY);
206         }
207 
get_enum(const char * field,const Enum ** dst) const208         status_t Object::get_enum(const char *field, const Enum **dst) const
209         {
210             const Enum *en = NULL;
211             status_t res = read_reference<const Enum *, Enum>(field, &en, JFT_OBJECT);
212             if (res != STATUS_OK)
213                 return res;
214             if (dst != NULL)
215                 *dst    = en;
216             return STATUS_OK;
217         }
218 
get_enum(const char * field,LSPString * dst) const219         status_t Object::get_enum(const char *field, LSPString *dst) const
220         {
221             const Enum *en = NULL;
222             status_t res = get_enum(field, &en);
223             if (res != STATUS_OK)
224                 return res;
225             else if (en == NULL)
226                 return STATUS_NULL;
227             if (dst == NULL)
228                 return STATUS_OK;
229 
230             return (dst->set(en->name())) ? STATUS_OK : STATUS_NO_MEM;
231         }
232 
get_enum(const char * field,const char ** dst) const233         status_t Object::get_enum(const char *field, const char **dst) const
234         {
235             const Enum *en = NULL;
236             status_t res = get_enum(field, &en);
237             if (res != STATUS_OK)
238                 return res;
239             else if (en == NULL)
240                 return STATUS_NULL;
241             if (dst != NULL)
242                 *dst = en->name()->get_utf8();
243 
244             return STATUS_OK;
245         }
246 
get_string(const char * field,const String ** dst) const247         status_t Object::get_string(const char *field, const String **dst) const
248         {
249             const String *str = NULL;
250             status_t res = read_reference<const String *, String>(field, &str, JFT_OBJECT);
251             if (res != STATUS_OK)
252                 return res;
253             if (dst != NULL)
254                 *dst    = str;
255             return STATUS_OK;
256         }
257 
get_string(const char * field,LSPString * dst) const258         status_t Object::get_string(const char *field, LSPString *dst) const
259         {
260             const String *str = NULL;
261             status_t res = get_string(field, &str);
262             if (res != STATUS_OK)
263                 return res;
264             else if (str == NULL)
265                 return STATUS_NULL;
266             if (dst == NULL)
267                 return STATUS_OK;
268 
269             return (dst->set(str->string())) ? STATUS_OK : STATUS_NO_MEM;
270         }
271 
get_string(const char * field,const char ** dst) const272         status_t Object::get_string(const char *field, const char **dst) const
273         {
274             const String *str = NULL;
275             status_t res = get_string(field, &str);
276             if (res != STATUS_OK)
277                 return res;
278             else if (str == NULL)
279                 return STATUS_NULL;
280             if (dst != NULL)
281                 *dst = str->string()->get_utf8();
282 
283             return STATUS_OK;
284         }
285 
to_string_padded(LSPString * dst,size_t pad)286         status_t Object::to_string_padded(LSPString *dst, size_t pad)
287         {
288 //            lsp_trace("*%p = new ", this);
289             if (!dst->fmt_append_ascii("*%p = new ", this))
290                 return STATUS_NO_MEM;
291             if (!dst->append_utf8(pClass))
292                 return STATUS_NO_MEM;
293             if (!dst->append_ascii(" {\n"))
294                 return STATUS_NO_MEM;
295 
296             ++pad;
297             for (size_t i=0; i<nSlots; ++i)
298             {
299                 prim_ptr_t ptr;
300 
301                 object_slot_t *s        = &vSlots[i];
302                 ObjectStreamClass *os   = s->desc;
303                 uint8_t *data           = &vData[s->offset];
304 
305 //                lsp_trace("i=%d, nslots=%d, s=%p, os=%p", int(i), int(nSlots), s, os);
306 
307                 if (!pad_string(dst, pad))
308                     return STATUS_NO_MEM;
309 //                lsp_trace("%s:\n", os->raw_name());
310                 if (!dst->fmt_append_utf8("%s:\n", os->raw_name()))
311                     return STATUS_NO_MEM;
312 
313                 ++pad;
314 
315                 // Dump fields
316                 for (size_t j=0, n=os->fields(); j<n; ++j)
317                 {
318                     const ObjectStreamField *f = os->field(j);
319                     ptr.p_ubyte     = &data[f->offset()];
320 
321                     if (!pad_string(dst, pad))
322                         return STATUS_NO_MEM;
323 //                    lsp_trace("ptr = %p (type=%d)", ptr.p_ubyte, int(f->type()));
324                     if (!dst->fmt_append_utf8("%s = ", f->name()->get_utf8()))
325                         return STATUS_NO_MEM;
326 
327                     bool res = true;
328                     switch (f->type())
329                     {
330                         case JFT_BYTE:      res = dst->fmt_append_utf8("(byte) %d\n", *ptr.p_byte); break;
331                         case JFT_DOUBLE:    res = dst->fmt_append_utf8("(double) %f\n", *ptr.p_double); break;
332                         case JFT_FLOAT:     res = dst->fmt_append_utf8("(float) %f\n", *ptr.p_float); break;
333                         case JFT_INTEGER:   res = dst->fmt_append_utf8("(int) %d\n", int(*ptr.p_int)); break;
334                         case JFT_LONG:      res = dst->fmt_append_utf8("(long) %lld\n", (long long)(*ptr.p_long)); break;
335                         case JFT_SHORT:     res = dst->fmt_append_utf8("(short) %d\n", int(*ptr.p_short)); break;
336                         case JFT_BOOL:      res = dst->fmt_append_utf8("(bool) %s\n", (*ptr.p_bool) ? "true" : "false"); break;
337                         case JFT_CHAR:
338                             res = dst->append_ascii("'");
339                             if (res)
340                                 res = dst->append(lsp_wchar_t(*ptr.p_char));
341                             if (res)
342                                 res = dst->append_ascii("'\n");
343                             break;
344                         case JFT_ARRAY:
345                         case JFT_OBJECT:
346                         {
347                             Object *obj = *ptr.p_object;
348                             if (obj != NULL)
349                                 res = obj->to_string_padded(dst, pad) == STATUS_OK;
350                             else
351                                 res = dst->append_ascii("null\n");
352                             break;
353                         }
354                         default:
355                             return STATUS_CORRUPTED;
356                     }
357 
358                     if (!res)
359                         return STATUS_NO_MEM;
360                 }
361                 // Dump data
362                 if (os->has_write_method())
363                 {
364                     size_t rows     = (s->size + 0xf) >> 4;
365                     uint8_t *curr   = &vData[s->offset];
366                     uint8_t *end    = &vData[s->offset + s->size];
367 
368                     bool res        = true;
369                     for (size_t j=0; j<rows; ++j)
370                     {
371                         if (!dst->fmt_append_ascii("%08x: ", int(j << 4)))
372                             return STATUS_NO_MEM;
373 
374                         // Hex codes
375                         for (size_t k=0; k<0x10; ++k)
376                         {
377                             if (&curr[k] < end)
378                                 res = dst->fmt_append_ascii("%02x ", int(curr[k]));
379                             else
380                                 res = dst->append_ascii("   ");
381                             if (res != STATUS_OK)
382                                 return STATUS_NO_MEM;
383                         }
384 
385                         // Characters
386                         for (size_t k=0; k<0x10; ++k)
387                         {
388                             if (&curr[k] < end)
389                             {
390                                 char c      = curr[k];
391                                 if ((c < 0x20) || (c >= 0x80))
392                                     c           = '.';
393                                 res = dst->append(c);
394                             }
395                             else
396                                 res = dst->append(' ');
397                             if (res != STATUS_OK)
398                                 return STATUS_NO_MEM;
399                         }
400                         if (!dst->append('\n'))
401                             return STATUS_NO_MEM;
402                     }
403                 }
404                 --pad;
405             }
406             --pad;
407 
408             if (!pad_string(dst, pad))
409                 return STATUS_NO_MEM;
410             if (!dst->append_ascii("}\n"))
411                 return STATUS_NO_MEM;
412 
413             return STATUS_OK;
414         }
415 
pad_string(LSPString * dst,size_t pad)416         bool Object::pad_string(LSPString *dst, size_t pad)
417         {
418             pad *= 2;
419             while (pad--)
420                 if (!dst->append(' '))
421                     return false;
422             return true;
423         }
424 
to_string(LSPString * dst) const425         status_t Object::to_string(LSPString *dst) const
426         {
427             Object *_this = const_cast<Object *>(this);
428             return _this->to_string_padded(dst, 0);
429         }
430 
431     } /* namespace java */
432 } /* namespace lsp */
433