mirror of
https://github.com/cemu-project/idapython.git
synced 2024-11-27 19:44:18 +01:00
1096 lines
31 KiB
C++
1096 lines
31 KiB
C++
#ifndef __PYCVT__
|
|
#define __PYCVT__
|
|
|
|
//<code(py_idaapi)>
|
|
|
|
//------------------------------------------------------------------------
|
|
// String constants used
|
|
static const char S_PY_IDCCVT_VALUE_ATTR[] = "__idc_cvt_value__";
|
|
static const char S_PY_IDCCVT_ID_ATTR[] = "__idc_cvt_id__";
|
|
static const char S_PY_IDC_OPAQUE_T[] = "py_idc_cvt_helper_t";
|
|
static const char S_PY_IDC_GLOBAL_VAR_FMT[] = "__py_cvt_gvar_%d";
|
|
|
|
// Constants used by get_idaapi_class_reference()
|
|
#define PY_CLSID_CVT_INT64 0
|
|
#define PY_CLSID_APPCALL_SKEL_OBJ 1
|
|
#define PY_CLSID_CVT_BYREF 2
|
|
#define PY_CLSID_LAST 3
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Use these macros to define script<->C fields
|
|
#define DEFINE_SCFIELDX(name, type, is_opt) { #name, type, qoffsetof(CUR_STRUC, name), is_opt }
|
|
#define DEFINE_SCFIELD(name, type) DEFINE_SCFIELDX(name, type, 0)
|
|
#define DEFINE_SCFIELD_OPT(name, type) DEFINE_SCFIELDX(name, type, 1)
|
|
|
|
//---------------------------------------------------------------------------
|
|
enum scfield_types_t
|
|
{
|
|
// Numeric fields
|
|
FT_FIRST_NUM,
|
|
FT_INT,
|
|
FT_SIZET,
|
|
FT_SSIZET,
|
|
FT_NUM16,
|
|
FT_NUM32,
|
|
FT_LAST_NUM,
|
|
// String field
|
|
FT_STR,
|
|
FT_CHAR,
|
|
// Object fields
|
|
FT_ARR,
|
|
// Allocated array of strings
|
|
FT_STRARR,
|
|
// Allocated array of 16bit numbers
|
|
FT_NUM16ARR,
|
|
// Fixed size character array. The size must be passed in the definition
|
|
FT_CHRARR_STATIC,
|
|
};
|
|
|
|
//---------------------------------------------------------------------------
|
|
struct scfld_t
|
|
{
|
|
const char *field_name;
|
|
uint32 field_type;
|
|
size_t field_offs;
|
|
bool is_optional;
|
|
};
|
|
|
|
#define FT_VALUE_MASK 0xFFFF0000
|
|
// Possible return values of conversion functions
|
|
#define FT_NOT_FOUND -1
|
|
#define FT_BAD_TYPE -2
|
|
#define FT_OK 1
|
|
|
|
//-----------------------------------------------------------------------
|
|
class pycvt_t
|
|
{
|
|
struct attr_t
|
|
{
|
|
qstring str;
|
|
uint64 u64;
|
|
// User is responsible to release this attribute when done
|
|
PyObject *py_obj;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------
|
|
static int get_attr(
|
|
PyObject *py_obj,
|
|
const char *attrname,
|
|
int ft,
|
|
attr_t &val)
|
|
{
|
|
PyObject *py_attr;
|
|
if ( (py_attr = PyW_TryGetAttrString(py_obj, attrname)) == NULL )
|
|
return FT_NOT_FOUND;
|
|
|
|
int cvt = FT_OK;
|
|
if ( ft == FT_STR || ft == FT_CHAR && PyString_Check(py_attr) )
|
|
val.str = PyString_AsString(py_attr);
|
|
else if ( (ft > FT_FIRST_NUM && ft < FT_LAST_NUM) && PyW_GetNumber(py_attr, &val.u64) )
|
|
; // nothing to be done
|
|
// A string array?
|
|
else if ( (ft == FT_STRARR || ft == FT_NUM16ARR || ft == FT_CHRARR_STATIC )
|
|
&& (PyList_CheckExact(py_attr) || PyW_IsSequenceType(py_attr)) )
|
|
{
|
|
// Return a reference to the attribute
|
|
val.py_obj = py_attr;
|
|
// Do not decrement the reference to this attribute
|
|
py_attr = NULL;
|
|
}
|
|
else
|
|
cvt = FT_BAD_TYPE;
|
|
Py_XDECREF(py_attr);
|
|
return cvt;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
static int idaapi make_str_list_cb(
|
|
PyObject *py_item,
|
|
Py_ssize_t index,
|
|
void *ud)
|
|
{
|
|
if ( !PyString_Check(py_item) )
|
|
return CIP_FAILED;
|
|
char **a = (char **)ud;
|
|
a[index] = qstrdup(PyString_AsString(py_item));
|
|
return CIP_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Converts an IDC list of strings to a C string list
|
|
static Py_ssize_t str_list_to_str_arr(
|
|
PyObject *py_list,
|
|
char ***arr)
|
|
{
|
|
// Take the size
|
|
Py_ssize_t size = pyvar_walk_list(py_list);
|
|
|
|
// Allocate a buffer
|
|
char **a = (char **)qalloc((size + 1) * sizeof(char *));
|
|
|
|
// Walk and populate
|
|
size = pyvar_walk_list(py_list, make_str_list_cb, a);
|
|
|
|
// Make the list NULL terminated
|
|
a[size] = NULL;
|
|
|
|
// Return the list to the user
|
|
*arr = a;
|
|
|
|
// Return the size of items processed
|
|
return size;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
typedef qvector<uint64> uint64vec_t;
|
|
static int idaapi make_int_list(
|
|
PyObject *py_item,
|
|
Py_ssize_t /*index*/,
|
|
void *ud)
|
|
{
|
|
uint64 val;
|
|
if ( !PyW_GetNumber(py_item, &val) )
|
|
return CIP_FAILED;
|
|
uint64vec_t *vec = (uint64vec_t *)ud;
|
|
vec->push_back(val);
|
|
return CIP_OK;
|
|
}
|
|
|
|
public:
|
|
//-----------------------------------------------------------------------
|
|
// Frees a NULL terminated list of fields
|
|
static void free_fields(
|
|
const scfld_t *fields,
|
|
void *store_area)
|
|
{
|
|
for ( int i=0; ; i++ )
|
|
{
|
|
// End of list?
|
|
const scfld_t &fd = fields[i];
|
|
if ( fd.field_name == NULL )
|
|
break;
|
|
|
|
void *store = (void *)((char *)store_area + fd.field_offs);
|
|
int ft = fd.field_type & ~FT_VALUE_MASK;
|
|
switch ( ft )
|
|
{
|
|
case FT_STR: // Simple string
|
|
{
|
|
char **s = (char **)store;
|
|
if ( *s != NULL )
|
|
{
|
|
qfree(*s);
|
|
*s = NULL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FT_STRARR: // Array of strings
|
|
{
|
|
char ***op = (char ***)store, **p = *op;
|
|
while ( *p != NULL )
|
|
qfree((void *)*p++);
|
|
qfree(*op);
|
|
*op = NULL;
|
|
}
|
|
break;
|
|
|
|
case FT_NUM16ARR:
|
|
{
|
|
uint16 **arr = (uint16 **)store;
|
|
if ( *arr != NULL )
|
|
{
|
|
qfree(*arr);
|
|
*arr = NULL;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Converts from a C structure to Python
|
|
static int from_c(
|
|
const scfld_t *fields,
|
|
void *read_area,
|
|
PyObject *py_obj)
|
|
{
|
|
PyObject *py_attr;
|
|
int i;
|
|
bool ok = false;
|
|
for ( i=0; ; i++ )
|
|
{
|
|
// End of list?
|
|
const scfld_t &fd = fields[i];
|
|
if ( fd.field_name == NULL )
|
|
{
|
|
ok = true;
|
|
break;
|
|
}
|
|
|
|
// Point to structure member
|
|
int ft = fd.field_type & ~FT_VALUE_MASK;
|
|
void *read = (void *)((char *)read_area + fd.field_offs);
|
|
// Create the python attribute properly
|
|
if ( ft > FT_FIRST_NUM && ft < FT_LAST_NUM )
|
|
{
|
|
if ( ft == FT_NUM16 )
|
|
py_attr = Py_BuildValue("H", *(uint16 *)read);
|
|
else if ( ft == FT_NUM32 )
|
|
py_attr = Py_BuildValue("I", *(uint32 *)read);
|
|
else if ( ft == FT_INT )
|
|
py_attr = Py_BuildValue("i", *(int *)read);
|
|
else if ( ft == FT_SIZET )
|
|
py_attr = Py_BuildValue(PY_FMT64,*(size_t *)read);
|
|
else if ( ft == FT_SSIZET )
|
|
py_attr = Py_BuildValue(PY_SFMT64,*(ssize_t *)read);
|
|
}
|
|
else if ( ft == FT_STR || ft == FT_CHAR )
|
|
{
|
|
if ( ft == FT_STR )
|
|
py_attr = PyString_FromString(*(char **)read);
|
|
else
|
|
py_attr = Py_BuildValue("c", *(char *)read);
|
|
}
|
|
else if ( ft == FT_STRARR )
|
|
{
|
|
char **arr = *(char ***)read;
|
|
py_attr = PyList_New(0);
|
|
while ( *arr != NULL )
|
|
PyList_Append(py_attr, PyString_FromString(*arr++));
|
|
}
|
|
else
|
|
continue;
|
|
PyObject_SetAttrString(py_obj, fd.field_name, py_attr);
|
|
Py_XDECREF(py_attr);
|
|
}
|
|
return ok ? -1 : i;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Converts fields from IDC and field description into a C structure
|
|
// If 'use_extlang' is specified, then the passed idc_obj is considered
|
|
// to be an opaque object and thus can be queried only through extlang
|
|
static int from_script(
|
|
const scfld_t *fields,
|
|
void *store_area,
|
|
PyObject *py_obj)
|
|
{
|
|
int i;
|
|
bool ok = false;
|
|
attr_t attr;
|
|
for ( i=0; ; i++ )
|
|
{
|
|
// End of list?
|
|
const scfld_t &fd = fields[i];
|
|
if ( fd.field_name == NULL )
|
|
{
|
|
ok = true;
|
|
break;
|
|
}
|
|
|
|
// Get field type
|
|
int ft = fd.field_type & ~FT_VALUE_MASK;
|
|
|
|
// Point to structure member
|
|
void *store = (void *)((char *)store_area + fd.field_offs);
|
|
|
|
// Retrieve attribute and type
|
|
int cvt = get_attr(py_obj, fd.field_name, ft, attr);
|
|
|
|
// Attribute not found?
|
|
if ( cvt == FT_NOT_FOUND )
|
|
{
|
|
// Skip optional fields
|
|
if ( fd.is_optional )
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
if ( ft == FT_STR )
|
|
*(char **)store = qstrdup(attr.str.c_str());
|
|
else if ( ft == FT_NUM32 )
|
|
*(uint32 *)store = uint32(attr.u64);
|
|
else if ( ft == FT_NUM16 )
|
|
*(uint16 *)store = attr.u64 & 0xffff;
|
|
else if ( ft == FT_INT )
|
|
*(int *)store = int(attr.u64);
|
|
else if ( ft == FT_SIZET )
|
|
*(size_t *)store = size_t(attr.u64);
|
|
else if ( ft == FT_SSIZET )
|
|
*(ssize_t *)store = ssize_t(attr.u64);
|
|
else if ( ft == FT_CHAR )
|
|
*(char *)store = *attr.str.c_str();
|
|
else if ( ft == FT_STRARR )
|
|
{
|
|
str_list_to_str_arr(attr.py_obj, (char ***)store);
|
|
Py_DECREF(attr.py_obj);
|
|
}
|
|
else if ( ft == FT_CHRARR_STATIC )
|
|
{
|
|
size_t sz = (fd.field_type & FT_VALUE_MASK) >> 16;
|
|
if ( sz == 0 )
|
|
break;
|
|
uint64vec_t w;
|
|
char *a = (char *) store;
|
|
if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) )
|
|
{
|
|
sz = qmin(w.size(), sz);
|
|
for ( size_t i=0; i < sz; i++ )
|
|
a[i] = w[i] & 0xFF;
|
|
}
|
|
}
|
|
else if ( ft == FT_NUM16ARR )
|
|
{
|
|
uint64vec_t w;
|
|
if ( pyvar_walk_list(attr.py_obj, make_int_list, &w) > 0 )
|
|
{
|
|
size_t max_sz = (fd.field_type & FT_VALUE_MASK) >> 16;
|
|
bool zero_term;
|
|
if ( max_sz == 0 )
|
|
{
|
|
zero_term = true;
|
|
max_sz = w.size();
|
|
}
|
|
else
|
|
{
|
|
zero_term = false;
|
|
max_sz = qmin(max_sz, w.size());
|
|
}
|
|
// Allocate as much as we parsed elements
|
|
// Add one more element if list was zero terminated
|
|
uint16 *a = (uint16 *)qalloc(sizeof(uint16) * (max_sz + (zero_term ? 1 : 0))) ;
|
|
for ( size_t i=0; i < max_sz; i++ )
|
|
a[i] = w[i] & 0xFF;
|
|
|
|
if ( zero_term )
|
|
a[max_sz] = 0;
|
|
*(uint16 **)store = a;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Unsupported field type!
|
|
break;
|
|
}
|
|
}
|
|
return ok ? -1 : i;
|
|
}
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
Py_ssize_t pyvar_walk_list(
|
|
PyObject *py_list,
|
|
int (idaapi *cb)(PyObject *py_item, Py_ssize_t index, void *ud),
|
|
void *ud)
|
|
{
|
|
if ( !PyList_CheckExact(py_list) && !PyW_IsSequenceType(py_list) )
|
|
return CIP_FAILED;
|
|
|
|
bool is_seq = !PyList_CheckExact(py_list);
|
|
Py_ssize_t size = is_seq ? PySequence_Size(py_list) : PyList_Size(py_list);
|
|
|
|
if ( cb == NULL )
|
|
return size;
|
|
|
|
Py_ssize_t i;
|
|
for ( i=0; i<size; i++ )
|
|
{
|
|
// Get the item
|
|
PyObject *py_item = is_seq ? PySequence_GetItem(py_list, i) : PyList_GetItem(py_list, i);
|
|
if ( py_item == NULL )
|
|
break;
|
|
|
|
int r = cb(py_item, i, ud);
|
|
|
|
// Decrement reference (if needed)
|
|
if ( r != CIP_OK_NODECREF && is_seq )
|
|
Py_DECREF(py_item); // Only sequences require us to decrement the reference
|
|
if ( r < CIP_OK )
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
PyObject *PyW_IntVecToPyList(const intvec_t &intvec)
|
|
{
|
|
size_t c = intvec.size();
|
|
PyObject *py_list = PyList_New(c);
|
|
|
|
for ( size_t i=0; i<c; i++ )
|
|
PyList_SetItem(py_list, i, PyInt_FromLong(intvec[i]));
|
|
|
|
return py_list;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static int idaapi pylist_to_intvec_cb(
|
|
PyObject *py_item,
|
|
Py_ssize_t /*index*/,
|
|
void *ud)
|
|
{
|
|
intvec_t &intvec = *(intvec_t *)ud;
|
|
uint64 num;
|
|
if (!PyW_GetNumber(py_item, &num))
|
|
num = 0;
|
|
|
|
intvec.push_back(int(num));
|
|
return CIP_OK;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool PyW_PyListToIntVec(PyObject *py_list, intvec_t &intvec)
|
|
{
|
|
intvec.clear();
|
|
return pyvar_walk_list(py_list, pylist_to_intvec_cb, &intvec) != CIP_FAILED;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
static int idaapi pylist_to_strvec_cb(
|
|
PyObject *py_item,
|
|
Py_ssize_t /*index*/,
|
|
void *ud)
|
|
{
|
|
qstrvec_t &strvec = *(qstrvec_t *)ud;
|
|
const char *s;
|
|
if ( !PyString_Check(py_item) )
|
|
s = "";
|
|
else
|
|
s = PyString_AsString(py_item);
|
|
|
|
strvec.push_back(s);
|
|
return CIP_OK;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool PyW_PyListToStrVec(PyObject *py_list, qstrvec_t &strvec)
|
|
{
|
|
strvec.clear();
|
|
return pyvar_walk_list(py_list, pylist_to_strvec_cb, &strvec) != CIP_FAILED;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Checks if the given py_var is a special PyIdc_cvt_helper object.
|
|
// It does that by examining the magic attribute and returns its numeric value.
|
|
// It returns -1 if the object is not a recognized helper object.
|
|
// Any Python object can be treated as an cvt object if this attribute is created.
|
|
static int get_pyidc_cvt_type(PyObject *py_var)
|
|
{
|
|
// Check if this our special by reference object
|
|
PyObject *attr = PyW_TryGetAttrString(py_var, S_PY_IDCCVT_ID_ATTR);
|
|
if ( attr == NULL )
|
|
return -1;
|
|
|
|
if ( !(PyInt_Check(attr) || PyLong_Check(attr)) )
|
|
{
|
|
Py_DECREF(attr);
|
|
return -1;
|
|
}
|
|
int r = (int)PyInt_AsLong(attr);
|
|
Py_DECREF(attr);
|
|
return r;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Utility function to convert a python object to an IDC object
|
|
// and sets a python exception on failure.
|
|
bool convert_pyobj_to_idc_exc(PyObject *py_obj, idc_value_t *idc_obj)
|
|
{
|
|
int sn = 0;
|
|
if ( pyvar_to_idcvar(py_obj, idc_obj, &sn) < CIP_OK )
|
|
{
|
|
PyErr_SetString(PyExc_ValueError, "Could not convert Python object to IDC object!");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
static idc_class_t *get_py_idc_cvt_opaque()
|
|
{
|
|
return find_idc_class(S_PY_IDC_OPAQUE_T);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Utility function to create opaque / convertible Python <-> IDC variables
|
|
// The referred Python variable will have its reference increased
|
|
static bool create_py_idc_opaque_obj(PyObject *py_var, idc_value_t *idc_var)
|
|
{
|
|
// Create an IDC object of this special helper class
|
|
if ( VarObject(idc_var, get_py_idc_cvt_opaque()) != eOk )
|
|
return false;
|
|
|
|
// Store the CVT id
|
|
idc_value_t idc_val;
|
|
idc_val.set_long(PY_ICID_OPAQUE);
|
|
VarSetAttr(idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val);
|
|
|
|
// Store the value as a PVOID referencing the given Python object
|
|
idc_val.set_pvoid(py_var);
|
|
VarSetAttr(idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val);
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// IDC Opaque object destructor: when the IDC object dies we kill the
|
|
// opaque Python object along with it
|
|
static const char py_idc_cvt_helper_dtor_args[] = { VT_OBJ, 0 };
|
|
static error_t idaapi py_idc_opaque_dtor(
|
|
idc_value_t *argv,
|
|
idc_value_t * /*res*/)
|
|
{
|
|
// Get the value from the object
|
|
idc_value_t idc_val;
|
|
VarGetAttr(&argv[0], S_PY_IDCCVT_VALUE_ATTR, &idc_val);
|
|
|
|
// Extract the Python object reference
|
|
PyObject *py_obj = (PyObject *)idc_val.pvoid;
|
|
|
|
// Decrease its reference (and eventually destroy it)
|
|
Py_DECREF(py_obj);
|
|
|
|
return eOk;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Converts a Python variable into an IDC variable
|
|
// This function returns on one CIP_XXXX
|
|
int pyvar_to_idcvar(
|
|
PyObject *py_var,
|
|
idc_value_t *idc_var,
|
|
int *gvar_sn)
|
|
{
|
|
PyObject *attr;
|
|
// None / NULL
|
|
if ( py_var == NULL || py_var == Py_None )
|
|
idc_var->set_long(0);
|
|
// Numbers?
|
|
else if ( PyW_GetNumberAsIDC(py_var, idc_var) )
|
|
return CIP_OK;
|
|
// String
|
|
else if ( PyString_Check(py_var) )
|
|
idc_var->_set_string(PyString_AsString(py_var), PyString_Size(py_var));
|
|
// Float
|
|
else if ( PyBool_Check(py_var) )
|
|
idc_var->set_long(py_var == Py_True ? 1 : 0);
|
|
// Boolean
|
|
else if ( PyFloat_Check(py_var) )
|
|
{
|
|
double dresult = PyFloat_AsDouble(py_var);
|
|
ieee_realcvt((void *)&dresult, idc_var->e, 3);
|
|
idc_var->vtype = VT_FLOAT;
|
|
}
|
|
// void*
|
|
else if ( PyCObject_Check(py_var) )
|
|
idc_var->set_pvoid(PyCObject_AsVoidPtr(py_var));
|
|
// Is it a Python list?
|
|
else if ( PyList_CheckExact(py_var) || PyW_IsSequenceType(py_var) )
|
|
{
|
|
// Create the object
|
|
VarObject(idc_var);
|
|
|
|
// Determine list size and type
|
|
bool is_seq = !PyList_CheckExact(py_var);
|
|
Py_ssize_t size = is_seq ? PySequence_Size(py_var) : PyList_Size(py_var);
|
|
bool ok = true;
|
|
qstring attr_name;
|
|
|
|
// Convert each item
|
|
for ( Py_ssize_t i=0; i<size; i++ )
|
|
{
|
|
// Get the item
|
|
PyObject *py_item = is_seq ? PySequence_GetItem(py_var, i) : PyList_GetItem(py_var, i);
|
|
|
|
// Convert the item into an IDC variable
|
|
idc_value_t v;
|
|
ok = pyvar_to_idcvar(py_item, &v, gvar_sn) >= CIP_OK;
|
|
if ( ok )
|
|
{
|
|
// Form the attribute name
|
|
PyObject *py_int = PyInt_FromSsize_t(i);
|
|
ok = PyW_ObjectToString(py_int, &attr_name);
|
|
if ( !ok )
|
|
break;
|
|
Py_DECREF(py_int);
|
|
// Store the attribute
|
|
VarSetAttr(idc_var, attr_name.c_str(), &v);
|
|
}
|
|
// Sequences return a new reference for GetItem()
|
|
if ( is_seq )
|
|
Py_DECREF(py_var);
|
|
if ( !ok )
|
|
break;
|
|
}
|
|
return ok ? CIP_OK : CIP_FAILED;
|
|
}
|
|
// Dictionary: we convert to an IDC object
|
|
else if ( PyDict_Check(py_var) )
|
|
{
|
|
// Create an empty IDC object
|
|
VarObject(idc_var);
|
|
|
|
// Get the dict.items() list
|
|
PyObject *py_items = PyDict_Items(py_var);
|
|
|
|
// Get the size of the list
|
|
qstring key_name;
|
|
bool ok = true;
|
|
Py_ssize_t size = PySequence_Size(py_items);
|
|
for ( Py_ssize_t i=0; i<size; i++ )
|
|
{
|
|
// Get item[i] -> (key, value)
|
|
PyObject *py_item = PyList_GetItem(py_items, i);
|
|
|
|
// Extract key/value
|
|
PyObject *key = PySequence_GetItem(py_item, 0);
|
|
PyObject *val = PySequence_GetItem(py_item, 1);
|
|
|
|
// Get key's string representation
|
|
PyW_ObjectToString(key, &key_name);
|
|
|
|
// Convert the attribute into an IDC value
|
|
idc_value_t v;
|
|
ok = pyvar_to_idcvar(val, &v, gvar_sn) >= CIP_OK;
|
|
if ( ok )
|
|
{
|
|
// Store the attribute
|
|
VarSetAttr(idc_var, key_name.c_str(), &v);
|
|
}
|
|
Py_XDECREF(key);
|
|
Py_XDECREF(val);
|
|
if ( !ok )
|
|
break;
|
|
}
|
|
// Decrement attribute reference
|
|
Py_DECREF(py_items);
|
|
return ok ? CIP_OK : CIP_FAILED;
|
|
}
|
|
// Possible function?
|
|
else if ( PyCallable_Check(py_var) )
|
|
{
|
|
idc_var->clear();
|
|
idc_var->vtype = VT_FUNC;
|
|
idc_var->funcidx = -1; // Does not apply
|
|
return CIP_OK;
|
|
}
|
|
// Objects:
|
|
// - pyidc_cvt objects: int64, byref, opaque
|
|
// - other python objects
|
|
else
|
|
{
|
|
// Get the type
|
|
int cvt_id = get_pyidc_cvt_type(py_var);
|
|
switch ( cvt_id )
|
|
{
|
|
//
|
|
// INT64
|
|
//
|
|
case PY_ICID_INT64:
|
|
// Get the value attribute
|
|
attr = PyW_TryGetAttrString(py_var, S_PY_IDCCVT_VALUE_ATTR);
|
|
if ( attr == NULL )
|
|
return false;
|
|
idc_var->set_int64(PyLong_AsLongLong(attr));
|
|
Py_DECREF(attr);
|
|
return CIP_OK;
|
|
//
|
|
// BYREF
|
|
//
|
|
case PY_ICID_BYREF:
|
|
{
|
|
// BYREF always require this parameter
|
|
if ( gvar_sn == NULL )
|
|
return CIP_FAILED;
|
|
|
|
// Get the value attribute
|
|
attr = PyW_TryGetAttrString(py_var, S_PY_IDCCVT_VALUE_ATTR);
|
|
if ( attr == NULL )
|
|
return CIP_FAILED;
|
|
|
|
// Create a global variable
|
|
char buf[MAXSTR];
|
|
qsnprintf(buf, sizeof(buf), S_PY_IDC_GLOBAL_VAR_FMT, *gvar_sn);
|
|
idc_value_t *gvar = add_idc_gvar(buf);
|
|
// Convert the python value into the IDC global variable
|
|
bool ok = pyvar_to_idcvar(attr, gvar, gvar_sn) >= CIP_OK;
|
|
if ( ok )
|
|
{
|
|
(*gvar_sn)++;
|
|
// Create a reference to this global variable
|
|
VarRef(idc_var, gvar);
|
|
}
|
|
Py_DECREF(attr);
|
|
return ok ? CIP_OK : CIP_FAILED;
|
|
}
|
|
//
|
|
// OPAQUE
|
|
//
|
|
case PY_ICID_OPAQUE:
|
|
{
|
|
if ( !create_py_idc_opaque_obj(py_var, idc_var) )
|
|
return CIP_FAILED;
|
|
return CIP_OK_NODECREF;
|
|
}
|
|
//
|
|
// Other objects
|
|
//
|
|
default:
|
|
// A normal object?
|
|
PyObject *py_dir = PyObject_Dir(py_var);
|
|
Py_ssize_t size = PyList_Size(py_dir);
|
|
if ( py_dir == NULL || !PyList_Check(py_dir) || size == 0 )
|
|
{
|
|
Py_XDECREF(py_dir);
|
|
return CIP_FAILED;
|
|
}
|
|
// Create the IDC object
|
|
VarObject(idc_var);
|
|
for ( Py_ssize_t i=0; i<size; i++ )
|
|
{
|
|
PyObject *item = PyList_GetItem(py_dir, i);
|
|
const char *field_name = PyString_AsString(item);
|
|
if ( field_name == NULL )
|
|
continue;
|
|
|
|
size_t len = strlen(field_name);
|
|
|
|
// Skip private attributes
|
|
if ( (len > 2 )
|
|
&& (strncmp(field_name, "__", 2) == 0 )
|
|
&& (strncmp(field_name+len-2, "__", 2) == 0) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
idc_value_t v;
|
|
// Get the non-private attribute from the object
|
|
attr = PyObject_GetAttrString(py_var, field_name);
|
|
if (attr == NULL
|
|
// Convert the attribute into an IDC value
|
|
|| pyvar_to_idcvar(attr, &v, gvar_sn) < CIP_OK)
|
|
{
|
|
Py_XDECREF(attr);
|
|
return CIP_FAILED;
|
|
}
|
|
|
|
// Store the attribute
|
|
VarSetAttr(idc_var, field_name, &v);
|
|
|
|
// Decrement attribute reference
|
|
Py_DECREF(attr);
|
|
}
|
|
}
|
|
}
|
|
return CIP_OK;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Converts an IDC variable to a Python variable
|
|
// If py_var points to an existing object then the object will be updated
|
|
// If py_var points to an existing immutable object then ZERO is returned
|
|
// Returns one of CIP_xxxx. Check pywraps.hpp
|
|
int idcvar_to_pyvar(
|
|
const idc_value_t &idc_var,
|
|
PyObject **py_var)
|
|
{
|
|
switch ( idc_var.vtype )
|
|
{
|
|
case VT_PVOID:
|
|
if ( *py_var == NULL )
|
|
*py_var = PyCObject_FromVoidPtr(idc_var.pvoid, NULL);
|
|
else
|
|
return CIP_IMMUTABLE;
|
|
break;
|
|
|
|
case VT_INT64:
|
|
{
|
|
// Recycle?
|
|
if ( *py_var != NULL )
|
|
{
|
|
// Recycling an int64 object?
|
|
int t = get_pyidc_cvt_type(*py_var);
|
|
if ( t != PY_ICID_INT64 )
|
|
return CIP_IMMUTABLE; // Cannot recycle immutable object
|
|
// Update the attribute
|
|
PyObject_SetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64));
|
|
return CIP_OK;
|
|
}
|
|
PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_CVT_INT64);
|
|
if ( py_cls == NULL )
|
|
return CIP_FAILED;
|
|
PYW_GIL_ENSURE;
|
|
*py_var = PyObject_CallFunctionObjArgs(py_cls, PyLong_FromLongLong(idc_var.i64), NULL);
|
|
PYW_GIL_RELEASE;
|
|
Py_DECREF(py_cls);
|
|
if ( PyW_GetError() || *py_var == NULL )
|
|
return CIP_FAILED;
|
|
break;
|
|
}
|
|
|
|
#if !defined(NO_OBSOLETE_FUNCS) || defined(__EXPR_SRC)
|
|
case VT_STR:
|
|
*py_var = PyString_FromString(idc_var.str);
|
|
break;
|
|
|
|
#endif
|
|
case VT_STR2:
|
|
if ( *py_var == NULL )
|
|
{
|
|
const qstring &s = idc_var.qstr();
|
|
*py_var = PyString_FromStringAndSize(s.begin(), s.length());
|
|
break;
|
|
}
|
|
else
|
|
return CIP_IMMUTABLE; // Cannot recycle immutable object
|
|
case VT_LONG:
|
|
// Cannot recycle immutable objects
|
|
if ( *py_var != NULL )
|
|
return CIP_IMMUTABLE;
|
|
#ifdef __EA64__
|
|
*py_var = PyLong_FromLongLong(idc_var.num);
|
|
#else
|
|
*py_var = PyLong_FromLong(idc_var.num);
|
|
#endif
|
|
break;
|
|
case VT_FLOAT:
|
|
if ( *py_var == NULL )
|
|
{
|
|
double x;
|
|
if ( ph.realcvt(&x, (uint16 *)idc_var.e, (sizeof(x)/2-1)|010) != 0 )
|
|
INTERR(30160);
|
|
|
|
*py_var = PyFloat_FromDouble(x);
|
|
break;
|
|
}
|
|
else
|
|
return CIP_IMMUTABLE;
|
|
|
|
case VT_REF:
|
|
{
|
|
if ( *py_var == NULL )
|
|
{
|
|
PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_CVT_BYREF);
|
|
if ( py_cls == NULL )
|
|
return CIP_FAILED;
|
|
|
|
// Create a byref object with None value. We populate it later
|
|
PYW_GIL_ENSURE;
|
|
*py_var = PyObject_CallFunctionObjArgs(py_cls, Py_None, NULL);
|
|
PYW_GIL_RELEASE;
|
|
Py_DECREF(py_cls);
|
|
if ( PyW_GetError() || *py_var == NULL )
|
|
return CIP_FAILED;
|
|
}
|
|
int t = *py_var == NULL ? -1 : get_pyidc_cvt_type(*py_var);
|
|
if ( t != PY_ICID_BYREF )
|
|
return CIP_FAILED;
|
|
|
|
// Dereference
|
|
// (Since we are not using VREF_COPY flag, we can safely const_cast)
|
|
idc_value_t *dref_v = VarDeref(const_cast<idc_value_t *>(&idc_var), VREF_LOOP);
|
|
if ( dref_v == NULL )
|
|
return CIP_FAILED;
|
|
|
|
// Can we recycle the object?
|
|
PyObject *new_py_val = PyW_TryGetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR);
|
|
if ( new_py_val != NULL )
|
|
{
|
|
// Recycle
|
|
t = idcvar_to_pyvar(*dref_v, &new_py_val);
|
|
Py_XDECREF(new_py_val); // DECREF because of GetAttrStr
|
|
|
|
// Success? Nothing more to be done
|
|
if ( t == CIP_OK )
|
|
return CIP_OK;
|
|
|
|
// Clear it so we don't recycle it
|
|
new_py_val = NULL;
|
|
}
|
|
// Try to convert (not recycle)
|
|
if ( idcvar_to_pyvar(*dref_v, &new_py_val) != CIP_OK )
|
|
return CIP_FAILED;
|
|
|
|
// Update the attribute
|
|
PyObject_SetAttrString(*py_var, S_PY_IDCCVT_VALUE_ATTR, new_py_val);
|
|
Py_DECREF(new_py_val);
|
|
break;
|
|
}
|
|
|
|
// Can convert back into a Python object or Python dictionary
|
|
// (Depending if py_var will be recycled and it was a dictionary)
|
|
case VT_OBJ:
|
|
{
|
|
// Check if this IDC object has __cvt_id__ and the __idc_cvt_value__ fields
|
|
idc_value_t idc_val;
|
|
if ( VarGetAttr(&idc_var, S_PY_IDCCVT_ID_ATTR, &idc_val) == eOk
|
|
&& VarGetAttr(&idc_var, S_PY_IDCCVT_VALUE_ATTR, &idc_val) == eOk )
|
|
{
|
|
// Extract the object
|
|
*py_var = (PyObject *) idc_val.pvoid;
|
|
return CIP_OK_NODECREF;
|
|
}
|
|
PyObject *obj;
|
|
bool is_dict = false;
|
|
|
|
// Need to create a new object?
|
|
if ( *py_var == NULL )
|
|
{
|
|
// Get skeleton class reference
|
|
PyObject *py_cls = get_idaapi_attr_by_id(PY_CLSID_APPCALL_SKEL_OBJ);
|
|
if ( py_cls == NULL )
|
|
return CIP_FAILED;
|
|
|
|
// Call constructor
|
|
PYW_GIL_ENSURE;
|
|
obj = PyObject_CallFunctionObjArgs(py_cls, NULL);
|
|
PYW_GIL_RELEASE;
|
|
Py_DECREF(py_cls);
|
|
if ( PyW_GetError() || obj == NULL )
|
|
return CIP_FAILED;
|
|
}
|
|
else
|
|
{
|
|
// Recycle existing variable
|
|
obj = *py_var;
|
|
if ( PyDict_Check(obj) )
|
|
is_dict = true;
|
|
}
|
|
|
|
// Walk the IDC attributes and store into python
|
|
for (const char *attr_name = VarFirstAttr(&idc_var);
|
|
attr_name != NULL;
|
|
attr_name = VarNextAttr(&idc_var, attr_name) )
|
|
{
|
|
// Get the attribute
|
|
idc_value_t v;
|
|
VarGetAttr(&idc_var, attr_name, &v, true);
|
|
|
|
// Convert attribute to a python value (recursively)
|
|
PyObject *py_attr(NULL);
|
|
int cvt = idcvar_to_pyvar(v, &py_attr);
|
|
if ( cvt <= CIP_IMMUTABLE )
|
|
{
|
|
// Delete the object (if we created it)
|
|
if ( *py_var == NULL )
|
|
Py_DECREF(obj);
|
|
|
|
return CIP_FAILED;
|
|
}
|
|
if ( is_dict )
|
|
PyDict_SetItemString(obj, attr_name, py_attr);
|
|
else
|
|
PyObject_SetAttrString(obj, attr_name, py_attr);
|
|
|
|
if ( cvt == CIP_OK )
|
|
Py_XDECREF(py_attr);
|
|
}
|
|
*py_var = obj;
|
|
break;
|
|
}
|
|
// Unhandled type
|
|
default:
|
|
*py_var = NULL;
|
|
return CIP_FAILED;
|
|
}
|
|
return CIP_OK;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Converts IDC arguments to Python argument list or just one tuple
|
|
// If 'decref' is NULL then 'pargs' will contain one element which is the tuple
|
|
bool pyw_convert_idc_args(
|
|
const idc_value_t args[],
|
|
int nargs,
|
|
ppyobject_vec_t &pargs,
|
|
boolvec_t *decref,
|
|
char *errbuf,
|
|
size_t errbufsize)
|
|
{
|
|
bool as_tupple = decref == NULL;
|
|
PyObject *py_tuple(NULL);
|
|
|
|
pargs.qclear();
|
|
|
|
if ( as_tupple )
|
|
{
|
|
py_tuple = PyTuple_New(nargs);
|
|
if ( py_tuple == NULL )
|
|
{
|
|
if ( errbuf != 0 && errbufsize > 0 )
|
|
qstrncpy(errbuf, "Failed to create a new tuple to store arguments!", errbufsize);
|
|
return false;
|
|
}
|
|
// Add the tuple
|
|
pargs.push_back(py_tuple);
|
|
}
|
|
else
|
|
{
|
|
decref->qclear();
|
|
}
|
|
|
|
for ( int i=0; i<nargs; i++ )
|
|
{
|
|
PyObject *py_obj(NULL);
|
|
int cvt = idcvar_to_pyvar(args[i], &py_obj);
|
|
if ( cvt < CIP_OK )
|
|
{
|
|
if ( errbuf != 0 && errbufsize > 0 )
|
|
qsnprintf(errbuf, errbufsize, "arg#%d has wrong type %d", i, args[i].vtype);
|
|
return false;
|
|
}
|
|
|
|
if ( as_tupple )
|
|
{
|
|
// Opaque object?
|
|
if ( cvt == CIP_OK_NODECREF )
|
|
{
|
|
// Increment reference for opaque objects.
|
|
// (A tupple will steal references of its set items,
|
|
// and for an opaque object we want it to still exist
|
|
// even if the tuple is gone)
|
|
Py_INCREF(py_obj);
|
|
}
|
|
// Save argument
|
|
PyTuple_SetItem(py_tuple, i, py_obj);
|
|
}
|
|
else
|
|
{
|
|
pargs.push_back(py_obj);
|
|
// Do not decrement reference of opaque objects
|
|
decref->push_back(cvt == CIP_OK);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Frees arguments returned by pyw_convert_idc_args()
|
|
void pyw_free_idc_args(
|
|
ppyobject_vec_t &pargs,
|
|
boolvec_t *decref)
|
|
{
|
|
if ( decref == NULL )
|
|
{
|
|
if ( !pargs.empty() )
|
|
Py_XDECREF(pargs[0]);
|
|
}
|
|
else
|
|
{
|
|
// free argument objects
|
|
for ( int i=(int)pargs.size()-1; i>=0; i-- )
|
|
{
|
|
if ( decref->at(i) )
|
|
Py_DECREF(pargs[i]);
|
|
}
|
|
decref->clear();
|
|
}
|
|
pargs.clear();
|
|
}
|
|
|
|
//</code(py_idaapi)>
|
|
|
|
#endif
|