/* * Mach Operating System * Copyright (c) 1992,1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* * ABSTRACT: * Provides the routine used by parser.c to generate * routine structures for each routine statement. * The parser generates a threaded list of statements * of which the most interesting are the various kinds * routine statments. The routine structure is defined * in routine.h which includes it name, kind of routine * and other information, * a pointer to an argument list which contains the name * and type information for each argument, and a list * of distinguished arguments, eg. Request and Reply * ports, waittime, retcode etc. */ #include #include #include "error.h" #include "global.h" #include "routine.h" #include "message.h" #include "cpu.h" u_int rtNumber = 0; routine_t * rtAlloc(void) { routine_t *new; new = (routine_t *) calloc(1, sizeof *new); if (new == rtNULL) fatal("rtAlloc(): %s", unix_error_string(errno)); new->rtNumber = rtNumber++; new->rtName = strNULL; new->rtUserName = strNULL; new->rtServerName = strNULL; return new; } void rtSkip(int n) { rtNumber += n; } argument_t * argAlloc(void) { static const argument_t prototype = { strNULL, /* identifier_t argName */ argNULL, /* argument_t *argNext */ akNone, /* arg_kind_t argKind */ itNULL, /* ipc_type_t *argType */ strNULL, /* string_t argVarName */ strNULL, /* string_t argMsgField */ strNULL, /* string_t argTTName */ strNULL, /* string_t argPadName */ flNone, /* ipc_flags_t argFlags */ d_NO, /* dealloc_t argDeallocate */ FALSE, /* boolean_t argLongForm */ FALSE, /* boolean_t argServerCopy */ FALSE, /* boolean_t argCountInOut */ rtNULL, /* routine_t *argRoutine */ argNULL, /* argument_t *argCount */ argNULL, /* argument_t *argCInOut */ argNULL, /* argument_t *argPoly */ argNULL, /* argument_t *argDealloc */ argNULL, /* argument_t *argSCopy */ argNULL, /* argument_t *argParent */ 1, /* int argMultiplier */ 0, /* int argRequestPos */ 0, /* int argReplyPos */ FALSE, /* boolean_t argByReferenceUser */ FALSE /* boolean_t argByReferenceServer */ }; argument_t *new; new = malloc(sizeof *new); if (new == argNULL) fatal("argAlloc(): %s", unix_error_string(errno)); *new = prototype; return new; } routine_t * rtMakeRoutine(identifier_t name, argument_t *args) { routine_t *rt = rtAlloc(); rt->rtName = name; rt->rtKind = rkRoutine; rt->rtArgs = args; return rt; } routine_t * rtMakeSimpleRoutine(identifier_t name, argument_t *args) { routine_t *rt = rtAlloc(); rt->rtName = name; rt->rtKind = rkSimpleRoutine; rt->rtArgs = args; return rt; } const char * rtRoutineKindToStr(routine_kind_t rk) { switch (rk) { case rkRoutine: return "Routine"; case rkSimpleRoutine: return "SimpleRoutine"; default: fatal("rtRoutineKindToStr(%d): not a routine_kind_t", rk); /*NOTREACHED*/ } } static void rtPrintArg(const argument_t *arg) { const ipc_type_t *it = arg->argType; if (!akCheck(arg->argKind, akbUserArg|akbServerArg) || (akIdent(arg->argKind) == akeCount) || (akIdent(arg->argKind) == akePoly)) return; printf("\n\t"); switch (akIdent(arg->argKind)) { case akeRequestPort: printf("RequestPort"); break; case akeReplyPort: printf("ReplyPort"); break; case akeWaitTime: printf("WaitTime"); break; case akeMsgOption: printf("MsgOption"); break; case akeMsgSeqno: printf("MsgSeqno\t"); break; default: if (akCheck(arg->argKind, akbRequest)) { if (akCheck(arg->argKind, akbSend)) printf("In"); else printf("(In)"); } if (akCheck(arg->argKind, akbReply)) { if (akCheck(arg->argKind, akbReturn)) printf("Out"); else printf("(Out)"); } printf("\t"); } printf("\t%s: %s", arg->argName, it->itName); if (arg->argDeallocate != it->itDeallocate) { if (arg->argDeallocate == d_YES) printf(", Dealloc"); else if (arg->argDeallocate == d_MAYBE) printf(", Dealloc[]"); else printf(", NotDealloc"); } if (arg->argLongForm != it->itLongForm) { if (arg->argLongForm) printf(", IsLong"); else printf(", IsNotLong"); } if (arg->argServerCopy) printf(", ServerCopy"); if (arg->argCountInOut) printf(", CountInOut"); } void rtPrintRoutine(const routine_t *rt) { const argument_t *arg; printf("%s (%d) %s(", rtRoutineKindToStr(rt->rtKind), rt->rtNumber, rt->rtName); for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) rtPrintArg(arg); printf(")\n"); } /* * Determines appropriate value of msg-simple for the message, * and whether this value can vary at runtime. (If it can vary, * then the simple value is optimistically returned as TRUE.) * Uses itInName values, so useful when sending messages. */ static void rtCheckSimpleIn(const argument_t *args, u_int mask, boolean_t *fixed, boolean_t *simple) { const argument_t *arg; boolean_t MayBeComplex = FALSE; boolean_t MustBeComplex = FALSE; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheck(arg->argKind, mask)) { const ipc_type_t *it = arg->argType; if (it->itInName == MACH_MSG_TYPE_POLYMORPHIC) MayBeComplex = TRUE; if (it->itIndefinite) MayBeComplex = TRUE; if (MACH_MSG_TYPE_PORT_ANY(it->itInName) || !it->itInLine) MustBeComplex = TRUE; } *fixed = MustBeComplex || !MayBeComplex; *simple = !MustBeComplex; } /* * Determines appropriate value of msg-simple for the message, * and whether this value can vary at runtime. (If it can vary, * then the simple value is optimistically returned as TRUE.) * Uses itOutName values, so useful when receiving messages * (and sending reply messages in KernelServer interfaces). */ static void rtCheckSimpleOut(const argument_t *args, u_int mask, boolean_t *fixed, boolean_t *simple) { const argument_t *arg; boolean_t MayBeComplex = FALSE; boolean_t MustBeComplex = FALSE; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheck(arg->argKind, mask)) { const ipc_type_t *it = arg->argType; if (it->itOutName == MACH_MSG_TYPE_POLYMORPHIC) MayBeComplex = TRUE; if (it->itIndefinite) MayBeComplex = TRUE; if (MACH_MSG_TYPE_PORT_ANY(it->itOutName) || !it->itInLine) MustBeComplex = TRUE; } *fixed = MustBeComplex || !MayBeComplex; *simple = !MustBeComplex; } static u_int rtFindSize(const argument_t *args, u_int mask) { const argument_t *arg; u_int size = sizeof_mach_msg_header_t; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheck(arg->argKind, mask)) { ipc_type_t *it = arg->argType; /* might need proper alignment on demanding 64bit archies */ size = (size + word_size-1) & ~(word_size-1); if (arg->argLongForm) { size += sizeof_mach_msg_type_long_t; } else { size += sizeof_mach_msg_type_t; } size += it->itMinTypeSize; } return size; } boolean_t rtCheckMask(const argument_t *args, u_int mask) { const argument_t *arg; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheckAll(arg->argKind, mask)) return TRUE; return FALSE; } boolean_t rtCheckMaskFunction(const argument_t *args, u_int mask, boolean_t (*func)(const argument_t *)) { const argument_t *arg; for (arg = args; arg != argNULL; arg = arg->argNext) if (akCheckAll(arg->argKind, mask)) if ((*func)(arg)) return TRUE; return FALSE; } /* arg->argType may be NULL in this function */ static void rtDefaultArgKind(const routine_t *rt, argument_t *arg) { if ((arg->argKind == akNone) && (rt->rtRequestPort == argNULL)) arg->argKind = akRequestPort; if (arg->argKind == akNone) arg->argKind = akIn; } /* * Initializes arg->argDeallocate, arg->argLongForm, * arg->argServerCopy, arg->argCountInOut from arg->argFlags. */ static void rtProcessArgFlags(argument_t *arg) { const ipc_type_t *it = arg->argType; arg->argFlags = itCheckFlags(arg->argFlags, arg->argName); if (((IsKernelServer && akCheck(arg->argKind, akbReturn)) || (IsKernelUser && akCheck(arg->argKind, akbSend))) && (arg->argFlags & flDealloc) && (it->itDeallocate == d_NO)) { /* * For a KernelServer interface and an Out argument, * or a KernelUser interface and an In argument, * we avoid a possible spurious warning about the deallocate bit. * For compatibility with Mach 2.5, the deallocate bit * may need to be enabled on some inline arguments. */ arg->argDeallocate = d_YES; } else arg->argDeallocate = itCheckDeallocate(it, arg->argFlags, it->itDeallocate, arg->argName); arg->argLongForm = itCheckIsLong(it, arg->argFlags, it->itLongForm, arg->argName); if (arg->argFlags & flServerCopy) { if (it->itIndefinite && akCheck(arg->argKind, akbSend)) arg->argServerCopy = TRUE; else warn("%s: ServerCopy on argument is meaningless", arg->argName); } if (arg->argFlags & flCountInOut) { if (it->itVarArray && it->itInLine && akCheck(arg->argKind, akbReply)) arg->argCountInOut = TRUE; else warn("%s: CountInOut on argument is meaningless", arg->argName); } } static void rtAugmentArgKind(argument_t *arg) { ipc_type_t *it = arg->argType; /* akbVariable means variable-sized inline. */ if (it->itVarArray && it->itInLine) { if (akCheckAll(arg->argKind, akbRequest|akbReply)) error("%s: Inline variable-sized arguments can't be InOut", arg->argName); arg->argKind = akAddFeature(arg->argKind, akbVariable); /* akbIndefinite means inline or out-of-line */ if (it->itIndefinite) arg->argKind = akAddFeature(arg->argKind, akbIndefinite); } /* * Kernel servers can't do quick-checking of request arguments * which are out-of-line or ports, because the deallocate bit isn't * predictable. This is because the deallocate bit is preserved * at message copyin time and normalized during message copyout. * This accomodates old IPC programs which expect the deallocate * bit to be preserved. */ if (akCheck(arg->argKind, akbRequest) && !arg->argLongForm && (it->itOutName != MACH_MSG_TYPE_POLYMORPHIC) && !it->itVarArray && !(IsKernelServer && (!it->itInLine || MACH_MSG_TYPE_PORT_ANY(it->itOutName)))) arg->argKind = akAddFeature(arg->argKind, akbRequestQC); if (akCheck(arg->argKind, akbReply) && !arg->argLongForm && (it->itOutName != MACH_MSG_TYPE_POLYMORPHIC) && !it->itVarArray) arg->argKind = akAddFeature(arg->argKind, akbReplyQC); /* * Need to use a local variable in the following cases: * 1) There is a translate-out function & the argument is being * returned. We need to translate it before it hits the message. * 2) There is a translate-in function & the argument is * sent and returned. We need a local variable for its address. * 3) There is a destructor function, which will be used * (SendRcv and not ReturnSnd), and there is a translate-in * function whose value must be saved for the destructor. * 4) This is a count arg, getting returned. The count can't get * stored directly into the msg-type, because the msg-type won't * get initialized until later, and that would trash the count. * 5) This is a poly arg, getting returned. The name can't get * stored directly into the msg-type, because the msg-type won't * get initialized until later, and that would trash the name. * 6) This is a dealloc arg, being returned. The name can't be * stored directly into the msg_type, because the msg-type * field is a bit-field. * 7) There is a payload-aware translate-in function defined. */ if (((it->itOutTrans != strNULL) && akCheck(arg->argKind, akbReturnSnd)) || ((it->itInTrans != strNULL) && akCheckAll(arg->argKind, akbSendRcv|akbReturnSnd)) || ((it->itDestructor != strNULL) && akCheck(arg->argKind, akbSendRcv) && !akCheck(arg->argKind, akbReturnSnd) && (it->itInTrans != strNULL)) || ((akIdent(arg->argKind) == akeCount) && akCheck(arg->argKind, akbReturnSnd)) || ((akIdent(arg->argKind) == akePoly) && akCheck(arg->argKind, akbReturnSnd)) || ((akIdent(arg->argKind) == akeDealloc) && akCheck(arg->argKind, akbReturnSnd)) || (it->itInTransPayload != strNULL)) { arg->argKind = akRemFeature(arg->argKind, akbReplyCopy); arg->argKind = akAddFeature(arg->argKind, akbVarNeeded); } /* * If the argument is a variable-length array that can be passed in-line * or out-of-line, and is being returned, the server procedure * is passed a pointer to the buffer, which it can change. */ if (it->itIndefinite && akCheck(arg->argKind, akbReturnSnd)) { arg->argKind = akAddFeature(arg->argKind, akbPointer); } } /* arg->argType may be NULL in this function */ static void rtCheckRoutineArg(routine_t *rt, argument_t *arg) { switch (akIdent(arg->argKind)) { case akeRequestPort: if (rt->rtRequestPort != argNULL) warn("multiple RequestPort args in %s; %s won't be used", rt->rtName, rt->rtRequestPort->argName); rt->rtRequestPort = arg; break; case akeReplyPort: if (akCheck (arg->argKind, akbUserArg)) { if (rt->rtUReplyPort != argNULL) warn("multiple UserReplyPort args in %s; %s won't be used", rt->rtName, rt->rtUReplyPort->argName); rt->rtUReplyPort = arg; } if (akCheck (arg->argKind, akbServerArg)) { if (rt->rtSReplyPort != argNULL) warn("multiple ServerReplyPort args in %s; %s won't be used", rt->rtName, rt->rtSReplyPort->argName); rt->rtSReplyPort = arg; } break; case akeWaitTime: if (rt->rtWaitTime != argNULL) warn("multiple WaitTime args in %s; %s won't be used", rt->rtName, rt->rtWaitTime->argName); rt->rtWaitTime = arg; break; case akeMsgOption: if (rt->rtMsgOption != argNULL) warn("multiple MsgOption args in %s; %s won't be used", rt->rtName, rt->rtMsgOption->argName); rt->rtMsgOption = arg; break; case akeMsgSeqno: if (rt->rtMsgSeqno != argNULL) warn("multiple MsgSeqno args in %s; %s won't be used", rt->rtName, rt->rtMsgSeqno->argName); rt->rtMsgSeqno = arg; break; case akeReturn: if (rt->rtReturn != argNULL) warn("multiple Return args in %s; %s won't be used", rt->rtName, rt->rtReturn->argName); rt->rtReturn = arg; break; default: break; } } /* arg->argType may be NULL in this function */ static void rtSetArgDefaults(routine_t *rt, argument_t *arg) { arg->argRoutine = rt; if (arg->argVarName == strNULL) arg->argVarName = arg->argName; if (arg->argMsgField == strNULL) switch(akIdent(arg->argKind)) { case akeRequestPort: arg->argMsgField = "Head.msgh_request_port"; break; case akeReplyPort: arg->argMsgField = "Head.msgh_reply_port"; break; case akeMsgSeqno: arg->argMsgField = "Head.msgh_seqno"; break; default: arg->argMsgField = arg->argName; break; } if (arg->argTTName == strNULL) arg->argTTName = strconcat(arg->argName, "Type"); if (arg->argPadName == strNULL) arg->argPadName = strconcat(arg->argName, "Pad"); /* * The poly args for the request and reply ports have special defaults, * because their msg-type-name values aren't stored in normal fields. */ if ((rt->rtRequestPort != argNULL) && (rt->rtRequestPort->argPoly == arg) && (arg->argType != itNULL)) { arg->argMsgField = "Head.msgh_bits"; arg->argType->itInTrans = "MACH_MSGH_BITS_REQUEST"; } if ((rt->rtUReplyPort != argNULL) && (rt->rtUReplyPort->argPoly == arg) && (arg->argType != itNULL)) { arg->argMsgField = "Head.msgh_bits"; arg->argType->itInTrans = "MACH_MSGH_BITS_REPLY"; } if ((rt->rtSReplyPort != argNULL) && (rt->rtSReplyPort->argPoly == arg) && (arg->argType != itNULL)) { arg->argMsgField = "Head.msgh_bits"; arg->argType->itInTrans = "MACH_MSGH_BITS_REPLY"; } } static void rtAddCountArg(argument_t *arg) { argument_t *count; count = argAlloc(); count->argName = strconcat(arg->argName, "Cnt"); count->argType = itMakeCountType(); count->argParent = arg; count->argMultiplier = arg->argType->itElement->itNumber; count->argNext = arg->argNext; arg->argNext = count; arg->argCount = count; if (arg->argType->itString) { /* C String gets no Count argument on either side. There is no explicit field in the message - the count is passed as part of the descriptor. */ count->argKind = akeCount; count->argVarName = (char *)0; } else count->argKind = akAddFeature(akCount, akCheck(arg->argKind, akbSendReturnBits)); if (arg->argLongForm) count->argMsgField = strconcat(arg->argTTName, ".msgtl_number"); else count->argMsgField = strconcat(arg->argTTName, ".msgt_number"); } static void rtAddCountInOutArg(argument_t *arg) { argument_t *count; /* * The user sees a single count variable. However, to get the * count passed from user to server for variable-sized inline OUT * arrays, we need two count arguments internally. This is * because the count value lives in different message fields (and * is scaled differently) in the request and reply messages. * * The two variables have the same name to simplify code generation. * * This variable has a null argParent field because it has akbRequest. * For example, see rtCheckVariable. */ count = argAlloc(); count->argName = strconcat(arg->argName, "Cnt"); count->argType = itMakeCountType(); count->argParent = argNULL; count->argNext = arg->argNext; arg->argNext = count; (count->argCInOut = arg->argCount)->argCInOut = count; count->argKind = akCountInOut; } static void rtAddPolyArg(argument_t *arg) { const ipc_type_t *it = arg->argType; argument_t *poly; arg_kind_t akbsend, akbreturn; poly = argAlloc(); poly->argName = strconcat(arg->argName, "Poly"); poly->argType = itMakePolyType(); poly->argParent = arg; poly->argNext = arg->argNext; arg->argNext = poly; arg->argPoly = poly; /* * akbsend is bits added if the arg is In; * akbreturn is bits added if the arg is Out. * The mysterious business with KernelServer subsystems: * when packing Out arguments, they use OutNames instead * of InNames, and the OutName determines if they are poly-in * as well as poly-out. */ akbsend = akbSend|akbSendBody; akbreturn = akbReturn|akbReturnBody; if (it->itInName == MACH_MSG_TYPE_POLYMORPHIC) { akbsend |= akbUserArg|akbSendSnd; if (!IsKernelServer) akbreturn |= akbServerArg|akbReturnSnd; } if (it->itOutName == MACH_MSG_TYPE_POLYMORPHIC) { akbsend |= akbServerArg|akbSendRcv; akbreturn |= akbUserArg|akbReturnRcv; if (IsKernelServer) akbreturn |= akbServerArg|akbReturnSnd; } poly->argKind = akPoly; if (akCheck(arg->argKind, akbSend)) poly->argKind = akAddFeature(poly->argKind, akCheck(arg->argKind, akbsend)); if (akCheck(arg->argKind, akbReturn)) poly->argKind = akAddFeature(poly->argKind, akCheck(arg->argKind, akbreturn)); if (arg->argLongForm) poly->argMsgField = strconcat(arg->argTTName, ".msgtl_name"); else poly->argMsgField = strconcat(arg->argTTName, ".msgt_name"); } static void rtAddDeallocArg(argument_t *arg) { argument_t *dealloc; dealloc = argAlloc(); dealloc->argName = strconcat(arg->argName, "Dealloc"); dealloc->argType = itMakeDeallocType(); dealloc->argParent = arg; dealloc->argNext = arg->argNext; arg->argNext = dealloc; arg->argDealloc = dealloc; /* * For Indefinite types, we leave out akbSendSnd and akbReturnSnd * so that the normal argument-packing is bypassed. The special code * generated for the Indefinite argument handles the deallocate bit. * (It can only be enabled if the data is actually out-of-line.) */ dealloc->argKind = akeDealloc; if (akCheck(arg->argKind, akbSend)) dealloc->argKind = akAddFeature(dealloc->argKind, akCheck(arg->argKind, akbUserArg|akbSend|akbSendBody| (arg->argType->itIndefinite ? 0 : akbSendSnd))); if (akCheck(arg->argKind, akbReturn)) { dealloc->argKind = akAddFeature(dealloc->argKind, akCheck(arg->argKind, akbServerArg|akbReturn|akbReturnBody| (arg->argType->itIndefinite ? 0 : akbReturnSnd))); /* * Without akbReturnSnd, rtAugmentArgKind will not add * akbVarNeeded and rtAddByReference will not set * argByReferenceServer. So we do it here. */ if (arg->argType->itIndefinite) { dealloc->argKind = akAddFeature(dealloc->argKind, akbVarNeeded); dealloc->argByReferenceServer = TRUE; } } if (arg->argLongForm) dealloc->argMsgField = strconcat(arg->argTTName, ".msgtl_header.msgt_deallocate"); else dealloc->argMsgField = strconcat(arg->argTTName, ".msgt_deallocate"); } static void rtAddSCopyArg(argument_t *arg) { argument_t *scopy; scopy = argAlloc(); scopy->argName = strconcat(arg->argName, "SCopy"); scopy->argType = itMakeDeallocType(); scopy->argParent = arg; scopy->argNext = arg->argNext; arg->argNext = scopy; arg->argSCopy = scopy; scopy->argKind = akServerCopy; if (arg->argLongForm) scopy->argMsgField = strconcat(arg->argTTName, ".msgtl_header.msgt_inline"); else scopy->argMsgField = strconcat(arg->argTTName, ".msgt_inline"); } static void rtCheckRoutineArgs(routine_t *rt) { argument_t *arg; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { const ipc_type_t *it = arg->argType; rtDefaultArgKind(rt, arg); rtCheckRoutineArg(rt, arg); /* need to set argTTName before adding implicit args */ rtSetArgDefaults(rt, arg); /* the arg may not have a type (if there was some error in parsing it), in which case we don't want to do these steps. */ if (it != itNULL) { /* need to set argLongForm before adding implicit args */ rtProcessArgFlags(arg); rtAugmentArgKind(arg); /* args added here will get processed in later iterations */ /* order of args is 'arg poly countinout count dealloc scopy' */ if (arg->argServerCopy) rtAddSCopyArg(arg); if (arg->argDeallocate == d_MAYBE) rtAddDeallocArg(arg); if (it->itVarArray) rtAddCountArg(arg); if (arg->argCountInOut) rtAddCountInOutArg(arg); if ((it->itInName == MACH_MSG_TYPE_POLYMORPHIC) || (it->itOutName == MACH_MSG_TYPE_POLYMORPHIC)) rtAddPolyArg(arg); } } } static void rtCheckArgTypes(routine_t *rt) { if (rt->rtRequestPort == argNULL) error("%s %s doesn't have a server port argument", rtRoutineKindToStr(rt->rtKind), rt->rtName); if (rt->rtReturn != argNULL) error("routine %s has a return arg", rt->rtName); if (rt->rtReturn == argNULL) rt->rtReturn = rt->rtRetCode; rt->rtServerReturn = rt->rtReturn; if ((rt->rtReturn != argNULL) && (rt->rtReturn->argType != itNULL)) itCheckReturnType(rt->rtReturn->argName, rt->rtReturn->argType); if ((rt->rtRequestPort != argNULL) && (rt->rtRequestPort->argType != itNULL)) itCheckRequestPortType(rt->rtRequestPort->argName, rt->rtRequestPort->argType); if ((rt->rtUReplyPort != argNULL) && (rt->rtUReplyPort->argType != itNULL)) itCheckReplyPortType(rt->rtUReplyPort->argName, rt->rtUReplyPort->argType); if ((rt->rtSReplyPort != argNULL) && (rt->rtSReplyPort->argType != itNULL)) itCheckReplyPortType(rt->rtSReplyPort->argName, rt->rtSReplyPort->argType); if ((rt->rtWaitTime != argNULL) && (rt->rtWaitTime->argType != itNULL)) itCheckNaturalType(rt->rtWaitTime->argName, rt->rtWaitTime->argType); if ((rt->rtMsgOption != argNULL) && (rt->rtMsgOption->argType != itNULL)) itCheckNaturalType(rt->rtMsgOption->argName, rt->rtMsgOption->argType); if ((rt->rtMsgSeqno != argNULL) && (rt->rtMsgSeqno->argType != itNULL)) itCheckNaturalType(rt->rtMsgSeqno->argName, rt->rtMsgSeqno->argType); } /* * Check for arguments which are missing seemingly needed functions. * We make this check here instead of in itCheckDecl, because here * we can take into account what kind of argument the type is * being used with. * * These are warnings, not hard errors, because mig will generate * reasonable code in any case. The generated code will work fine * if the ServerType and TransType are really the same, even though * they have different names. */ static void rtCheckArgTrans(const routine_t *rt) { const argument_t *arg; /* the arg may not have a type (if there was some error in parsing it) */ for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { const ipc_type_t *it = arg->argType; if ((it != itNULL) && !streql(it->itServerType, it->itTransType)) { if (akCheck(arg->argKind, akbSendRcv) && (it->itInTrans == strNULL)) warn("%s: argument has no in-translation function", arg->argName); if (akCheck(arg->argKind, akbReturnSnd) && (it->itOutTrans == strNULL)) warn("%s: argument has no out-translation function", arg->argName); } } } /* * Adds an implicit return-code argument. It exists in the reply message, * where it is the first piece of data. Even if there is no reply * message (rtOneWay is true), we generate the argument because * the server-side stub needs a dummy reply msg to return error codes * back to the server loop. */ static void rtAddRetCode(routine_t *rt) { argument_t *arg = argAlloc(); arg->argName = "RetCode"; arg->argType = itRetCodeType; arg->argKind = akRetCode; rt->rtRetCode = arg; /* add at beginning, so return-code is first in the reply message */ arg->argNext = rt->rtArgs; rt->rtArgs = arg; } /* * Adds a dummy WaitTime argument to the function. * This argument doesn't show up in any C argument lists; * it implements the global WaitTime statement. */ static void rtAddWaitTime(routine_t *rt, identifier_t name) { argument_t *arg = argAlloc(); argument_t **loc; arg->argName = "dummy WaitTime arg"; arg->argVarName = name; arg->argType = itWaitTimeType; arg->argKind = akeWaitTime; rt->rtWaitTime = arg; /* add wait-time after msg-option, if possible */ if (rt->rtMsgOption != argNULL) loc = &rt->rtMsgOption->argNext; else loc = &rt->rtArgs; arg->argNext = *loc; *loc = arg; rtSetArgDefaults(rt, arg); } /* * Adds a dummy MsgOption argument to the function. * This argument doesn't show up in any C argument lists; * it implements the global MsgOption statement. */ static void rtAddMsgOption(routine_t *rt, identifier_t name) { argument_t *arg = argAlloc(); argument_t **loc; arg->argName = "dummy MsgOption arg"; arg->argVarName = name; arg->argType = itMsgOptionType; arg->argKind = akeMsgOption; rt->rtMsgOption = arg; /* add msg-option after msg-seqno */ if (rt->rtMsgSeqno != argNULL) loc = &rt->rtMsgSeqno->argNext; else loc = &rt->rtArgs; arg->argNext = *loc; *loc = arg; rtSetArgDefaults(rt, arg); } /* * Adds a dummy reply port argument to the function. If USER is true, the * user reply port is set, otherwise the server. */ static void rtAddDummyReplyPort(routine_t *rt, ipc_type_t *type, int user) { argument_t *arg = argAlloc(); argument_t **loc; arg->argName = "dummy ReplyPort arg"; arg->argVarName = "dummy ReplyPort arg"; arg->argType = type; arg->argKind = akeReplyPort; if (user) rt->rtUReplyPort = arg; else rt->rtSReplyPort = arg; /* add the reply port after the request port */ if (rt->rtRequestPort != argNULL) loc = &rt->rtRequestPort->argNext; else loc = &rt->rtArgs; arg->argNext = *loc; *loc = arg; rtSetArgDefaults(rt, arg); } /* * Initializes argRequestPos, argReplyPos, rtMaxRequestPos, rtMaxReplyPos, * rtNumRequestVar, rtNumReplyVar, and adds akbVarNeeded to those arguments * that need it because of variable-sized inline considerations. * * argRequestPos and argReplyPos get -1 if the value shouldn't be used. */ static void rtCheckVariable(routine_t *rt) { argument_t *arg; int NumRequestVar = 0; int NumReplyVar = 0; int MaxRequestPos = 0; int MaxReplyPos = 0; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { argument_t *parent = arg->argParent; if (parent == argNULL) { if (akCheck(arg->argKind, akbRequest|akbSend)) { arg->argRequestPos = NumRequestVar; MaxRequestPos = NumRequestVar; if (akCheck(arg->argKind, akbVariable)) NumRequestVar++; } else arg->argRequestPos = -1; if (akCheck(arg->argKind, akbReply|akbReturn)) { arg->argReplyPos = NumReplyVar; MaxReplyPos = NumReplyVar; if (akCheck(arg->argKind, akbVariable)) NumReplyVar++; } else arg->argReplyPos = -1; } else { arg->argRequestPos = parent->argRequestPos; arg->argReplyPos = parent->argReplyPos; } /* Out variables that follow a variable-sized field need VarNeeded or ReplyCopy; they can't be stored directly into the reply message. */ if (akCheck(arg->argKind, akbReturnSnd) && !akCheck(arg->argKind, akbReplyCopy|akbVarNeeded) && (arg->argReplyPos > 0)) arg->argKind = akAddFeature(arg->argKind, akbVarNeeded); } rt->rtNumRequestVar = NumRequestVar; rt->rtNumReplyVar = NumReplyVar; rt->rtMaxRequestPos = MaxRequestPos; rt->rtMaxReplyPos = MaxReplyPos; } /* * Adds akbDestroy where needed. */ static void rtCheckDestroy(routine_t *rt) { argument_t *arg; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { const ipc_type_t *it = arg->argType; if(akCheck(arg->argKind, akbSendRcv) && !akCheck(arg->argKind, akbReturnSnd)) { if ((it->itDestructor != strNULL) || (akCheck(arg->argKind, akbIndefinite) && !arg->argServerCopy)) arg->argKind = akAddFeature(arg->argKind, akbDestroy); } } } /* * Sets ByReferenceUser and ByReferenceServer. */ static void rtAddByReference(routine_t *rt) { argument_t *arg; for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { const ipc_type_t *it = arg->argType; if (akCheck(arg->argKind, akbReturnRcv) && (it->itStruct || it->itIndefinite)) { arg->argByReferenceUser = TRUE; /* * A CountInOut arg itself is not akbReturnRcv, * so we need to set argByReferenceUser specially. */ if (arg->argCInOut != argNULL) arg->argCInOut->argByReferenceUser = TRUE; } if (akCheck(arg->argKind, akbReturnSnd) && (it->itStruct || it->itIndefinite)) arg->argByReferenceServer = TRUE; } } void rtCheckRoutine(routine_t *rt) { /* Initialize random fields. */ rt->rtOneWay = (rt->rtKind == rkSimpleRoutine); rt->rtServerName = strconcat(ServerPrefix, rt->rtName); rt->rtServerName = strconcat(RoutinePrefix, rt->rtServerName); rt->rtUserName = strconcat(UserPrefix, rt->rtName); rt->rtUserName = strconcat(RoutinePrefix, rt->rtUserName); /* Add implicit arguments. */ rtAddRetCode(rt); /* Check out the arguments and their types. Add count, poly implicit args. Any arguments added after rtCheckRoutineArgs should have rtSetArgDefaults called on them. */ rtCheckRoutineArgs(rt); /* Add dummy WaitTime and MsgOption arguments, if the routine doesn't have its own args and the user specified global values. */ if (rt->rtUReplyPort == argNULL) { if (rt->rtOneWay) rtAddDummyReplyPort(rt, itZeroReplyPortType, 1); else rtAddDummyReplyPort(rt, itRealReplyPortType, 1); } if (rt->rtSReplyPort == argNULL) { if (rt->rtOneWay) rtAddDummyReplyPort(rt, itZeroReplyPortType, 0); else rtAddDummyReplyPort(rt, itRealReplyPortType, 0); } if (rt->rtMsgOption == argNULL) { if (MsgOption == strNULL) rtAddMsgOption(rt, "MACH_MSG_OPTION_NONE"); else rtAddMsgOption(rt, MsgOption); } if ((rt->rtWaitTime == argNULL) && (WaitTime != strNULL)) rtAddWaitTime(rt, WaitTime); /* Now that all the arguments are in place, do more checking. */ rtCheckArgTypes(rt); rtCheckArgTrans(rt); if (rt->rtOneWay && rtCheckMask(rt->rtArgs, akbReturn)) error("%s %s has OUT argument", rtRoutineKindToStr(rt->rtKind), rt->rtName); /* If there were any errors, don't bother calculating more info that is only used in code generation anyway. Therefore, the following functions don't have to worry about null types. */ if (errors > 0) return; rtCheckSimpleIn(rt->rtArgs, akbRequest, &rt->rtSimpleFixedRequest, &rt->rtSimpleSendRequest); rtCheckSimpleOut(rt->rtArgs, akbRequest, &rt->rtSimpleCheckRequest, &rt->rtSimpleReceiveRequest); rt->rtRequestSize = rtFindSize(rt->rtArgs, akbRequest); if (IsKernelServer) rtCheckSimpleOut(rt->rtArgs, akbReply, &rt->rtSimpleFixedReply, &rt->rtSimpleSendReply); else rtCheckSimpleIn(rt->rtArgs, akbReply, &rt->rtSimpleFixedReply, &rt->rtSimpleSendReply); rtCheckSimpleOut(rt->rtArgs, akbReply, &rt->rtSimpleCheckReply, &rt->rtSimpleReceiveReply); rt->rtReplySize = rtFindSize(rt->rtArgs, akbReply); rtCheckVariable(rt); rtCheckDestroy(rt); rtAddByReference(rt); rt->rtNoReplyArgs = !rtCheckMask(rt->rtArgs, akbReturnSnd); }