/* -*- mode: C; c-file-style: "bsd" -*- */ #include #include "client.h" #include "errors.h" #include "interfaces.h" #include "types.h" typedef struct { CORBA_unsigned_long len; char opname[1]; } OpNameData; #define IS_ONEWAY(desc,index) \ (((index) >= PORBIT_OPERATION_BASE && (index) < PORBIT_GETTER_BASE) && \ (desc)->operations._buffer[(index)-PORBIT_OPERATION_BASE].mode == CORBA_OP_ONEWAY) static GPtrArray * do_marshal (CV *cv, I32 ax, I32 items, CORBA_InterfaceDef_FullInterfaceDescription *desc, I32 index, CORBA_Object obj, GIOPConnection *connection, GIOP_unsigned_long request_id) { OpNameData *operation_name_data; static struct iovec operation_vec; char *name = NULL; GIOPSendBuffer *send_buffer = NULL; SV *error_sv = NULL; GPtrArray *return_types = NULL; dTHR; /* Determine the operation name used over GIOP */ if (index >= PORBIT_OPERATION_BASE && index < PORBIT_GETTER_BASE) { name = g_strdup (desc->operations._buffer[index-PORBIT_OPERATION_BASE].name); } else if (index >= PORBIT_GETTER_BASE && index < PORBIT_SETTER_BASE) { name = g_strconcat ("_get_", desc->attributes._buffer[index-PORBIT_GETTER_BASE].name, NULL); } else if (index >= PORBIT_SETTER_BASE) { name = g_strconcat ("_set_", desc->attributes._buffer[index-PORBIT_SETTER_BASE].name, NULL); } /* Create a SendBuffer for the result */ operation_name_data = (OpNameData *)g_malloc (sizeof (OpNameData) + strlen(name)); operation_name_data->len = strlen(name) + 1; strcpy (operation_name_data->opname, name); operation_vec.iov_base = operation_name_data; operation_vec.iov_len = sizeof(CORBA_unsigned_long) + operation_name_data->len; send_buffer = giop_send_request_buffer_use(connection, NULL, request_id, !IS_ONEWAY(desc,index), &(obj->active_profile->object_key_vec), &operation_vec, &ORBit_default_principal_iovec); if (!send_buffer) { error_sv = porbit_system_except ("IDL:omg.org/CORBA/COMM_FAILURE:1.0", 0, CORBA_COMPLETED_NO); goto exception; } /* Do the marshalling. We accumulate the return types into an array for * use while demarshalling. */ return_types = g_ptr_array_new(); if (index >= PORBIT_OPERATION_BASE && index < PORBIT_GETTER_BASE) { CORBA_OperationDescription *opr = &desc->operations._buffer[index-PORBIT_OPERATION_BASE]; CORBA_unsigned_long i, st_index; if (opr->result->kind != CORBA_tk_void) g_ptr_array_add (return_types, opr->result); st_index = 1; for (i = 0 ; iparameters._length; i++) { SV *arg = (st_index<(CORBA_unsigned_long)items) ? ST(st_index) : &PL_sv_undef; switch (opr->parameters._buffer[i].mode) { case CORBA_PARAM_IN: if (!porbit_put_sv (send_buffer, opr->parameters._buffer[i].type, arg)) { warn ("Error marshalling parameter '%s'", opr->parameters._buffer[i].name); error_sv = porbit_system_except ("IDL:omg.org/CORBA/MARSHAL:1.0", 0, CORBA_COMPLETED_NO); goto exception; } st_index++; break; case CORBA_PARAM_INOUT: if (!SvROK(arg) || !porbit_put_sv (send_buffer, opr->parameters._buffer[i].type, SvRV (arg))) { if (!SvROK (arg)) warn ("INOUT parameter must be a reference"); else warn ("Error marshalling parameter '%s'", opr->parameters._buffer[i].name); error_sv = porbit_system_except ("IDL:omg.org/CORBA/MARSHAL:1.0", 0, CORBA_COMPLETED_NO); goto exception; } st_index++; /* Fall through */ case CORBA_PARAM_OUT: g_ptr_array_add (return_types, opr->parameters._buffer[i].type); break; } } } else if (index >= PORBIT_GETTER_BASE && index < PORBIT_SETTER_BASE) { g_ptr_array_add (return_types, desc->attributes._buffer[index-PORBIT_GETTER_BASE].type); } else if (index >= PORBIT_SETTER_BASE) { if (items < 2) { warn("%s::%s called without second argument", HvNAME(CvSTASH(cv)), name); error_sv = porbit_system_except ("IDL:omg.org/CORBA/MARSHAL:1.0", 0, CORBA_COMPLETED_NO); goto exception; } if (!porbit_put_sv (send_buffer, desc->attributes._buffer[index-PORBIT_SETTER_BASE].type, ST(1))) { warn ("Error marshalling attribute value"); error_sv = porbit_system_except ("IDL:omg.org/CORBA/MARSHAL:1.0", 0, CORBA_COMPLETED_NO); goto exception; } } /* Invoke the operation */ giop_send_buffer_write(send_buffer); exception: giop_send_buffer_unuse(send_buffer); g_free (operation_name_data); g_free (name); if (error_sv) { if (return_types) g_ptr_array_free (return_types, TRUE); porbit_throw (error_sv); } return return_types; } static GIOPConnection * do_demarshal (CV *cv, I32 ax, I32 items, CORBA_InterfaceDef_FullInterfaceDescription *desc, I32 index, GPtrArray *return_types, guint *return_count, CORBA_Object obj, GIOPConnection *connection, GIOP_unsigned_long request_id) { GIOPRecvBuffer *recv_buffer; SV *error_sv = NULL; SV **results = NULL; CORBA_unsigned_long i; CORBA_OperationDescription *opr = NULL; dTHR; if (index >= PORBIT_OPERATION_BASE && index < PORBIT_GETTER_BASE) opr = &desc->operations._buffer[index-PORBIT_OPERATION_BASE]; recv_buffer = giop_recv_reply_buffer_use_2(connection, request_id, TRUE); if (!recv_buffer) { error_sv = porbit_system_except ("IDL:omg.org/CORBA/COMM_FAILURE:1.0", 0, CORBA_COMPLETED_MAYBE); goto exception; } if (recv_buffer->message.u.reply.reply_status == GIOP_LOCATION_FORWARD) { if (obj->forward_locations != NULL) ORBit_delete_profiles(obj->forward_locations); obj->forward_locations = ORBit_demarshal_IOR(recv_buffer); connection = ORBit_object_get_forwarded_connection(obj); giop_recv_buffer_unuse(recv_buffer); return connection; } else if (recv_buffer->message.u.reply.reply_status != GIOP_NO_EXCEPTION) { error_sv = porbit_get_exception (recv_buffer, NULL, recv_buffer->message.u.reply.reply_status, opr); if (!error_sv) error_sv = porbit_system_except ("IDL:omg.org/CORBA/MARSHAL:1.0", 0, CORBA_COMPLETED_YES); goto exception; } /* Demarshal return parameters */ results = g_new0 (SV *, return_types->len); for (i=0; ilen; i++) { results[i] = porbit_get_sv (recv_buffer, return_types->pdata[i]); if (!results[i]) { warn ("Error demarshalling result"); error_sv = porbit_system_except ("IDL:omg.org/CORBA/MARSHAL:1.0", 0, CORBA_COMPLETED_YES); goto exception; } } if (index >= PORBIT_OPERATION_BASE && index < PORBIT_GETTER_BASE) { CORBA_unsigned_long i, st_index, ret_index; /* First write back INOUT parameters into their references. * (Is this safe? If we end up calling back to perl, could the * stack already be overridden?) */ st_index = 1; ret_index = (opr->result->kind == CORBA_tk_void) ? 0 : 1; for (i = 0 ; iparameters._length; i++) { switch (opr->parameters._buffer[i].mode) { case CORBA_PARAM_IN: st_index++; break; case CORBA_PARAM_INOUT: sv_setsv (SvRV(ST(st_index)), results[ret_index]); st_index++; ret_index++; break; case CORBA_PARAM_OUT: ret_index++; break; } } /* Now write out return value and OUT parameters to stack */ st_index = 0; ret_index = 0; if (opr->result->kind != CORBA_tk_void) { ST(st_index) = sv_2mortal(results[0]); st_index++; ret_index++; } for (i = 0 ; iparameters._length; i++) { switch (opr->parameters._buffer[i].mode) { case CORBA_PARAM_IN: break; case CORBA_PARAM_INOUT: ret_index++; break; case CORBA_PARAM_OUT: ST(st_index) = sv_2mortal (results[ret_index]); st_index++; ret_index++; break; } } *return_count = st_index; } else if (index >= PORBIT_GETTER_BASE && index < PORBIT_SETTER_BASE) { ST(0) = sv_2mortal(results[0]); } g_free (results); results = NULL; exception: if (results) { for (i=0; i < return_types->len; i++) if (results[i]) SvREFCNT_dec (results[i]); g_free (results); } g_ptr_array_free (return_types, TRUE); giop_recv_buffer_unuse(recv_buffer); if (error_sv) porbit_throw (error_sv); return NULL; } XS(_porbit_callStub) { dXSARGS; GIOP_unsigned_long request_id; GIOPConnection *connection, *new_connection; GPtrArray *return_types; guint return_count; I32 index; PORBitIfaceInfo *info; CORBA_Object obj; char *repoid; SV **repoidp; index = XSANY.any_i32; repoidp = hv_fetch(CvSTASH(cv), PORBIT_REPOID_KEY, strlen(PORBIT_REPOID_KEY), 0); if (!repoidp) croak("_porbit_callStub called with bad package (no %s)",PORBIT_REPOID_KEY); #if (PATCHLEVEL < 6) repoid = SvPV(GvSV(*repoidp), PL_na); #else repoid = SvPV_nolen(GvSV(*repoidp)); #endif /* PATCHLEVEL */ info = porbit_find_interface_description (repoid); if (!info) croak("_porbit_callStub called on undefined interface"); /* Get the discriminator */ if (items < 1) croak("method must have object as first argument"); obj = porbit_sv_to_objref(ST(0)); /* may croak */ if (!obj) { croak("Can't call CORBA method on an undefined value"); } connection = ORBit_object_get_connection(obj); retry_request: /* This is utterly broken and I'm ashamed to copy it from ORBit. But, * it is necessary for compatibility. */ request_id = GPOINTER_TO_UINT(alloca(0)); return_types = do_marshal (cv, ax, items, info->desc, index, obj, connection, request_id); return_count = return_types->len; /* FIXME: Somewhat dubious stack growing code */ if (PL_stack_max - &ST(0) < return_count) stack_grow (PL_stack_sp, &ST(0), return_count); if (IS_ONEWAY (info->desc, index)) { if (return_count != 0) { warn ("ONEWAY operation has output parameters or a return value!"); } } else { new_connection = do_demarshal (cv, ax, items, info->desc, index, return_types, &return_count, obj, connection, request_id); if (new_connection) { connection = new_connection; goto retry_request; } } switch (GIMME_V) { case G_ARRAY: XSRETURN(return_count); case G_SCALAR: XSRETURN(1); case G_VOID: XSRETURN_EMPTY; } }