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