601 lines
16 KiB
C
601 lines
16 KiB
C
// SPDX-FileCopyrightText: 2021 deroad <wargio@libero.it>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include "class_attribute.h"
|
|
|
|
static ut8 *make_string(RzBuffer *buf, st64 size) {
|
|
ut8 *buffer;
|
|
st64 buffer_size = rz_buf_size(buf);
|
|
if (size < 1 || size >= buffer_size) {
|
|
return NULL;
|
|
}
|
|
|
|
buffer = (ut8 *)malloc(size + 1);
|
|
if (!buffer || rz_buf_read(buf, buffer, size) < (size - 1)) {
|
|
rz_warn_if_reached();
|
|
free(buffer);
|
|
return NULL;
|
|
}
|
|
buffer[size] = 0;
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static char *resolve_const_pool_index(ConstPool **pool, ut32 poolsize, ut32 index) {
|
|
const ConstPool *cpool;
|
|
if (index >= poolsize || !(cpool = pool[index])) {
|
|
return NULL;
|
|
}
|
|
return java_constant_pool_stringify(cpool);
|
|
}
|
|
|
|
bool java_attribute_set_unknown(Attribute *attr, RzBuffer *buf) {
|
|
attr->type = ATTRIBUTE_TYPE_UNKNOWN;
|
|
if (attr->attribute_length < 1) {
|
|
return true;
|
|
}
|
|
st64 size = (st64)attr->attribute_length;
|
|
rz_buf_seek(buf, size, RZ_BUF_CUR);
|
|
return true;
|
|
}
|
|
|
|
bool java_attribute_set_constantvalue(Attribute *attr, RzBuffer *buf) {
|
|
rz_warn_if_fail(attr->attribute_length == 2);
|
|
AttributeConstantValue *acv = RZ_NEW0(AttributeConstantValue);
|
|
if (!acv) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
|
|
if (!rz_buf_read_be16(buf, &acv->index)) {
|
|
free(acv);
|
|
return false;
|
|
}
|
|
|
|
attr->type = ATTRIBUTE_TYPE_CONSTANTVALUE;
|
|
attr->info = (void *)acv;
|
|
return true;
|
|
}
|
|
|
|
bool java_attribute_set_code(ConstPool **pool, ut32 poolsize, Attribute *attr, RzBuffer *buf, bool is_oak) {
|
|
AttributeCode *ac = RZ_NEW0(AttributeCode);
|
|
if (!ac) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
|
|
if (is_oak) {
|
|
ut8 max_stack;
|
|
if (!rz_buf_read8(buf, &max_stack)) {
|
|
free(ac);
|
|
return false;
|
|
}
|
|
ac->max_stack = max_stack;
|
|
|
|
ut8 max_locals;
|
|
if (!rz_buf_read8(buf, &max_locals)) {
|
|
free(ac);
|
|
return false;
|
|
}
|
|
ac->max_locals = max_locals;
|
|
|
|
ut16 code_length;
|
|
if (!rz_buf_read_be16(buf, &code_length)) {
|
|
free(ac);
|
|
return false;
|
|
}
|
|
ac->code_length = code_length;
|
|
} else {
|
|
if (!rz_buf_read_be16(buf, &ac->max_stack) ||
|
|
!rz_buf_read_be16(buf, &ac->max_locals) ||
|
|
!rz_buf_read_be32(buf, &ac->code_length)) {
|
|
free(ac);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ac->code_offset = attr->offset + (is_oak ? 10 : 14); // 6 bytes for attribute + 8 as code
|
|
rz_buf_seek(buf, ac->code_length, RZ_BUF_CUR);
|
|
if (!rz_buf_read_be16(buf, &ac->exceptions_count)) {
|
|
free(ac);
|
|
return false;
|
|
}
|
|
|
|
if (ac->exceptions_count > 0) {
|
|
ac->exceptions = RZ_NEWS0(ExceptionTable, ac->exceptions_count);
|
|
if (!ac->exceptions) {
|
|
free(ac);
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
|
|
for (ut32 i = 0; i < ac->exceptions_count; ++i) {
|
|
if (!rz_buf_read_be16(buf, &ac->exceptions[i].start_pc) ||
|
|
!rz_buf_read_be16(buf, &ac->exceptions[i].end_pc) ||
|
|
!rz_buf_read_be16(buf, &ac->exceptions[i].handler_pc) ||
|
|
!rz_buf_read_be16(buf, &ac->exceptions[i].catch_type)) {
|
|
free(ac);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rz_buf_read_be16(buf, &ac->attributes_count)) {
|
|
free(ac->exceptions);
|
|
free(ac);
|
|
return false;
|
|
}
|
|
|
|
if (ac->attributes_count > 0) {
|
|
ac->attributes = RZ_NEWS0(Attribute *, ac->attributes_count);
|
|
if (!ac->attributes) {
|
|
free(ac->exceptions);
|
|
free(ac);
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
|
|
for (ut32 i = 0; i < ac->attributes_count; ++i) {
|
|
Attribute *attr = java_attribute_new(buf, UT64_MAX);
|
|
if (attr && java_attribute_resolve(pool, poolsize, attr, buf, false)) {
|
|
ac->attributes[i] = attr;
|
|
} else {
|
|
java_attribute_free(attr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
attr->type = ATTRIBUTE_TYPE_CODE;
|
|
attr->info = (void *)ac;
|
|
return true;
|
|
}
|
|
|
|
bool java_attribute_set_sourcefile(Attribute *attr, RzBuffer *buf) {
|
|
rz_warn_if_fail(attr->attribute_length == 2);
|
|
AttributeSourceFile *asf = RZ_NEW0(AttributeSourceFile);
|
|
if (!asf) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
if (!rz_buf_read_be16(buf, &asf->index)) {
|
|
free(asf);
|
|
return false;
|
|
}
|
|
|
|
attr->type = ATTRIBUTE_TYPE_SOURCEFILE;
|
|
attr->info = (void *)asf;
|
|
return true;
|
|
}
|
|
|
|
bool java_attribute_set_sourcedebugextension(Attribute *attr, RzBuffer *buf) {
|
|
attr->type = ATTRIBUTE_TYPE_SOURCEDEBUGEXTENSION;
|
|
if (attr->attribute_length < 1) {
|
|
return true;
|
|
}
|
|
st64 size = (st64)attr->attribute_length;
|
|
/*
|
|
The debug_extension array holds a string, which must be in UTF-8 format. There is no terminating zero byte.
|
|
The string in the debug_extension item will be interpreted as extended debugging information.
|
|
*/
|
|
attr->info = make_string(buf, size);
|
|
if (!attr->info) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool java_attribute_set_linenumbertable(Attribute *attr, RzBuffer *buf) {
|
|
rz_warn_if_fail(attr->attribute_length >= 2);
|
|
AttributeLineNumberTable *alnt = RZ_NEW0(AttributeLineNumberTable);
|
|
if (!alnt) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
if (!rz_buf_read_be16(buf, &alnt->table_length)) {
|
|
free(alnt);
|
|
return false;
|
|
}
|
|
if (alnt->table_length > 0) {
|
|
alnt->table = RZ_NEWS0(LineNumberTable, alnt->table_length);
|
|
if (!alnt->table) {
|
|
free(alnt);
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
for (ut32 i = 0; i < alnt->table_length; ++i) {
|
|
if (!rz_buf_read_be16(buf, &alnt->table[i].start_pc) ||
|
|
!rz_buf_read_be16(buf, &alnt->table[i].line_number)) {
|
|
free(alnt->table);
|
|
free(alnt);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
attr->type = ATTRIBUTE_TYPE_LINENUMBERTABLE;
|
|
attr->info = (void *)alnt;
|
|
return true;
|
|
}
|
|
|
|
bool java_attribute_set_localvariabletable(Attribute *attr, RzBuffer *buf) {
|
|
rz_warn_if_fail(attr->attribute_length >= 2);
|
|
AttributeLocalVariableTable *alvt = RZ_NEW0(AttributeLocalVariableTable);
|
|
if (!alvt) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
if (!rz_buf_read_be16(buf, &alvt->table_length)) {
|
|
free(alvt);
|
|
return false;
|
|
}
|
|
if (alvt->table_length > 0) {
|
|
alvt->table = RZ_NEWS0(LocalVariableTable, alvt->table_length);
|
|
if (!alvt->table) {
|
|
free(alvt);
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
for (ut32 i = 0; i < alvt->table_length; ++i) {
|
|
if (!rz_buf_read_be16(buf, &alvt->table[i].start_pc) ||
|
|
!rz_buf_read_be16(buf, &alvt->table[i].length) ||
|
|
!rz_buf_read_be16(buf, &alvt->table[i].name_index) ||
|
|
!rz_buf_read_be16(buf, &alvt->table[i].descriptor_index) ||
|
|
!rz_buf_read_be16(buf, &alvt->table[i].index)) {
|
|
free(alvt->table);
|
|
free(alvt);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
attr->type = ATTRIBUTE_TYPE_LOCALVARIABLETABLE;
|
|
attr->info = (void *)alvt;
|
|
return true;
|
|
}
|
|
|
|
bool java_attribute_set_localvariabletypetable(Attribute *attr, RzBuffer *buf) {
|
|
rz_warn_if_fail(attr->attribute_length >= 2);
|
|
AttributeLocalVariableTypeTable *alvtt = RZ_NEW0(AttributeLocalVariableTypeTable);
|
|
if (!alvtt) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
if (!rz_buf_read_be16(buf, &alvtt->table_length)) {
|
|
free(alvtt);
|
|
return false;
|
|
}
|
|
if (alvtt->table_length > 0) {
|
|
alvtt->table = RZ_NEWS0(LocalVariableTypeTable, alvtt->table_length);
|
|
if (!alvtt->table) {
|
|
free(alvtt);
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
for (ut32 i = 0; i < alvtt->table_length; ++i) {
|
|
if (!rz_buf_read_be16(buf, &alvtt->table[i].start_pc) ||
|
|
!rz_buf_read_be16(buf, &alvtt->table[i].length) ||
|
|
!rz_buf_read_be16(buf, &alvtt->table[i].name_index) ||
|
|
!rz_buf_read_be16(buf, &alvtt->table[i].signature_index) ||
|
|
!rz_buf_read_be16(buf, &alvtt->table[i].index)) {
|
|
free(alvtt->table);
|
|
free(alvtt);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
attr->type = ATTRIBUTE_TYPE_LOCALVARIABLETYPETABLE;
|
|
attr->info = (void *)alvtt;
|
|
return true;
|
|
}
|
|
|
|
bool java_attribute_set_module(Attribute *attr, RzBuffer *buf) {
|
|
rz_warn_if_fail(attr->attribute_length >= 16);
|
|
AttributeModule *am = RZ_NEW0(AttributeModule);
|
|
if (!am) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
ut16 *tmp16 = NULL;
|
|
if (!rz_buf_read_be16(buf, &am->module_name_index) ||
|
|
!rz_buf_read_be16(buf, &am->module_flags) ||
|
|
!rz_buf_read_be16(buf, &am->module_version_index)) {
|
|
goto java_attribute_set_module_bad;
|
|
return false;
|
|
}
|
|
|
|
if (!rz_buf_read_be16(buf, &am->requires_count)) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
|
|
if (am->requires_count > 0) {
|
|
am->
|
|
requires
|
|
= RZ_NEWS0(ModuleRequire, am->requires_count);
|
|
if (!am->requires) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
for (ut32 i = 0; i < am->requires_count; ++i) {
|
|
if (!rz_buf_read_be16(buf, &am->requires[i].index) ||
|
|
!rz_buf_read_be16(buf, &am->requires[i].flags) ||
|
|
!rz_buf_read_be16(buf, &am->requires[i].version_index)) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rz_buf_read_be16(buf, &am->exports_count)) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
|
|
if (am->exports_count > 0) {
|
|
am->exports = RZ_NEWS0(ModuleExport, am->exports_count);
|
|
if (!am->exports) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
for (ut32 i = 0; i < am->exports_count; ++i) {
|
|
if (!rz_buf_read_be16(buf, &am->exports[i].index) ||
|
|
!rz_buf_read_be16(buf, &am->exports[i].flags) ||
|
|
!rz_buf_read_be16(buf, &am->exports[i].to_count)) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
tmp16 = RZ_NEWS0(ut16, am->exports[i].to_count);
|
|
if (!tmp16) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
am->exports[i].to_indices = tmp16;
|
|
for (ut32 k = 0; k < am->exports[i].to_count; ++k) {
|
|
if (!rz_buf_read_be16(buf, &tmp16[k])) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rz_buf_read_be16(buf, &am->opens_count)) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
|
|
if (am->opens_count > 0) {
|
|
am->opens = RZ_NEWS0(ModuleOpen, am->opens_count);
|
|
if (!am->opens) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
for (ut32 i = 0; i < am->opens_count; ++i) {
|
|
if (!rz_buf_read_be16(buf, &am->opens[i].index) ||
|
|
!rz_buf_read_be16(buf, &am->opens[i].flags) ||
|
|
!rz_buf_read_be16(buf, &am->opens[i].to_count)) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
tmp16 = RZ_NEWS0(ut16, am->opens[i].to_count);
|
|
if (!tmp16) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
am->opens[i].to_indices = tmp16;
|
|
for (ut32 k = 0; k < am->opens[i].to_count; ++k) {
|
|
if (!rz_buf_read_be16(buf, &tmp16[k])) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rz_buf_read_be16(buf, &am->uses_count)) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
|
|
if (am->uses_count > 0) {
|
|
tmp16 = RZ_NEWS0(ut16, am->uses_count);
|
|
if (!tmp16) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
am->uses_index = tmp16;
|
|
for (ut32 i = 0; i < am->uses_count; ++i) {
|
|
if (!rz_buf_read_be16(buf, &tmp16[i])) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rz_buf_read_be16(buf, &am->provides_count)) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
|
|
if (am->provides_count > 0) {
|
|
am->provides = RZ_NEWS0(ModuleProvide, am->provides_count);
|
|
if (!am->provides) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
for (ut32 i = 0; i < am->provides_count; ++i) {
|
|
if (!rz_buf_read_be16(buf, &am->provides[i].index) ||
|
|
!rz_buf_read_be16(buf, &am->provides[i].with_count)) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
tmp16 = RZ_NEWS0(ut16, am->provides[i].with_count);
|
|
if (!tmp16) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
am->provides[i].with_indices = tmp16;
|
|
for (ut32 k = 0; k < am->provides[i].with_count; ++k) {
|
|
if (!rz_buf_read_be16(buf, &tmp16[k])) {
|
|
goto java_attribute_set_module_bad;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
attr->type = ATTRIBUTE_TYPE_MODULE;
|
|
attr->info = (void *)am;
|
|
return true;
|
|
|
|
java_attribute_set_module_bad:
|
|
if (am->exports) {
|
|
for (ut32 i = 0; i < am->exports_count; ++i) {
|
|
free(am->exports[i].to_indices);
|
|
}
|
|
}
|
|
if (am->opens) {
|
|
for (ut32 i = 0; i < am->opens_count; ++i) {
|
|
free(am->opens[i].to_indices);
|
|
}
|
|
}
|
|
if (am->provides) {
|
|
for (ut32 i = 0; i < am->provides_count; ++i) {
|
|
free(am->provides[i].with_indices);
|
|
}
|
|
}
|
|
free(am->uses_index);
|
|
free(am->exports);
|
|
free(am->requires);
|
|
free(am);
|
|
return false;
|
|
}
|
|
|
|
bool java_attribute_set_modulepackages(Attribute *attr, RzBuffer *buf) {
|
|
rz_warn_if_fail(attr->attribute_length >= 2);
|
|
AttributeModulePackages *amp = RZ_NEW0(AttributeModulePackages);
|
|
if (!amp) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
|
|
if (!rz_buf_read_be16(buf, &->package_count)) {
|
|
free(amp);
|
|
return false;
|
|
}
|
|
|
|
if (amp->package_count > 0) {
|
|
amp->package_index = RZ_NEWS0(ut16, amp->package_count);
|
|
if (!amp->package_index) {
|
|
free(amp);
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
for (ut32 k = 0; k < amp->package_count; ++k) {
|
|
if (!rz_buf_read_be16(buf, &->package_index[k])) {
|
|
free(amp->package_index);
|
|
free(amp);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
attr->type = ATTRIBUTE_TYPE_MODULEPACKAGES;
|
|
attr->info = (void *)amp;
|
|
return true;
|
|
}
|
|
|
|
bool java_attribute_set_modulemainclass(Attribute *attr, RzBuffer *buf) {
|
|
rz_warn_if_fail(attr->attribute_length == 2);
|
|
AttributeModuleMainClass *ammc = RZ_NEW0(AttributeModuleMainClass);
|
|
if (!ammc) {
|
|
rz_warn_if_reached();
|
|
return false;
|
|
}
|
|
if (!rz_buf_read_be16(buf, &ammc->main_class_index)) {
|
|
free(ammc);
|
|
return false;
|
|
}
|
|
attr->type = ATTRIBUTE_TYPE_MODULEMAINCLASS;
|
|
attr->info = (void *)ammc;
|
|
return true;
|
|
}
|
|
|
|
bool java_attribute_resolve(ConstPool **pool, ut32 poolsize, Attribute *attr, RzBuffer *buf, bool is_oak) {
|
|
char *name = resolve_const_pool_index(pool, poolsize, attr->attribute_name_index);
|
|
if (!name) {
|
|
return false;
|
|
}
|
|
|
|
bool result = false;
|
|
if (!strcmp(name, "ConstantValue")) {
|
|
result = java_attribute_set_constantvalue(attr, buf);
|
|
} else if (!strcmp(name, "Code")) {
|
|
result = java_attribute_set_code(pool, poolsize, attr, buf, is_oak);
|
|
} else if (!strcmp(name, "SourceFile")) {
|
|
result = java_attribute_set_sourcefile(attr, buf);
|
|
} else if (!strcmp(name, "SourceDebugExtension")) {
|
|
result = java_attribute_set_sourcedebugextension(attr, buf);
|
|
} else if (!strcmp(name, "LineNumberTable")) {
|
|
result = java_attribute_set_linenumbertable(attr, buf);
|
|
} else if (!strcmp(name, "LocalVariableTable")) {
|
|
result = java_attribute_set_localvariabletable(attr, buf);
|
|
} else if (!strcmp(name, "LocalVariableTypeTable")) {
|
|
result = java_attribute_set_localvariabletypetable(attr, buf);
|
|
} else if (!strcmp(name, "Module")) {
|
|
result = java_attribute_set_module(attr, buf);
|
|
} else if (!strcmp(name, "ModulePackages")) {
|
|
result = java_attribute_set_modulepackages(attr, buf);
|
|
} else if (!strcmp(name, "ModuleMainClass")) {
|
|
result = java_attribute_set_modulemainclass(attr, buf);
|
|
}
|
|
|
|
if (!result) {
|
|
result = java_attribute_set_unknown(attr, buf);
|
|
}
|
|
free(name);
|
|
return result;
|
|
}
|
|
|
|
Attribute *java_attribute_new(RzBuffer *buf, ut64 offset) {
|
|
Attribute *attr = RZ_NEW0(Attribute);
|
|
rz_return_val_if_fail(attr, NULL);
|
|
|
|
attr->offset = offset;
|
|
|
|
if (!rz_buf_read_be16(buf, &attr->attribute_name_index)) {
|
|
free(attr);
|
|
return NULL;
|
|
}
|
|
|
|
if (!rz_buf_read_be32(buf, &attr->attribute_length)) {
|
|
free(attr);
|
|
return NULL;
|
|
}
|
|
|
|
return attr;
|
|
}
|
|
|
|
void java_attribute_free(Attribute *attr) {
|
|
if (!attr || !attr->info) {
|
|
free(attr);
|
|
return;
|
|
}
|
|
if (attr->type == ATTRIBUTE_TYPE_CODE) {
|
|
AttributeCode *ac = (AttributeCode *)attr->info;
|
|
free(ac->exceptions);
|
|
if (ac->attributes) {
|
|
for (ut32 i = 0; i < ac->attributes_count; ++i) {
|
|
java_attribute_free(ac->attributes[i]);
|
|
}
|
|
free(ac->attributes);
|
|
}
|
|
} else if (attr->type == ATTRIBUTE_TYPE_LINENUMBERTABLE) {
|
|
AttributeLineNumberTable *alnt = (AttributeLineNumberTable *)attr->info;
|
|
free(alnt->table);
|
|
} else if (attr->type == ATTRIBUTE_TYPE_LOCALVARIABLETABLE) {
|
|
AttributeLocalVariableTable *alvt = (AttributeLocalVariableTable *)attr->info;
|
|
free(alvt->table);
|
|
} else if (attr->type == ATTRIBUTE_TYPE_MODULE) {
|
|
AttributeModule *am = (AttributeModule *)attr->info;
|
|
for (ut32 i = 0; i < am->exports_count; ++i) {
|
|
free(am->exports[i].to_indices);
|
|
}
|
|
for (ut32 i = 0; i < am->opens_count; ++i) {
|
|
free(am->opens[i].to_indices);
|
|
}
|
|
for (ut32 i = 0; i < am->provides_count; ++i) {
|
|
free(am->provides[i].with_indices);
|
|
}
|
|
free(am->uses_index);
|
|
free(am->exports);
|
|
free(am->requires);
|
|
} else if (attr->type == ATTRIBUTE_TYPE_MODULEPACKAGES) {
|
|
AttributeModulePackages *amp = (AttributeModulePackages *)attr->info;
|
|
free(amp->package_index);
|
|
}
|
|
free(attr->info);
|
|
free(attr);
|
|
}
|