diff options
Diffstat (limited to 'mig/routine.c')
-rw-r--r-- | mig/routine.c | 1334 |
1 files changed, 1334 insertions, 0 deletions
diff --git a/mig/routine.c b/mig/routine.c new file mode 100644 index 0000000..70707bd --- /dev/null +++ b/mig/routine.c @@ -0,0 +1,1334 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> + +#include "error.h" +#include "global.h" +#include "routine.h" +#include "message.h" + +u_int rtNumber = 0; + +routine_t * +rtAlloc(void) +{ + register 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->rtErrorName = 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 */ + }; + register 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) +{ + register routine_t *rt = rtAlloc(); + + rt->rtName = name; + rt->rtKind = rkRoutine; + rt->rtArgs = args; + + return rt; +} + +routine_t * +rtMakeSimpleRoutine(identifier_t name, argument_t *args) +{ + register routine_t *rt = rtAlloc(); + + rt->rtName = name; + rt->rtKind = rkSimpleRoutine; + rt->rtArgs = args; + + return rt; +} + +routine_t * +rtMakeProcedure(identifier_t name, argument_t *args) +{ + register routine_t *rt = rtAlloc(); + + rt->rtName = name; + rt->rtKind = rkProcedure; + rt->rtArgs = args; + + warn("Procedure %s: obsolete routine kind", name); + + return rt; +} + +routine_t * +rtMakeSimpleProcedure(identifier_t name, argument_t *args) +{ + register routine_t *rt = rtAlloc(); + + rt->rtName = name; + rt->rtKind = rkSimpleProcedure; + rt->rtArgs = args; + + warn("SimpleProcedure %s: obsolete routine kind", name); + + return rt; +} + +routine_t * +rtMakeFunction(identifier_t name, argument_t *args, ipc_type_t *type) +{ + register routine_t *rt = rtAlloc(); + register argument_t *ret = argAlloc(); + + ret->argName = name; + ret->argKind = akReturn; + ret->argType = type; + ret->argNext = args; + + rt->rtName = name; + rt->rtKind = rkFunction; + rt->rtArgs = ret; + + warn("Function %s: obsolete routine kind", name); + + return rt; +} + +const char * +rtRoutineKindToStr(routine_kind_t rk) +{ + switch (rk) + { + case rkRoutine: + return "Routine"; + case rkSimpleRoutine: + return "SimpleRoutine"; + case rkProcedure: + return "Procedure"; + case rkSimpleProcedure: + return "SimpleProcedure"; + case rkFunction: + return "Function"; + default: + fatal("rtRoutineKindToStr(%d): not a routine_kind_t", rk); + /*NOTREACHED*/ + } +} + +static void +rtPrintArg(register const argument_t *arg) +{ + register 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(register const routine_t *rt) +{ + register 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); + + if (rt->rtKind == rkFunction) + printf("): %s\n", rt->rtReturn->argType->itName); + else + printf(")\n"); + + 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) +{ + register 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)) + { + register 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) +{ + register 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)) + { + register 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) +{ + register 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)) + { + register ipc_type_t *it = arg->argType; + + if (arg->argLongForm) { + /* might need proper alignment on 64bit archies */ + size = (size + word_size-1) & ~(word_size-1); + size += sizeof_mach_msg_type_long_t; + } else { + register bs = (it->itSize / 8); /* in bytes */ + size += (bs > sizeof_mach_msg_type_t) ? bs : sizeof_mach_msg_type_t; + } + + size += it->itMinTypeSize; + } + + return size; +} + +boolean_t +rtCheckMask(const argument_t *args, u_int mask) +{ + register 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 *)) +{ + register 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(register argument_t *arg) +{ + register 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) +{ + register 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. + */ + + 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))) + { + 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, register 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(register argument_t *arg) +{ + register 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(register argument_t *arg) +{ + register 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(register argument_t *arg) +{ + register const ipc_type_t *it = arg->argType; + register 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(register argument_t *arg) +{ + register 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(register argument_t *arg) +{ + register 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) +{ + register argument_t *arg; + + for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) + { + register 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->rtKind == rkFunction) && + (rt->rtReturn == argNULL)) + error("Function %s doesn't have a return arg", rt->rtName); + + if ((rt->rtKind != rkFunction) && + (rt->rtReturn != argNULL)) + error("non-function %s has a return arg", rt->rtName); + + if ((rt->rtReturn == argNULL) && !rt->rtProcedure) + 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)) + itCheckIntType(rt->rtWaitTime->argName, + rt->rtWaitTime->argType); + + if ((rt->rtMsgOption != argNULL) && + (rt->rtMsgOption->argType != itNULL)) + itCheckIntType(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) +{ + register 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) + { + register 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) +{ + register 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) +{ + register 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) +{ + register 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) +{ + register 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(register routine_t *rt) +{ + register argument_t *arg; + int NumRequestVar = 0; + int NumReplyVar = 0; + int MaxRequestPos = 0; + int MaxReplyPos = 0; + + for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { + register 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(register routine_t *rt) +{ + register argument_t *arg; + + for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { + register 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(register routine_t *rt) +{ + register argument_t *arg; + + for (arg = rt->rtArgs; arg != argNULL; arg = arg->argNext) { + register 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(register routine_t *rt) +{ + /* Initialize random fields. */ + + rt->rtErrorName = ErrorProc; + rt->rtOneWay = ((rt->rtKind == rkSimpleProcedure) || + (rt->rtKind == rkSimpleRoutine)); + rt->rtProcedure = ((rt->rtKind == rkProcedure) || + (rt->rtKind == rkSimpleProcedure)); + rt->rtUseError = rt->rtProcedure || (rt->rtKind == rkFunction); + 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); + + if (rt->rtKind == rkFunction) + rt->rtNoReplyArgs = FALSE; + else + rt->rtNoReplyArgs = !rtCheckMask(rt->rtArgs, akbReturnSnd); +} |