-- File [Ivy]<Nelson>Lupine>LupineDeclareStubsImpl.mesa.
-- Last edited by BZM on 14-May-82 13:06:34.

-- This module cooperates with LupineDeclare*Impl to export LupineDeclare.


DIRECTORY
  CWF USING [SWF1, WF1],
  LongString USING [EqualStrings],
  LupineDeclare USING [
	DispatcherType, PktSite,
	TransferDeclaration, TransferName, TransferSite,
	WriteParameterName, WriteTypeName ],
  LupineDeclarePrivate USING [DispatcherName, GetString],
  LupineManagerPrivate USING [
	AllocString, Indent, IsNull, MaxIdentifierLength,
	Nest, Options, String, StringNIL,
	WFL, WFL1, WFLL, WFS, WFS1, WFSL ],
  LupineMarshal USING [
	BeginAllocation, EndAllocation,
	EnumerateParams, FieldInfo, FreeParamInfo, FromPacket,
	MakeParamInfo, ParamProcedure, ParamInfo, ParamIndex, ParamLocation,
	ToPacket, VariableNames, Words ],
  LupineSymbolTable USING [
	GetTypeInfo, IsAnonymous, SymbolHandle,
	TransferTypes, TypeHandle, TypeInfo ];


LupineDeclareStubsImpl: PROGRAM
  IMPORTS
	CWF, Declare: LupineDeclare, LongString, LupineManagerPrivate,
	Marshal: LupineMarshal, Private: LupineDeclarePrivate,
	ST: LupineSymbolTable 
  EXPORTS LupineDeclare
  = BEGIN OPEN LupineManagerPrivate, LupineDeclare;


  ParamInfo: TYPE = Marshal.ParamInfo;
  ParamLocation: TYPE = Marshal.ParamLocation;

  TransferInfo: TYPE = Transfer ST.TypeInfo;


-- Public code generation routines.

  ProcedureStub: PUBLIC PROCEDURE [
	transfer: ST.SymbolHandle,
	transferType: ST.TypeHandle,
	transferDeclaration: TransferDeclaration,
	signalDispatcherType: DispatcherType,
	options: Options,
	nest: Nest ] =
    BEGIN
    nameString: AllocString = [MaxIdentifierLength];
    procName: String = Declare.TransferName[transfer, nameString];
    transferInfo: TransferInfo = GetTransferInfo[transferType];
    argInfo, resultInfo: ParamInfo;
    [argInfo, resultInfo] ← GetArgResultInfo[
	transferInfo: transferInfo,
	transferDeclaration: transferDeclaration,
	transferSite: caller,  pktSite: stubFrame,
	options: options ];
    BEGIN ENABLE UNWIND => FreeArgResultInfo[argInfo, resultInfo];
    WFS1["*N"L];
    TransferHeader[ nest: nest,
	transferName: procName,
	transferInfo: transferInfo,
	argInfo: argInfo, resultInfo: resultInfo,
	public: transferDeclaration=inInterface ];
    WFS1[" =*N"L];
    CallingStubBody[ nest: nest+1,
	transferName: procName, blockName: procName,
	transferInfo: transferInfo,
	signalDispatcher: Private.DispatcherName[signalDispatcherType],
	argInfo: argInfo, resultInfo: resultInfo];
    END; -- ENABLE UNWIND.
    FreeArgResultInfo[argInfo, resultInfo];
    END;

  DispatcherStubArm: PUBLIC PROCEDURE [
	transfer: ST.SymbolHandle,
	transferType: ST.TypeHandle,
	transferDeclaration: TransferDeclaration,
	options: Options,
	nest: Nest ] =
    BEGIN
    nameString: AllocString = [MaxIdentifierLength];
    transferName: String = Declare.TransferName[transfer, nameString];
    WFSL[ Indent[nest], transferName, " => RETURN[*N"L,
      Indent[nest+1], transferName, "Stub["L,
      Private.GetString[dispatcherArgs], "]];*N"L ];
    END;

  DispatcherStubBody: PUBLIC PROCEDURE [
	transfer: ST.SymbolHandle,
	transferType: ST.TypeHandle,
	transferDeclaration: TransferDeclaration,
	inlineBody: BOOLEAN,
	options: Options,
	nest: Nest ] =
    BEGIN
    nameString: AllocString = [MaxIdentifierLength];
    transferName: String = Declare.TransferName[transfer, nameString];
    transferInfo: TransferInfo = GetTransferInfo[transferType];
    argInfo, resultInfo: ParamInfo;
    stubName: AllocString = [MaxIdentifierLength];
    CWF.SWF1[stubName, "%LSStub"L, transferName];
      -- The stubName here must agree with that used in DispatcherStubArm.
    [argInfo, resultInfo] ← GetArgResultInfo[
	transferInfo: transferInfo,
	transferDeclaration: transferDeclaration,
	transferSite: callee,  pktSite: rpcRuntime,
	options: options ];
    BEGIN ENABLE UNWIND => FreeArgResultInfo[argInfo, resultInfo];
    WFSL["*N"L, Indent[nest], stubName, ": "L];
    TransferType[ nest: nest,
	transferInfo: transferInfo,
	argInfo: argInfo, resultInfo: resultInfo,
	printAsComment: TRUE ];
    WFS1[" RpcPrivate.Dispatcher =*N"L];
    CalledStubBody[ nest: nest+1,
	transferName: transferName, blockName: stubName,
	transferInfo: transferInfo,
	inlineBody: inlineBody,
	argInfo: argInfo, resultInfo: resultInfo ];
    END;  -- ENABLE UNWIND.
    FreeArgResultInfo[argInfo, resultInfo];
    END;

  SignalCode: PUBLIC PROCEDURE [
	transfer: ST.SymbolHandle,
	transferType: ST.TypeHandle,
	transferDeclaration: TransferDeclaration,
	options: Options,
	nest: Nest ] =
    BEGIN
    nameString: AllocString = [MaxIdentifierLength];
    transferInfo: TransferInfo = GetTransferInfo[transferType];
    argInfo, resultInfo: ParamInfo;
    [argInfo, resultInfo] ← GetArgResultInfo[
	transferInfo: transferInfo,
	transferDeclaration: transferDeclaration,
	transferSite: caller, pktSite: stubFrame,
	options: options ];
    BEGIN ENABLE UNWIND => FreeArgResultInfo[argInfo, resultInfo];
    WFS1["*N"L];
    TransferHeader[ nest: nest,
	transferName: Declare.TransferName[transfer, nameString],
	transferInfo: transferInfo,
	argInfo: argInfo, resultInfo: resultInfo,
	public: transferDeclaration=inInterface ];
    WFS1[" = CODE;*N"L];
    END; -- ENABLE UNWIND.
    FreeArgResultInfo[argInfo, resultInfo];
    END;  -- SignalCode.

  SignalCatchStub: PUBLIC PROCEDURE [
	transfer: ST.SymbolHandle,
	transferType: ST.TypeHandle,
	transferDeclaration: TransferDeclaration,
	signalDispatcherType: DispatcherType ← none,
	options: Options,
	nest: Nest ] =
    BEGIN
    nameString: AllocString = [MaxIdentifierLength];
    signalName: String = Declare.TransferName[transfer, nameString];
    transferInfo: TransferInfo = GetTransferInfo[transferType];
    argInfo, resultInfo: ParamInfo;
    [argInfo, resultInfo] ← GetArgResultInfo[
	transferInfo: transferInfo,
	transferDeclaration: transferDeclaration,
	transferSite: caller,  pktSite: rpcRuntime,
	options: options ];
    BEGIN ENABLE UNWIND => FreeArgResultInfo[argInfo, resultInfo];
    WFSL["*N"L, Indent[nest], signalName, " "L];
    TransferType[ nest: nest,
	printAsComment: TRUE,
	transferInfo: transferInfo,
	argInfo: argInfo, resultInfo: resultInfo ];
    WFS1[" =>*N"L];
    CallingStubBody[ nest: nest+1,
	transferName: signalName, blockName: signalName,
	transferInfo: transferInfo,
	signalDispatcher: Private.DispatcherName[signalDispatcherType],
	argInfo: argInfo, resultInfo: resultInfo ];
    END; -- ENABLE UNWIND.
    FreeArgResultInfo[argInfo, resultInfo];
    END;



-- Detailed code generation utilities.

  TransferHeader: PROCEDURE [
	transferName: String,
	transferInfo: TransferInfo,
	argInfo, resultInfo: ParamInfo,
	public: BOOLEAN,
	nest: Nest ] =
    BEGIN
    WFSL[Indent[nest], transferName,
      (IF public THEN ": PUBLIC "L ELSE ": "L),
      (IF transferInfo.safe THEN "SAFE "L ELSE ""L) ];
    TransferType[nest: nest,
      transferInfo: transferInfo, argInfo: argInfo, resultInfo: resultInfo];
    END;

  TransferType: PROCEDURE [
	transferInfo: TransferInfo,
	argInfo, resultInfo: ParamInfo,
	printAsComment: BOOLEAN←FALSE,
	nest: Nest ] =
    BEGIN
    TransferTypeName: ARRAY ST.TransferTypes OF STRING = [
      Procedure: "PROCEDURE"L, Error: "ERROR"L, Signal: "SIGNAL"L,
      Port: "PORT"L, Program: "PROGRAM"L, Process: "PROCESS"L,
      Other: "<<<ERROR>>>"L ];
    WFS[ (IF printAsComment THEN "--"L ELSE ""L),
      TransferTypeName[transferInfo.kind],
      (IF argInfo.paramCount>0 OR resultInfo.paramCount>0 THEN " "L ELSE ""L)];
    FullParameterList[ nest: nest,
	argInfo: argInfo, resultInfo: resultInfo,
	printingComment: printAsComment ];
    IF printAsComment THEN WFS1["--"L];
    END;


-- Generate Client procedure stubs and Server catch-phrase stubs.

  CallingStubBody: PROCEDURE [
	transferName, blockName: String,
	transferInfo: TransferInfo,
	signalDispatcher: String,
	argInfo, resultInfo: ParamInfo,
	nest: Nest ] =
    BEGIN
    CallPkt: String = "pkt"L; -- For signal stubs, this must be = Dispatcher's pkt.
    ReturnPkt: String = "pkt"L;  -- For signal stubs, ...
    CallLength: String = "pktLength"L;
    ReturnLength: String --must equal-- = CallLength;
    ReturnLimit: String = "returnLimit"L;  -- Not used at this time.
    LastPkt: String = "lastPkt"L;
    ArgPtr: String = "argPkt"L;
    ResultPtr: String = "resPkt"L;
    CallVars: Marshal.VariableNames = [
      pkt: CallPkt, overlayPtr: ArgPtr,
      pktLength: CallLength, pktLimit: ""L,
      lastPkt: LastPkt ];
    ReturnVars: Marshal.VariableNames = [
      pkt: ReturnPkt, overlayPtr: ResultPtr,
      pktLength: ReturnLength, pktLimit: ReturnLimit,
      lastPkt: LastPkt ];
    WFL[nest, (IF transferInfo.safe THEN "TRUSTED "L ELSE ""L), "BEGIN"L];
    SELECT transferInfo.kind FROM
      Signal, Error =>
	BEGIN
	--ParamsAsLocalVars[paramInfo: argInfo, ...];
	  -- Anonymous signal arguments are impossible to handle,
	  -- the compiler will complain.
	ParamsAsLocalVars [ nest: nest,
	  paramInfo: resultInfo, doOnlyAnonymousParams: TRUE ];
	  -- Declare locals corresponding to anon. AC result parameters.
	END;
      Procedure =>
	BEGIN
	--ParamsAsLocalVars[paramInfo: argInfo, ...];
	  -- All argument declarations already performed in the PROC header.
	ParamsAsLocalVars [ nest: nest,
	  paramInfo: resultInfo, doOnlyAddressParams: TRUE ];
	  -- AC results are not declared in the PROC header when there are
	  -- any static results (this saves space).  Declare them now.
	END;
      ENDCASE => ERROR;
    OverlayRecord[
      paramInfo: argInfo, transferIndexName: transferName, nest: nest ];
    OverlayRecord[paramInfo: resultInfo, nest: nest];
    SELECT transferInfo.kind FROM
      Signal, Error =>
	-- These reuse the Dispatcher's call pkt.
	IF ~LongString.EqualStrings[CallPkt, Private.GetString[dispatcherPkt]]
	  THEN ERROR;
      ENDCASE =>
	-- Procedures (and the rest) use a freshly declared pkt.
	LocalPacket[ nest: nest,
	  pktName: CallPkt,
	  size: MAX[argInfo.sizeOf.pktToAllocate, resultInfo.sizeOf.pktToAllocate] ];
    IF ~LongString.EqualStrings[CallPkt, ReturnPkt]
      THEN WFLL[nest, ReturnPkt, ": RpcPrivate.StubPkt = "L, CallPkt, ";"L];
    OverlayPointer[ nest: nest,
      paramInfo: argInfo,
      pktName: CallPkt, overlayPtrName: ArgPtr,
      do: both ];
    OverlayPointer[ nest: nest,
      paramInfo: resultInfo,
      pktName: ReturnPkt, overlayPtrName: ResultPtr,
      do: both ];
    PacketLength[ nest: nest,
      paramInfo: argInfo,
      pktLengthName: CallLength,
      declare: yes, assign: yes ];
    WFL[nest, LastPkt, ": BOOLEAN;"L];
    SELECT transferInfo.kind FROM
      Procedure =>
        BEGIN
        WFSL[Indent[nest], "RpcPrivate.StartCall[callPkt: "L, CallPkt,
          ", interface: myInterface"L ];
        IF argInfo.hasConversation THEN DoConversationArg[argInfo];
        WFS1["];*N"L];
	END;
      Signal, Error =>
	WFL[nest, "RpcPrivate.StartSignal[signalPkt: "L, CallPkt, "];"L];
      ENDCASE => ERROR;
    IF ~argInfo.hasOverlayParams
      THEN WFLL[nest, ArgPtr, ".transferIndex ← "L, transferName, ";"L]; 
    Marshal.ToPacket[ nest: nest,
      paramInfo: argInfo,
      overlayHandling: [static: copyToPkt],
      varNames: CallVars ];
    WFSL[Indent[nest],
      "[returnLength: , lastPkt: "L, LastPkt,
      "] ←*N"L, Indent[nest+1],
      "RpcPrivate.Call[ pkt: "L, CallPkt, ", callLength: "L, CallLength,
        ",*BmaxReturnLength: "L];
	CWF.WF1["%LD"L, @resultInfo.sizeOf.pktToAllocate];
	IF ~IsNull[signalDispatcher]
	  THEN WFS[", signalHandler: "L, signalDispatcher];
	WFS1["];*N"L];
    PacketLength[ nest: nest,
	paramInfo: resultInfo,
	pktLengthName: ReturnLength,
	declare: no, assign: ifNeeded ];
    SELECT TRUE FROM
      resultInfo.transferType=Error =>
	WFL1[nest, "Lupine.RuntimeError;  -- Impossible to RESUME an ERROR."L];
      resultInfo.alwaysOnePkt =>
	BEGIN
	Marshal.FromPacket[ nest: nest,
	  paramInfo: resultInfo,
	  overlayHandling: [static: leaveInPkt],
	  varNames: ReturnVars ];
	ReturnStmt[ nest: nest,
	  type: transferInfo.kind,
	  resultInfo: resultInfo,
	  resultOverlayPtr: ResultPtr,
	  staticResults: inPktOverlay ];
	END;  -- alwaysOnePkt.
      resultInfo.alwaysMultiplePkts =>
	BEGIN
	Marshal.FromPacket[ nest: nest,
	  paramInfo: resultInfo,
	  overlayHandling: [static: copyToFrame],
	  varNames: ReturnVars ];
	ReturnStmt[ nest: nest,
	  type: transferInfo.kind,
	  resultInfo: resultInfo,
	  resultOverlayPtr: ResultPtr,
	  staticResults: inFrame ];
	END;  -- alwaysMultiplePkt.
      ~resultInfo.hasOverlayParamType[static] =>
	BEGIN
        -- Could be multiple packets, no statics.
	Marshal.FromPacket[ nest: nest,
	  paramInfo: resultInfo,
	  overlayHandling: [static: leaveInPkt],
	  varNames: ReturnVars ];
	ReturnStmt[ nest: nest,
	  type: transferInfo.kind,
	  resultInfo: resultInfo,
	  resultOverlayPtr: ResultPtr,
	  staticResults: inFrame ];
	END;  -- One or more packets, no statics.
      ENDCASE => -- Could be one or more packets, some statics.
	BEGIN
	WFL1[nest, "BEGIN  -- OnePkt."L];
	WFL[nest, "onePkt: BOOLEAN = "L, LastPkt, ";"L];
	WFL1[nest, "IF ~onePkt THEN BEGIN  -- Must move statics from pkt now."L];
      	  Marshal.FromPacket[ nest: nest+1,
	    paramInfo: resultInfo,
	    overlayHandling: [static: justCopyToFrame],
	    varNames: ReturnVars ];
	  WFL1[nest+1, "END;"L];
	Marshal.FromPacket[ nest: nest,
	  paramInfo: resultInfo,
	  overlayHandling: [static: leaveInPkt],
	  varNames: ReturnVars ];
	WFL1[nest, "IF onePkt"L];
	  WFS[Indent[nest+1], "THEN "L];
	  Return[
	    type: transferInfo.kind,
	    resultInfo: resultInfo,
	    resultOverlayPtr: ResultPtr,
	    staticResults: inPktOverlay ];
	  WFS["*N"L, Indent[nest+1], "ELSE "L];
	  Return[
	    type: transferInfo.kind,
	    resultInfo: resultInfo,
	    resultOverlayPtr: ResultPtr,
	    staticResults: inFrame ];
	  WFS1[";*N"L];
	WFL1[nest, "END;  -- OnePkt."L];
	END;  -- One or more packets.
    WFL[nest, "END;  -- "L, blockName, "."L];
    END;  -- CallingStubBody.

  DoConversationArg: PROCEDURE [argInfo: ParamInfo] =
    BEGIN
    conversationFound: BOOLEAN ← FALSE;
    PrintConversationName: Marshal.ParamProcedure =
      BEGIN
      IF paramFieldInfo.location = inConversation
	THEN BEGIN
	  Declare.WriteParameterName[paramName, paramIndex];
	  RETURN[stop: (conversationFound ← TRUE)];
	  END;
      END;
    WFS1[", localConversation: "L];
    Marshal.EnumerateParams[paramInfo: argInfo, paramProc: PrintConversationName];
    IF ~conversationFound THEN ERROR;
    END;


-- Generate Server procedure stubs and Client signal-raising stubs.

  CalledStubBody: PROCEDURE [
	transferName, blockName: String,
	transferInfo: TransferInfo,
	inlineBody: BOOLEAN,
	argInfo, resultInfo: ParamInfo,
	nest: Nest ] =
    BEGIN
    CallPkt: String = "pkt"L;  -- Must be identical to Dispatcher's pkt.
    ReturnPkt: String = CallPkt;  -- ... pkt.
    CallLength: String = "pktLength"L;
    ReturnLength: String --must equal-- = CallLength;
    CallLimit: String = "callLimit"L;  -- Not used at this time.
    LastPkt: String = "lastPkt"L;  -- Must be identical to Dispatcher's lastPkt.
    ArgPtr: String = "argPkt"L;
    ResultPtr: String = "resPkt"L;
    CallVars: Marshal.VariableNames = [
      pkt: CallPkt, overlayPtr: ArgPtr,
      pktLength: CallLength, pktLimit: CallLimit,
      lastPkt: LastPkt ];
    ReturnVars: Marshal.VariableNames = [
      pkt: ReturnPkt, overlayPtr: ResultPtr,
      pktLength: ReturnLength, pktLimit: ""L,
      lastPkt: LastPkt ];
    WFL[nest, (IF inlineBody THEN "INLINE "L ELSE ""L), "BEGIN"L];
    ParamsAsLocalVars[nest: nest, paramInfo: argInfo];
    ParamsAsLocalVars[nest: nest, paramInfo: resultInfo];
    OverlayRecord[paramInfo: argInfo, nest: nest];
    OverlayRecord[paramInfo: resultInfo, nest: nest];
    IF ~LongString.EqualStrings[CallPkt, Private.GetString[dispatcherPkt]] THEN ERROR;
    --IF ~LongString.EqualStrings[CallPkt, ReturnPkt] THEN ERROR;
    OverlayPointer[ nest: nest,
      paramInfo: argInfo,
      pktName: CallPkt, overlayPtrName: ArgPtr,
      do: both ];
    OverlayPointer[ nest: nest,
      paramInfo: resultInfo,
      pktName: ReturnPkt, overlayPtrName: ResultPtr,
      do: IF argInfo.alwaysOnePkt THEN both ELSE declare ];
    PacketLength[ nest: nest,
      paramInfo: argInfo,
      pktLengthName: CallLength,
      declare: IF argInfo.transferType=Error THEN ifNeeded ELSE yes,
      assign: ifNeeded ];
    nest ← Marshal.BeginAllocation[paramInfo: argInfo, nest: nest];
    SELECT TRUE FROM
      argInfo.alwaysOnePkt =>
	BEGIN
	Marshal.FromPacket[ nest: nest,
	  paramInfo: argInfo,
	  overlayHandling: [static: leaveInPkt],
	  varNames: CallVars ];
	--OverlayPointer[ nest: nest,
	  --paramInfo: resultInfo,
	  --pktName: ReturnPkt, overlayPtrName: ResultPtr,
	  --do: argInfo.alwaysOnePkt => assigned above ];
       CallStmt[ nest: nest,
	  transferName: transferName,
	  type: transferInfo.kind,
	  argInfo: argInfo, resultInfo: resultInfo,
	  staticArgs: inPktOverlay, staticResults: inPktOverlay,
	  argOverlayPtr: ArgPtr, resultOverlayPtr: ResultPtr ];
	END;  -- alwaysOnePkt.
      argInfo.alwaysMultiplePkts =>
	BEGIN
	Marshal.FromPacket[ nest: nest,
	  paramInfo: argInfo,
	  overlayHandling: [static: copyToFrame],
	  varNames: CallVars ];
	OverlayPointer[ nest: nest,
	  paramInfo: resultInfo,
	  pktName: ReturnPkt, overlayPtrName: ResultPtr,
	  do: assign ];
	CallStmt[ nest: nest,
	  transferName: transferName,
	  type: transferInfo.kind,
	  argInfo: argInfo, resultInfo: resultInfo,
	  staticArgs: inFrame, staticResults: inPktOverlay,
	  argOverlayPtr: ArgPtr, resultOverlayPtr: ResultPtr ];
	END;  -- alwaysMultiplePkt.
      ~argInfo.hasOverlayParamType[static] =>
	BEGIN
	-- Could be multiple packets, no statics.
	Marshal.FromPacket[ nest: nest,
	  paramInfo: argInfo,
	  overlayHandling: [static: leaveInPkt],
	  varNames: CallVars ];
	OverlayPointer[ nest: nest,
	  paramInfo: resultInfo,
	  pktName: ReturnPkt, overlayPtrName: ResultPtr,
	  do: assign ];
	CallStmt[ nest: nest,
	  transferName: transferName,
	  type: transferInfo.kind,
	  argInfo: argInfo, resultInfo: resultInfo,
	  staticArgs: inFrame, staticResults: inPktOverlay,
	  argOverlayPtr: ArgPtr, resultOverlayPtr: ResultPtr ];
	END;  -- One or more packets, no statics.
      ENDCASE => -- Could be one or more packets, some statics.
	BEGIN
	WFL1[nest, "BEGIN  -- OnePkt."L];
	WFL[nest, "onePkt: BOOLEAN = "L, LastPkt, ";"L];
	WFL1[nest, "IF ~onePkt THEN BEGIN  -- Must move statics from pkt now."L];
      	  Marshal.FromPacket[ nest: nest+1,
	    paramInfo: argInfo,
	    overlayHandling: [static: justCopyToFrame],
	    varNames: CallVars ];
	  WFL1[nest+1, "END;"L];
	Marshal.FromPacket[ nest: nest,
	  paramInfo: argInfo,
	  overlayHandling: [static: leaveInPkt],
	  varNames: CallVars ];
	OverlayPointer[ nest: nest,
	  paramInfo: resultInfo,
	  pktName: ReturnPkt, overlayPtrName: ResultPtr,
	  do: assign ];
	WFL1[nest, "IF onePkt"L];
	  WFS[Indent[nest+1], "THEN "L];
	  Call[ nest: nest+2,
	    transferName: transferName,
	    type: transferInfo.kind,
	    argInfo: argInfo, resultInfo: resultInfo,
	    staticArgs: inPktOverlay, staticResults: inPktOverlay,
	    argOverlayPtr: ArgPtr, resultOverlayPtr: ResultPtr ];
	  WFS["*N"L, Indent[nest+1], "ELSE "L];
	  Call[ nest: nest+2,
	    transferName: transferName,
	    type: transferInfo.kind,
	    argInfo: argInfo, resultInfo: resultInfo,
	    staticArgs: inFrame, staticResults: inPktOverlay,
	    argOverlayPtr: ArgPtr, resultOverlayPtr: ResultPtr ];
	  WFS1[";*N"L];
	WFL1[nest, "END;  -- OnePkt."L];
	END;  -- One or more packets.
    PacketLength[ nest: nest,
	paramInfo: resultInfo,
	pktLengthName: ReturnLength,
	declare: no,
	assign: IF argInfo.transferType=Error THEN no ELSE yes ];
    Marshal.ToPacket[ nest: nest,
      paramInfo: resultInfo,
      overlayHandling: [static: alreadyInPkt],
      varNames: ReturnVars ];
    nest ← Marshal.EndAllocation[ nest: nest,
      paramInfo: argInfo,
      justCloseBlocks: argInfo.transferType=Error ];
    IF argInfo.transferType # Error  -- Nothing can follow an ERROR.
      THEN BEGIN
	WFSL[Indent[nest],
	  "RETURN["L, Private.GetString[dispatcherReturnLength],
	  ": "L, ReturnLength, "];*N"L ];
	END;
    WFL[nest, "END;  -- "L, blockName, "."L];
    END;  -- CalledStubBody.


-- Call, return, and parameter record routines.

  CallStmt: PROCEDURE [
	transferName: String,
	type: ST.TransferTypes,
	argInfo, resultInfo: ParamInfo,
	staticArgs, staticResults: ParamLocation,
	argOverlayPtr, resultOverlayPtr: String,
	nest: Nest ] =
    BEGIN
    WFS1[Indent[nest]];
    Call[transferName, type, argInfo, resultInfo,
	  staticArgs, staticResults, argOverlayPtr, resultOverlayPtr, nest];
    WFS1[";*N"L];
    END;

  Call: PROCEDURE [
	transferName: String,
	type: ST.TransferTypes,
	argInfo, resultInfo: ParamInfo,
	staticArgs, staticResults: ParamLocation,
	argOverlayPtr, resultOverlayPtr: String,
	nest: Nest ] =
    BEGIN
    CallName: ARRAY ST.TransferTypes OF String = [
      Procedure: ""L, Error: "ERROR "L, Signal: "SIGNAL "L,
      Port: ""L, Program: "START "L, Process: "FORK "L, Other: ""L ];
    IF resultInfo.paramCount > 0
      THEN BEGIN
	ParameterConstructor[
	  paramInfo: resultInfo,
	  overlayPtrName: resultOverlayPtr,
	  statics: staticResults ];
	IF resultInfo.paramCount < 2
	  THEN WFS1[" ← "L] ELSE WFS[" ←*N"L, Indent[nest+1]];
	END;
    WFS[CallName[type], --ModuleName[interface], "."L,-- transferName];
    ParameterConstructor[
      paramInfo: argInfo, overlayPtrName: argOverlayPtr, statics: staticArgs];
    END;

  ReturnStmt: PROCEDURE [
	type: ST.TransferTypes,
	resultInfo: ParamInfo,
	resultOverlayPtr: String,
	staticResults: ParamLocation,
	nest: Nest ] =
    BEGIN
    WFS1[Indent[nest]];
    Return[type, resultInfo, resultOverlayPtr, staticResults];
    WFS1[";*N"L];
    END;

  Return: PROCEDURE [
	type: ST.TransferTypes,
	resultInfo: ParamInfo,
	resultOverlayPtr: String,
	staticResults: ParamLocation ] =
    BEGIN
    ReturnName: ARRAY ST.TransferTypes OF String = [
      Procedure: "RETURN"L, Error: "RESUME"L, Signal: "RESUME"L,
      Port: ""L, Program: "STOP"L, Process: ""L, Other: ""L ];
    WFS1[ReturnName[type]];
    ParameterConstructor[
      paramInfo: resultInfo,
      overlayPtrName: resultOverlayPtr,
      statics: staticResults ];
    END;

  ParameterConstructor: PROCEDURE [
	paramInfo: ParamInfo,
	overlayPtrName: String,
	statics: ParamLocation ] =
    BEGIN
    DoField: Marshal.ParamProcedure =
      BEGIN
      IF paramIndex > 1 THEN WFS1[", "L];
      SELECT paramFieldInfo.location FROM
	inConversation => WFS1[Private.GetString[dispatcherConversation]];
	inPktOverlay => {
	  IF statics=inPktOverlay THEN WFS[overlayPtrName, "."L]; 
	  Declare.WriteParameterName[paramName, paramIndex] };
	inFrame, inFrameAndOverlay =>
	  Declare.WriteParameterName[paramName, paramIndex];
	ENDCASE => ERROR;
      END;  -- DoField.
    WFS1["["L];
    Marshal.EnumerateParams[paramInfo, DoField];
    WFS1["]"L];
    END;

  FullParameterList: PROCEDURE [
	argInfo, resultInfo: ParamInfo,
	printingComment: BOOLEAN,
	nest: Nest ] =
    BEGIN
    IF argInfo.paramCount > 0
      THEN BEGIN
      ArgNameTypeList: Marshal.ParamProcedure =
	BEGIN
	IF paramIndex > 1 THEN WFS1[", "L];
	Declare.WriteParameterName[paramName, paramIndex];
	WFS1[": "L];  Declare.WriteTypeName[paramType];
	END;  -- ArgNameTypeList.
      WFS1["["L];
      Marshal.EnumerateParams[argInfo, ArgNameTypeList];
      WFS1["]"L];
      END;
    IF resultInfo.paramCount > 0
      THEN BEGIN
      keywordComments: BOOLEAN =
	~printingComment AND OverlayStaticsStayInPkt[resultInfo];
      -- If the overlay never moves, do not allocate needless space for
      -- results by assigning keywords.  This means that nonstatics
      -- will have no keywords and be undeclared too.  Their needed
      -- declarations are handled separately by ParamsAsLocalVars. 
      ResultNameTypeList: Marshal.ParamProcedure =
	BEGIN
	IF paramIndex > 1 THEN WFS1[", "L];
	IF keywordComments THEN WFS1["--"L];
	Declare.WriteParameterName[paramName, paramIndex];
	WFS1[IF keywordComments THEN ":-- "L ELSE ": "L];
	Declare.WriteTypeName[paramType];
	END;  -- ResultNameTypeList.
      SELECT argInfo.paramCount FROM
        0 =>  NULL;
	IN [1..2] =>  WFS1[" "L];
        >2 =>
	  WFS["*N"L, Indent[nest+1], (IF printingComment THEN "-- "L ELSE ""L)];
	ENDCASE => NULL;
      WFS1["RETURNS ["L];
      Marshal.EnumerateParams[resultInfo, ResultNameTypeList];
      WFS1["]"L];
      END;
    END;

  ParamsAsLocalVars: PROCEDURE [
  	paramInfo: ParamInfo,
	doOnlyAnonymousParams: BOOLEAN←FALSE,
	doOnlyAddressParams: BOOLEAN←FALSE,
	nest: Nest ] =
    BEGIN
    DeclareLocalVar: Marshal.ParamProcedure =
      BEGIN
      IF doOnlyAnonymousParams AND ~ST.IsAnonymous[paramName] THEN RETURN;
      IF doOnlyAddressParams AND ~OverlayStaticsStayInPkt[paramInfo] THEN RETURN;
      IF ParamStaysInPkt[paramInfo, paramFieldInfo] THEN RETURN;
      SELECT paramFieldInfo.location FROM
        inFrame, inPktOverlay, inFrameAndOverlay =>
	  BEGIN
	  WFS1[Indent[nest]];
	  Declare.WriteParameterName[paramName, paramIndex];
	  WFS1[": "L];
	  Declare.WriteTypeName[paramType];
	  WFS1[";*N"L];
	  END;
	ENDCASE => NULL;  -- Skip others (eg, conversation, stream).
      END;  -- DeclareLocalVar.
    Marshal.EnumerateParams[paramInfo, DeclareLocalVar];
    END;

  ParamStaysInPkt: PROCEDURE [
	paramInfo: ParamInfo, fieldInfo: Marshal.FieldInfo ]
      RETURNS [--yes:-- BOOLEAN] =
    INLINE BEGIN OPEN info: paramInfo;
    RETURN[
      fieldInfo.location = inPktOverlay AND
      ( info.alwaysOnePkt OR
	info.transferSite=caller AND info.paramRecordKind=argument OR
	info.transferSite=callee AND info.paramRecordKind=result )  ];
    END;

  OverlayStaticsStayInPkt: PROCEDURE [paramInfo: ParamInfo]
      RETURNS [--yes:-- BOOLEAN] =
    INLINE BEGIN
    RETURN[ paramInfo.hasOverlayParamType[static] AND paramInfo.alwaysOnePkt ];
    END;


-- Overlay record, overlay pointer, and packet length routines.

  OverlayRecord: PROCEDURE [
	paramInfo: ParamInfo,
	transferIndexName: String←StringNIL,
	nest: Nest] =
    BEGIN
    overlayIndex: Marshal.Words ← paramInfo.sizeOf.overlayHeader;
    printedOne: BOOLEAN ← FALSE;
    DeclareField: Marshal.ParamProcedure =
      BEGIN
      SELECT paramFieldInfo.location FROM
	inPktOverlay, inFrameAndOverlay =>
	  BEGIN
	  IF printedOne THEN WFS1[", "L] ELSE printedOne ← TRUE;
	  Declare.WriteParameterName[paramName, paramIndex];
	  CWF.WF1[" (%D): "L, @overlayIndex];
	  overlayIndex ← overlayIndex + paramFieldInfo.size;
	  Declare.WriteTypeName[paramType];
	  END;
	ENDCASE => NULL;
      END;  -- DeclareField.
    SELECT paramInfo.paramRecordKind FROM
      argument =>
	IF paramInfo.hasOverlayParams OR paramInfo.transferSite=caller
	  THEN BEGIN
	    WFSL[Indent[nest],
	      "ArgumentOverlay: TYPE = MACHINE DEPENDENT RECORD [*B"L,
	      "transferIndex (0): RpcControl."L,
	      (SELECT paramInfo.transferType FROM
		Procedure => Private.GetString[procedureIndex],
		Signal, Error => Private.GetString[signalIndex],
		ENDCASE => ERROR) ];
	    IF ~IsNull[transferIndexName] THEN WFS[" ← "L, transferIndexName];
	    SELECT paramInfo.transferDeclaration FROM
	      inRoutine => WFS1[", callback (1): RpcPrivate.Dispatcher"L];
	      ENDCASE => NULL;
	    printedOne ← TRUE;
	    Marshal.EnumerateParams[paramInfo, DeclareField];
	    WFS1["];*N"L];
	    END;
      result =>
	IF paramInfo.hasOverlayParams
	  THEN BEGIN
	    WFS[ Indent[nest],
	      "ResultOverlay: TYPE = MACHINE DEPENDENT RECORD [*B"L ];
	    Marshal.EnumerateParams[
	      paramInfo: paramInfo, paramProc: DeclareField,
	      includeRESULTs: TRUE ];
	    WFS1["];*N"L];
	    END;
      ENDCASE => ERROR;
    IF overlayIndex # paramInfo.sizeOf.overlayParamRecord THEN ERROR;
    END;

  OverlayPointer: PROCEDURE [
	paramInfo: ParamInfo,
	pktName, overlayPtrName: String,
	do: {declare, assign, both},
	nest: Nest ] =
    BEGIN
    IF NOT ( paramInfo.hasOverlayParams OR
	(paramInfo.paramRecordKind=argument AND paramInfo.transferSite=caller) )
      THEN RETURN;
    WFS[Indent[nest], overlayPtrName];
    SELECT do FROM
      declare, both => 
       WFSL[
        (SELECT  paramInfo.pktSite FROM
	  rpcRuntime => ": LONG "L, stubFrame => ": "L, ENDCASE => ERROR),
	"POINTER TO "L,
        (SELECT paramInfo.paramRecordKind FROM
	  argument => "Argument"L, result => "Result"L, ENDCASE => ERROR),
        "Overlay"L];
      ENDCASE => NULL;
    SELECT do FROM assign => WFS1[" ← "L]; both => WFS1[" = "L]; ENDCASE => NULL;
    SELECT do FROM
      assign, both => WFS["@"L, pktName, ".data[0]"L];
      ENDCASE => NULL;
    WFS1[";*N"L];
    END;

  LocalPacket: PROCEDURE [pktName: String, size: Marshal.Words, nest: Nest] =
    BEGIN
    WFS[Indent[nest], pktName, "Buffer: ARRAY [1..RpcPrivate.pktOverhead"L];
      CWF.WF1["+%LD] OF WORD;*N"L, @size];
    WFLL[nest, pktName, ": RpcPrivate.StubPkt = RpcPrivate.GetStubPkt[space: @"L,
      pktName, "Buffer];"L];
    END;

  PacketLength: PROCEDURE [
	paramInfo: ParamInfo,
	pktLengthName: String,
	declare, assign: {yes, ifNeeded, no},
	nest: Nest ] =
    BEGIN
    HasInFrameParams: PROC [paramInfo: ParamInfo] RETURNS [BOOLEAN] =
      INLINE {RETURN[
	paramInfo.adrInfo.hasAddresses OR
	paramInfo.adrInfo.hasDynamics OR
	~paramInfo.alwaysOnePkt ]};
    declareNeeded: BOOLEAN = SELECT declare FROM
      yes => TRUE,  no => FALSE,  ifNeeded => HasInFrameParams[paramInfo],
      ENDCASE => ERROR;
    assignNeeded: BOOLEAN = SELECT assign FROM
      yes => TRUE,  no => FALSE,  ifNeeded => HasInFrameParams[paramInfo],
      ENDCASE => ERROR;
    IF declareNeeded OR assignNeeded THEN WFS[Indent[nest], pktLengthName];
    IF declareNeeded THEN WFS1[": RpcPrivate.DataLength"L];
    IF assignNeeded
      THEN CWF.WF1[" ← %LD"L, @paramInfo.sizeOf.overlayParamRecord];
    IF declareNeeded OR assignNeeded THEN WFS1[";*N"L];
    END;


-- ParamInfo veneer.

  GetArgResultInfo: PROCEDURE [
	transferInfo: TransferInfo,
	transferDeclaration: TransferDeclaration,
	transferSite: TransferSite,
	pktSite: PktSite,
	options: Options ]
      RETURNS [argInfo, resultInfo: ParamInfo] =
    BEGIN
    argInfo ← Marshal.MakeParamInfo[
	paramRecord: transferInfo.argumentType,
	paramRecordKind: argument,
	transferType: transferInfo.kind,
	transferDeclaration: transferDeclaration,
	transferSite: transferSite,
	pktSite: pktSite,
	options: options ];
    resultInfo ← Marshal.MakeParamInfo[
	paramRecord: transferInfo.resultType,
	paramRecordKind: result,
	RESULTsParamInfo: argInfo,
	transferType: transferInfo.kind,
	transferDeclaration: transferDeclaration,
	transferSite: transferSite,
	pktSite: pktSite,
	options: options
      ! UNWIND => Marshal.FreeParamInfo[argInfo] ];
    END;

  FreeArgResultInfo: PROCEDURE [argInfo, resultInfo: ParamInfo] =
    INLINE BEGIN
    Marshal.FreeParamInfo[argInfo];
    Marshal.FreeParamInfo[resultInfo];
    END;

  
  GetTransferInfo: PROCEDURE [typeHandle: ST.TypeHandle]
      RETURNS[--transferInfo:-- TransferInfo] =
    INLINE BEGIN
    typeInfo: ST.TypeInfo = ST.GetTypeInfo[type: typeHandle];
    WITH typeInfo: typeInfo SELECT FROM
      Transfer => RETURN[typeInfo];
      ENDCASE => RETURN[ERROR];
    END;


END.  -- LupineDeclareStubsImpl.