blob: 82629b7ef161a0c6b4c58337a3c540c01120386b [file] [log] [blame]
typedef struct {
struct _cffi_type_context_s ctx; /* inlined substructure */
PyObject *types_dict;
PyObject *included_ffis;
PyObject *included_libs;
PyObject *_keepalive1;
PyObject *_keepalive2;
} builder_c_t;
static PyObject *all_primitives[_CFFI__NUM_PRIM];
static CTypeDescrObject *g_ct_voidp, *g_ct_chararray;
static PyObject *build_primitive_type(int num); /* forward */
#define primitive_in_range(num) ((num) >= 0 && (num) < _CFFI__NUM_PRIM)
#define get_primitive_type(num) \
((primitive_in_range(num) && all_primitives[num] != NULL) ? \
all_primitives[num] : build_primitive_type(num))
static int init_global_types_dict(PyObject *ffi_type_dict)
{
int err;
PyObject *ct_void, *ct_char, *ct2, *pnull;
/* XXX some leaks in case these functions fail, but well,
MemoryErrors during importing an extension module are kind
of bad anyway */
ct_void = get_primitive_type(_CFFI_PRIM_VOID); // 'void'
if (ct_void == NULL)
return -1;
ct2 = new_pointer_type((CTypeDescrObject *)ct_void); // 'void *'
if (ct2 == NULL)
return -1;
g_ct_voidp = (CTypeDescrObject *)ct2;
ct_char = get_primitive_type(_CFFI_PRIM_CHAR); // 'char'
if (ct_char == NULL)
return -1;
ct2 = new_pointer_type((CTypeDescrObject *)ct_char); // 'char *'
if (ct2 == NULL)
return -1;
ct2 = new_array_type((CTypeDescrObject *)ct2, -1); // 'char[]'
if (ct2 == NULL)
return -1;
g_ct_chararray = (CTypeDescrObject *)ct2;
pnull = new_simple_cdata(NULL, g_ct_voidp);
if (pnull == NULL)
return -1;
err = PyDict_SetItemString(ffi_type_dict, "NULL", pnull);
Py_DECREF(pnull);
return err;
}
static void free_builder_c(builder_c_t *builder, int ctx_is_static)
{
if (!ctx_is_static) {
size_t i;
const void *mem[] = {builder->ctx.types,
builder->ctx.globals,
builder->ctx.struct_unions,
//builder->ctx.fields: allocated with struct_unions
builder->ctx.enums,
builder->ctx.typenames};
for (i = 0; i < sizeof(mem) / sizeof(*mem); i++) {
if (mem[i] != NULL)
PyMem_Free((void *)mem[i]);
}
}
Py_XDECREF(builder->included_ffis);
Py_XDECREF(builder->included_libs);
Py_XDECREF(builder->types_dict);
Py_XDECREF(builder->_keepalive1);
Py_XDECREF(builder->_keepalive2);
}
static int init_builder_c(builder_c_t *builder,
const struct _cffi_type_context_s *ctx)
{
PyObject *ldict = PyDict_New();
if (ldict == NULL)
return -1;
if (ctx)
builder->ctx = *ctx;
else
memset(&builder->ctx, 0, sizeof(builder->ctx));
builder->types_dict = ldict;
builder->included_ffis = NULL;
builder->included_libs = NULL;
builder->_keepalive1 = NULL;
builder->_keepalive2 = NULL;
return 0;
}
static PyObject *build_primitive_type(int num)
{
/* XXX too many translations between here and new_primitive_type() */
static const char *primitive_name[] = {
NULL,
"_Bool",
"char",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long",
"unsigned long",
"long long",
"unsigned long long",
"float",
"double",
"long double",
"wchar_t",
"int8_t",
"uint8_t",
"int16_t",
"uint16_t",
"int32_t",
"uint32_t",
"int64_t",
"uint64_t",
"intptr_t",
"uintptr_t",
"ptrdiff_t",
"size_t",
"ssize_t",
"int_least8_t",
"uint_least8_t",
"int_least16_t",
"uint_least16_t",
"int_least32_t",
"uint_least32_t",
"int_least64_t",
"uint_least64_t",
"int_fast8_t",
"uint_fast8_t",
"int_fast16_t",
"uint_fast16_t",
"int_fast32_t",
"uint_fast32_t",
"int_fast64_t",
"uint_fast64_t",
"intmax_t",
"uintmax_t",
"float _Complex",
"double _Complex",
"char16_t",
"char32_t",
};
PyObject *x;
assert(sizeof(primitive_name) == sizeof(*primitive_name) * _CFFI__NUM_PRIM);
if (num == _CFFI_PRIM_VOID) {
x = new_void_type();
}
else if (primitive_in_range(num) && primitive_name[num] != NULL) {
x = new_primitive_type(primitive_name[num]);
}
else if (num == _CFFI__UNKNOWN_PRIM) {
PyErr_SetString(FFIError, "primitive integer type with an unexpected "
"size (or not an integer type at all)");
return NULL;
}
else if (num == _CFFI__UNKNOWN_FLOAT_PRIM) {
PyErr_SetString(FFIError, "primitive floating-point type with an "
"unexpected size (or not a float type at all)");
return NULL;
}
else if (num == _CFFI__UNKNOWN_LONG_DOUBLE) {
PyErr_SetString(FFIError, "primitive floating-point type is "
"'long double', not supported for now with "
"the syntax 'typedef double... xxx;'");
return NULL;
}
else {
PyErr_Format(PyExc_NotImplementedError, "prim=%d", num);
return NULL;
}
all_primitives[num] = x;
return x;
}
static PyObject *realize_global_int(builder_c_t *builder, int gindex)
{
int neg;
char got[64];
unsigned long long value;
struct _cffi_getconst_s gc;
const struct _cffi_global_s *g = &builder->ctx.globals[gindex];
gc.ctx = &builder->ctx;
gc.gindex = gindex;
/* note: we cast g->address to this function type; we do the same
in parse_c_type:parse_sequel() too. Note that the called function
may be declared simply with "unsigned long long *" as argument,
which is fine as it is the first field in _cffi_getconst_s. */
assert(&gc.value == (unsigned long long *)&gc);
neg = ((int(*)(struct _cffi_getconst_s *))g->address)(&gc);
value = gc.value;
switch (neg) {
case 0:
if (value <= (unsigned long long)LONG_MAX)
return PyInt_FromLong((long)value);
else
return PyLong_FromUnsignedLongLong(value);
case 1:
if ((long long)value >= (long long)LONG_MIN)
return PyInt_FromLong((long)value);
else
return PyLong_FromLongLong((long long)value);
default:
break;
}
if (neg == 2)
sprintf(got, "%llu (0x%llx)", value, value);
else
sprintf(got, "%lld", (long long)value);
PyErr_Format(FFIError, "the C compiler says '%.200s' is equal to %s, "
"but the cdef disagrees", g->name, got);
return NULL;
}
static CTypeDescrObject *
unwrap_fn_as_fnptr(PyObject *x)
{
assert(PyTuple_Check(x));
return (CTypeDescrObject *)PyTuple_GET_ITEM(x, 0);
}
static CTypeDescrObject *
unexpected_fn_type(PyObject *x)
{
CTypeDescrObject *ct = unwrap_fn_as_fnptr(x);
char *text1 = ct->ct_name;
char *text2 = text1 + ct->ct_name_position + 1;
assert(text2[-3] == '(');
text2[-3] = '\0';
PyErr_Format(FFIError, "the type '%s%s' is a function type, not a "
"pointer-to-function type", text1, text2);
text2[-3] = '(';
return NULL;
}
static PyObject *
realize_c_type_or_func(builder_c_t *builder,
_cffi_opcode_t opcodes[], int index); /* forward */
/* Interpret an opcodes[] array. If opcodes == ctx->types, store all
the intermediate types back in the opcodes[]. Returns a new
reference.
*/
static CTypeDescrObject *
realize_c_type(builder_c_t *builder, _cffi_opcode_t opcodes[], int index)
{
PyObject *x = realize_c_type_or_func(builder, opcodes, index);
if (x == NULL || CTypeDescr_Check(x))
return (CTypeDescrObject *)x;
else {
unexpected_fn_type(x);
Py_DECREF(x);
return NULL;
}
}
static void _realize_name(char *target, const char *prefix, const char *srcname)
{
/* "xyz" => "struct xyz"
"$xyz" => "xyz"
"$1" => "struct $1"
*/
if (srcname[0] == '$' && srcname[1] != '$' &&
!('0' <= srcname[1] && srcname[1] <= '9')) {
strcpy(target, &srcname[1]);
}
else {
strcpy(target, prefix);
strcat(target, srcname);
}
}
static void _unrealize_name(char *target, const char *srcname)
{
/* reverse of _realize_name() */
if (strncmp(srcname, "struct ", 7) == 0) {
strcpy(target, &srcname[7]);
}
else if (strncmp(srcname, "union ", 6) == 0) {
strcpy(target, &srcname[6]);
}
else if (strncmp(srcname, "enum ", 5) == 0) {
strcpy(target, &srcname[5]);
}
else {
strcpy(target, "$");
strcat(target, srcname);
}
}
static PyObject * /* forward */
_fetch_external_struct_or_union(const struct _cffi_struct_union_s *s,
PyObject *included_ffis, int recursion);
static PyObject *
_realize_c_struct_or_union(builder_c_t *builder, int sindex)
{
PyObject *x;
_cffi_opcode_t op2;
const struct _cffi_struct_union_s *s;
if (sindex == _CFFI__IO_FILE_STRUCT) {
/* returns a single global cached opaque type */
static PyObject *file_struct = NULL;
if (file_struct == NULL)
file_struct = new_struct_or_union_type("FILE",
CT_STRUCT | CT_IS_FILE);
Py_XINCREF(file_struct);
return file_struct;
}
s = &builder->ctx.struct_unions[sindex];
op2 = builder->ctx.types[s->type_index];
if ((((uintptr_t)op2) & 1) == 0) {
x = (PyObject *)op2; /* found already in the "primary" slot */
Py_INCREF(x);
}
else {
CTypeDescrObject *ct = NULL;
if (!(s->flags & _CFFI_F_EXTERNAL)) {
int flags = (s->flags & _CFFI_F_UNION) ? CT_UNION : CT_STRUCT;
char *name = alloca(8 + strlen(s->name));
_realize_name(name,
(s->flags & _CFFI_F_UNION) ? "union " : "struct ",
s->name);
if (strcmp(name, "struct _IO_FILE") == 0)
x = _realize_c_struct_or_union(builder, _CFFI__IO_FILE_STRUCT);
else
x = new_struct_or_union_type(name, flags);
if (x == NULL)
return NULL;
if (!(s->flags & _CFFI_F_OPAQUE)) {
assert(s->first_field_index >= 0);
ct = (CTypeDescrObject *)x;
ct->ct_size = (Py_ssize_t)s->size;
ct->ct_length = s->alignment; /* may be -1 */
ct->ct_flags &= ~CT_IS_OPAQUE;
ct->ct_flags |= CT_LAZY_FIELD_LIST;
ct->ct_extra = builder;
}
else
assert(s->first_field_index < 0);
}
else {
assert(s->first_field_index < 0);
x = _fetch_external_struct_or_union(s, builder->included_ffis, 0);
if (x == NULL) {
if (!PyErr_Occurred())
PyErr_Format(FFIError, "'%s %.200s' should come from "
"ffi.include() but was not found",
(s->flags & _CFFI_F_UNION) ? "union"
: "struct", s->name);
return NULL;
}
if (!(s->flags & _CFFI_F_OPAQUE)) {
if (((CTypeDescrObject *)x)->ct_flags & CT_IS_OPAQUE) {
const char *prefix = (s->flags & _CFFI_F_UNION) ? "union"
: "struct";
PyErr_Format(PyExc_NotImplementedError,
"'%s %.200s' is opaque in the ffi.include(), "
"but no longer in the ffi doing the include "
"(workaround: don't use ffi.include() but "
"duplicate the declarations of everything "
"using %s %.200s)",
prefix, s->name, prefix, s->name);
Py_DECREF(x);
return NULL;
}
}
}
/* Update the "primary" OP_STRUCT_UNION slot */
assert((((uintptr_t)x) & 1) == 0);
assert(builder->ctx.types[s->type_index] == op2);
Py_INCREF(x);
builder->ctx.types[s->type_index] = x;
if (ct != NULL && s->size == (size_t)-2) {
/* oops, this struct is unnamed and we couldn't generate
a C expression to get its size. We have to rely on
complete_struct_or_union() to compute it now. */
if (do_realize_lazy_struct(ct) < 0) {
builder->ctx.types[s->type_index] = op2;
return NULL;
}
}
}
return x;
}
static PyObject *
realize_c_type_or_func_now(builder_c_t *builder, _cffi_opcode_t op,
_cffi_opcode_t opcodes[], int index)
{
PyObject *x, *y, *z;
Py_ssize_t length = -1;
switch (_CFFI_GETOP(op)) {
case _CFFI_OP_PRIMITIVE:
x = get_primitive_type(_CFFI_GETARG(op));
Py_XINCREF(x);
break;
case _CFFI_OP_POINTER:
y = realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op));
if (y == NULL)
return NULL;
if (CTypeDescr_Check(y)) {
x = new_pointer_type((CTypeDescrObject *)y);
}
else {
assert(PyTuple_Check(y)); /* from _CFFI_OP_FUNCTION */
x = PyTuple_GET_ITEM(y, 0);
Py_INCREF(x);
}
Py_DECREF(y);
break;
case _CFFI_OP_ARRAY:
length = (Py_ssize_t)opcodes[index + 1];
/* fall-through */
case _CFFI_OP_OPEN_ARRAY:
y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op));
if (y == NULL)
return NULL;
z = new_pointer_type((CTypeDescrObject *)y);
Py_DECREF(y);
if (z == NULL)
return NULL;
x = new_array_type((CTypeDescrObject *)z, length);
Py_DECREF(z);
break;
case _CFFI_OP_STRUCT_UNION:
x = _realize_c_struct_or_union(builder, _CFFI_GETARG(op));
break;
case _CFFI_OP_ENUM:
{
const struct _cffi_enum_s *e;
_cffi_opcode_t op2;
e = &builder->ctx.enums[_CFFI_GETARG(op)];
op2 = builder->ctx.types[e->type_index];
if ((((uintptr_t)op2) & 1) == 0) {
x = (PyObject *)op2;
Py_INCREF(x);
}
else {
PyObject *enumerators = NULL, *enumvalues = NULL, *tmp;
Py_ssize_t i, j, n = 0;
const char *p;
int gindex;
PyObject *args;
PyObject *basetd = get_primitive_type(e->type_prim);
if (basetd == NULL)
return NULL;
if (*e->enumerators != '\0') {
n++;
for (p = e->enumerators; *p != '\0'; p++)
n += (*p == ',');
}
enumerators = PyTuple_New(n);
if (enumerators == NULL)
return NULL;
enumvalues = PyTuple_New(n);
if (enumvalues == NULL) {
Py_DECREF(enumerators);
return NULL;
}
p = e->enumerators;
for (i = 0; i < n; i++) {
j = 0;
while (p[j] != ',' && p[j] != '\0')
j++;
tmp = PyText_FromStringAndSize(p, j);
if (tmp == NULL)
break;
PyTuple_SET_ITEM(enumerators, i, tmp);
gindex = search_in_globals(&builder->ctx, p, j);
assert(gindex >= 0);
assert(builder->ctx.globals[gindex].type_op ==
_CFFI_OP(_CFFI_OP_ENUM, -1));
tmp = realize_global_int(builder, gindex);
if (tmp == NULL)
break;
PyTuple_SET_ITEM(enumvalues, i, tmp);
p += j + 1;
}
args = NULL;
if (!PyErr_Occurred()) {
char *name = alloca(6 + strlen(e->name));
_realize_name(name, "enum ", e->name);
args = Py_BuildValue("(sOOO)", name, enumerators,
enumvalues, basetd);
}
Py_DECREF(enumerators);
Py_DECREF(enumvalues);
if (args == NULL)
return NULL;
x = b_new_enum_type(NULL, args);
Py_DECREF(args);
if (x == NULL)
return NULL;
/* Update the "primary" _CFFI_OP_ENUM slot, which
may be the same or a different slot than the "current" one */
assert((((uintptr_t)x) & 1) == 0);
assert(builder->ctx.types[e->type_index] == op2);
Py_INCREF(x);
builder->ctx.types[e->type_index] = x;
/* Done, leave without updating the "current" slot because
it may be done already above. If not, never mind, the
next call to realize_c_type() will do it. */
return x;
}
break;
}
case _CFFI_OP_FUNCTION:
{
PyObject *fargs;
int i, base_index, num_args, ellipsis, abi;
y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op));
if (y == NULL)
return NULL;
base_index = index + 1;
num_args = 0;
/* note that if the arguments are already built, they have a
pointer in the 'opcodes' array, and GETOP() returns a
random even value. But OP_FUNCTION_END is odd, so the
condition below still works correctly. */
while (_CFFI_GETOP(opcodes[base_index + num_args]) !=
_CFFI_OP_FUNCTION_END)
num_args++;
ellipsis = _CFFI_GETARG(opcodes[base_index + num_args]) & 0x01;
abi = _CFFI_GETARG(opcodes[base_index + num_args]) & 0xFE;
switch (abi) {
case 0:
abi = FFI_DEFAULT_ABI;
break;
case 2:
#if defined(MS_WIN32) && !defined(_WIN64)
abi = FFI_STDCALL;
#else
abi = FFI_DEFAULT_ABI;
#endif
break;
default:
PyErr_Format(FFIError, "abi number %d not supported", abi);
Py_DECREF(y);
return NULL;
}
fargs = PyTuple_New(num_args);
if (fargs == NULL) {
Py_DECREF(y);
return NULL;
}
for (i = 0; i < num_args; i++) {
z = (PyObject *)realize_c_type(builder, opcodes, base_index + i);
if (z == NULL) {
Py_DECREF(fargs);
Py_DECREF(y);
return NULL;
}
PyTuple_SET_ITEM(fargs, i, z);
}
z = new_function_type(fargs, (CTypeDescrObject *)y, ellipsis, abi);
Py_DECREF(fargs);
Py_DECREF(y);
if (z == NULL)
return NULL;
x = PyTuple_Pack(1, z); /* hack: hide the CT_FUNCTIONPTR. it will
be revealed again by the OP_POINTER */
Py_DECREF(z);
break;
}
case _CFFI_OP_NOOP:
x = realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op));
break;
case _CFFI_OP_TYPENAME:
{
/* essential: the TYPENAME opcode resolves the type index looked
up in the 'ctx->typenames' array, but it does so in 'ctx->types'
instead of in 'opcodes'! */
int type_index = builder->ctx.typenames[_CFFI_GETARG(op)].type_index;
x = realize_c_type_or_func(builder, builder->ctx.types, type_index);
break;
}
default:
PyErr_Format(PyExc_NotImplementedError, "op=%d", (int)_CFFI_GETOP(op));
return NULL;
}
return x;
}
static int _realize_recursion_level;
static PyObject *
realize_c_type_or_func(builder_c_t *builder,
_cffi_opcode_t opcodes[], int index)
{
PyObject *x;
_cffi_opcode_t op = opcodes[index];
if ((((uintptr_t)op) & 1) == 0) {
x = (PyObject *)op;
Py_INCREF(x);
return x;
}
if (_realize_recursion_level >= 1000) {
PyErr_Format(PyExc_RuntimeError,
"type-building recursion too deep or infinite. "
"This is known to occur e.g. in ``struct s { void(*callable)"
"(struct s); }''. Please report if you get this error and "
"really need support for your case.");
return NULL;
}
_realize_recursion_level++;
x = realize_c_type_or_func_now(builder, op, opcodes, index);
_realize_recursion_level--;
if (x != NULL && opcodes == builder->ctx.types && opcodes[index] != x) {
assert((((uintptr_t)x) & 1) == 0);
assert((((uintptr_t)opcodes[index]) & 1) == 1);
Py_INCREF(x);
opcodes[index] = x;
}
return x;
}
static CTypeDescrObject *
realize_c_func_return_type(builder_c_t *builder,
_cffi_opcode_t opcodes[], int index)
{
PyObject *x;
_cffi_opcode_t op = opcodes[index];
if ((((uintptr_t)op) & 1) == 0) {
/* already built: assert that it is a function and fish
for the return type */
x = (PyObject *)op;
assert(PyTuple_Check(x)); /* from _CFFI_OP_FUNCTION */
x = PyTuple_GET_ITEM(x, 0);
assert(CTypeDescr_Check(x));
assert(((CTypeDescrObject *)x)->ct_flags & CT_FUNCTIONPTR);
x = PyTuple_GET_ITEM(((CTypeDescrObject *)x)->ct_stuff, 1);
assert(CTypeDescr_Check(x));
Py_INCREF(x);
return (CTypeDescrObject *)x;
}
else {
assert(_CFFI_GETOP(op) == _CFFI_OP_FUNCTION);
return realize_c_type(builder, opcodes, _CFFI_GETARG(opcodes[index]));
}
}
static int do_realize_lazy_struct(CTypeDescrObject *ct)
{
/* This is called by force_lazy_struct() in _cffi_backend.c */
assert(ct->ct_flags & (CT_STRUCT | CT_UNION));
if (ct->ct_flags & CT_LAZY_FIELD_LIST) {
builder_c_t *builder;
char *p;
int n, i, sflags;
const struct _cffi_struct_union_s *s;
const struct _cffi_field_s *fld;
PyObject *fields, *args, *res;
assert(!(ct->ct_flags & CT_IS_OPAQUE));
builder = ct->ct_extra;
assert(builder != NULL);
p = alloca(2 + strlen(ct->ct_name));
_unrealize_name(p, ct->ct_name);
n = search_in_struct_unions(&builder->ctx, p, strlen(p));
if (n < 0)
Py_FatalError("lost a struct/union!");
s = &builder->ctx.struct_unions[n];
fld = &builder->ctx.fields[s->first_field_index];
/* XXX painfully build all the Python objects that are the args
to b_complete_struct_or_union() */
fields = PyList_New(s->num_fields);
if (fields == NULL)
return -1;
for (i = 0; i < s->num_fields; i++, fld++) {
_cffi_opcode_t op = fld->field_type_op;
int fbitsize = -1;
PyObject *f;
CTypeDescrObject *ctf;
switch (_CFFI_GETOP(op)) {
case _CFFI_OP_BITFIELD:
assert(fld->field_size >= 0);
fbitsize = (int)fld->field_size;
/* fall-through */
case _CFFI_OP_NOOP:
ctf = realize_c_type(builder, builder->ctx.types,
_CFFI_GETARG(op));
break;
default:
Py_DECREF(fields);
PyErr_Format(PyExc_NotImplementedError, "field op=%d",
(int)_CFFI_GETOP(op));
return -1;
}
if (ctf != NULL && fld->field_offset == (size_t)-1) {
/* unnamed struct, with field positions and sizes entirely
determined by complete_struct_or_union() and not checked.
Or, bitfields (field_size >= 0), similarly not checked. */
assert(fld->field_size == (size_t)-1 || fbitsize >= 0);
}
else if (ctf == NULL || detect_custom_layout(ct, SF_STD_FIELD_POS,
ctf->ct_size, fld->field_size,
"wrong size for field '",
fld->name, "'") < 0) {
Py_DECREF(fields);
return -1;
}
f = Py_BuildValue("(sOin)", fld->name, ctf,
fbitsize, (Py_ssize_t)fld->field_offset);
if (f == NULL) {
Py_DECREF(fields);
return -1;
}
PyList_SET_ITEM(fields, i, f);
}
sflags = 0;
if (s->flags & _CFFI_F_CHECK_FIELDS)
sflags |= SF_STD_FIELD_POS;
if (s->flags & _CFFI_F_PACKED)
sflags |= SF_PACKED;
args = Py_BuildValue("(OOOnii)", ct, fields, Py_None,
(Py_ssize_t)s->size,
s->alignment,
sflags);
Py_DECREF(fields);
if (args == NULL)
return -1;
ct->ct_extra = NULL;
ct->ct_flags |= CT_IS_OPAQUE;
res = b_complete_struct_or_union(NULL, args);
ct->ct_flags &= ~CT_IS_OPAQUE;
Py_DECREF(args);
if (res == NULL) {
ct->ct_extra = builder;
return -1;
}
assert(ct->ct_stuff != NULL);
ct->ct_flags &= ~CT_LAZY_FIELD_LIST;
Py_DECREF(res);
return 1;
}
else {
assert(ct->ct_flags & CT_IS_OPAQUE);
return 0;
}
}