-- File [Ivy]<Nelson>Lupine>LupineMarshalTypeManagerImpl.mesa.
-- Last edited by BZM on 11-May-82 16:20:51.

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


DIRECTORY
  CWF USING [SWF3, WF1],
  LupineDeclare USING [
	ParameterName, WriteParameterName, WriteTypeName ],
  LupineManagerPrivate USING [
	AllocString, Indent, MaxIdentifierLength,
	Nest, ParamPassingMethod, String, StringNIL,
	WFS, WFS1, WFSL, WFL, WFL1, WFLL ],
  LupineMarshal USING [
	FieldInfo, ParamIndex, ParamInfo,
	ParamProcedure, ParamRecordKind, OverlayHandling,
	OverlayParamType, VariableNames, Version ],
  LupineMarshalPrivate USING [
	ContainsEmbeddedPtrs,
	CopyCharacters, CopyOne, CopyTwo, CopyType, CopyUninterpreted,
	Direction, EnumerateFrameParams, EnumerateOverlayParams, Error,
	IsExplicitHandle, IsShortString,
	MarshalInfo, MarshalInfoObject,
	MarshalArray, MarshalDescriptor, MarshalList,
	MarshalPointer, MarshalRecord, MarshalRef,
	MarshalSequence, MarshalTransfer,MarshalVariantPart,
	MaxGeneratedIdLength, OperationProc,
	ParentInfo, ParentInfoNull, Passing,
	SubStrings, UniqueName, WriteNEW ],
  LupineSymbolTable USING [GetTypeInfo, TypeHandle, TypeInfo, Types];


LupineMarshalTypeManagerImpl: PROGRAM
  IMPORTS
  	Declare: LupineDeclare, LupineManagerPrivate,
  	Private: LupineMarshalPrivate, ST: LupineSymbolTable
  EXPORTS LupineMarshal, LupineMarshalPrivate
  = BEGIN OPEN LupineManagerPrivate, LupineMarshal;



 -- Types from the internal LupineManagerPrivate interface.

  ParentInfo: TYPE = Private.ParentInfo;
  MarshalInfo: TYPE = Private.MarshalInfo;


  -- Parameter protocol versions:

  ParameterProtocolVersions: PUBLIC PROCEDURE
      RETURNS [oldestSupported, newestSupported: Version] =
    {RETURN [
      oldestSupported: 1,
      newestSupported: 1 ]};



  -- NOTE:
  -- The string constants in recursively-callable routines within this
  -- this module have intentionally been put into the global frame.  This
  -- is necessary because putting the literals into the local frames gives
  -- them sizes in the 100-600 word range, which causes the deep calls
  -- to get frame faults.  When the amount of frame space increases, just
  -- substitute "L for "-'-L-'-.

 

-- These routines generate marshaling code for top-level parameters.

  ToPacket: PUBLIC PROCEDURE [
	paramInfo: ParamInfo,
	overlayHandling: OverlayHandling,
	varNames: VariableNames,
	nest: Nest ] =
    BEGIN
    overlayNeedsMoving: BOOLEAN ← FALSE;
    construct: BOOLEAN ← TRUE;  -- Assign, not construct, when FALSE.
    FOR overlayType: OverlayParamType
      IN [FIRST[OverlayParamType]..PRED[LAST[OverlayParamType]]] DO
	IF paramInfo.hasOverlayParamType[overlayType]
	  THEN SELECT overlayHandling[overlayType] FROM
	    copyToPkt => overlayNeedsMoving ← TRUE;
	    ENDCASE => construct ← FALSE;
	      -- Must use assignment when whole record can't be constructed.
      ENDLOOP;
    IF overlayNeedsMoving
      THEN BEGIN  -- Move some overlay params from local frame to pkt.
	printedOne: BOOLEAN ← FALSE;
	MoveOverlayParam: ParamProcedure =
	  BEGIN
	  SELECT overlayHandling[paramFieldInfo.overlayParamType] FROM
	    alreadyInPkt => NULL;
	    copyToPkt =>
	      BEGIN
	      IF construct
	        THEN {IF printedOne THEN WFS1[", "--L--] ELSE printedOne ← TRUE}
		ELSE WFS[Indent[nest], varNames.overlayPtr, "."--L--];
	      Declare.WriteParameterName[paramName, paramIndex];
	      WFS1[IF construct THEN ": "--L-- ELSE " ← "--L--];
	      Declare.WriteParameterName[paramName, paramIndex];
	      IF ~construct THEN WFS1[";*N"--L--];
	      END;
	    ENDCASE => ERROR;
	  END;  -- MoveOverlayParam.
	IF construct THEN WFS[Indent[nest], varNames.overlayPtr, "↑ ← ["--L--];
	Private.EnumerateOverlayParams[paramInfo, MoveOverlayParam];
	IF construct THEN WFS1["];*N"--L--];
      	END;  -- Move the overlay params.
    BEGIN  -- Move remaining nonoverlay params from local frame to pkt.
    marshalInfoObject: Private.MarshalInfoObject ← [
	paramInfo: paramInfo,
	paramFieldInfo: NULL,
	varNames: varNames,
	direction: toPkt ];
    marshalInfo: MarshalInfo = @marshalInfoObject;
    MarshalFrameParam: ParamProcedure =
      BEGIN
      nameString: AllocString = [MaxIdentifierLength];
      marshalInfo.paramFieldInfo ← paramFieldInfo;
      MarshalType[ nest: nest,
	  name: Declare.ParameterName[paramName, paramIndex, nameString],
	  type: paramType,
	  parentInfo: Private.ParentInfoNull,
	  marshalInfo: marshalInfo ];
      END;  -- MarshalFrameParam.
    Private.EnumerateFrameParams[paramInfo, MarshalFrameParam];
    END;  -- Move the remaining nonoverlay params.
    END;



  FromPacket: PUBLIC PROCEDURE [
	paramInfo: ParamInfo,
	overlayHandling: OverlayHandling,
	varNames: VariableNames,
	nest: Nest ] =
    BEGIN
    overlayNeedsMoving: BOOLEAN ← FALSE;
    FOR overlayType: OverlayParamType IN OverlayParamType DO
      IF paramInfo.hasOverlayParamType[overlayType] AND
           overlayHandling[overlayType] IN [copyToFrame..justCopyToFrame]
	 THEN {overlayNeedsMoving ← TRUE; EXIT};
      ENDLOOP;
    IF overlayNeedsMoving
      THEN BEGIN  -- Move some overlay params from pkt to local frame.
	printedOne: BOOLEAN ← FALSE;
	ExtractOverlayParam: ParamProcedure =
	  BEGIN
	  SELECT overlayHandling[paramFieldInfo.overlayParamType] FROM
	    leaveInPkt => NULL;
	    copyToFrame, justCopyToFrame =>
	      BEGIN
	      IF printedOne THEN WFS1[", "--L--] ELSE printedOne ← TRUE;
	      Declare.WriteParameterName[paramName, paramIndex];
	      WFS1[": "--L--];
	      Declare.WriteParameterName[paramName, paramIndex];
	      END;
	    ENDCASE => ERROR;
	  END;  -- ExtractOverlayParam.
	WFS[Indent[nest], "["--L--];
	Private.EnumerateOverlayParams[paramInfo, ExtractOverlayParam];
	WFS["] ← "--L--, varNames.overlayPtr, "↑;*N"--L--];
      	END;  -- Move the overlay params.
    FOR overlayType: OverlayParamType IN OverlayParamType DO
      IF overlayHandling[overlayType] = justCopyToFrame THEN RETURN;
      ENDLOOP;
    BEGIN  -- Move remaining nonoverlay params from local frame to pkt.
    marshalInfoObject: Private.MarshalInfoObject ← [
	paramInfo: paramInfo,
	paramFieldInfo: NULL,
	varNames: varNames,
	direction: fromPkt ];
    marshalInfo: MarshalInfo = @marshalInfoObject;
    MarshalFrameParam: ParamProcedure =
      BEGIN
      nameString: AllocString = [MaxIdentifierLength];
      marshalInfo.paramFieldInfo ← paramFieldInfo;
      MarshalType[ nest: nest,
	  name: Declare.ParameterName[paramName, paramIndex, nameString],
	  type: paramType,
	  parentInfo: Private.ParentInfoNull,
	  marshalInfo: marshalInfo ];
      END;  -- MarshalFrameParam.
    Private.EnumerateFrameParams[paramInfo, MarshalFrameParam];
    END;  -- Move the remaining nonoverlay params.
    BEGIN  -- Check that the correct amount of pkt.data was unmarshaled.
    HasInFrameParams: PROC [paramInfo: ParamInfo] RETURNS [BOOLEAN] =
    -- This routine is used to perform constant folding on "pktLength".
    -- See LupineDeclareStubsImpl.PacketLength.
      INLINE {RETURN[
	paramInfo.adrInfo.hasAddresses OR
	paramInfo.adrInfo.hasDynamics OR
	~paramInfo.alwaysOnePkt ]};
    WFSL[Indent[nest], "Lupine.CheckPktLength[pkt: "L, varNames.pkt,
      ", pktLength: "L];
    IF HasInFrameParams[paramInfo]
      THEN WFS1[varNames.pktLength]
      ELSE CWF.WF1["%LD"L, @paramInfo.sizeOf.overlayParamRecord];
    WFS1["];*N"L];
    END;
    END;


-- General type marshaling routine.


  OperationInfo: TYPE = RECORD [
    canMarshal: BOOLEAN ← TRUE,
    newBlock: BOOLEAN ← TRUE] ← [];

  MarshalOpInfo: PACKED ARRAY ST.Types OF OperationInfo = [
     -- These four must have a block because of Start/StopReadonly.
	Pointer: [newBlock: TRUE],
	Ref: [newBlock: TRUE],
	List: [newBlock: TRUE],
	Descriptor: [newBlock: TRUE],
     -- These are simple and don't need a block.
	Null: [newBlock: FALSE],
	Basic: [newBlock: FALSE],
	Transfer: [newBlock: FALSE],
	VariantPart: [newBlock: FALSE],
	RelativePtr: [newBlock: FALSE],
	Zone: [newBlock: FALSE],
	Opaque: [newBlock: FALSE],
     -- These guys cannot be marshaled.
	Definition: [canMarshal: FALSE, newBlock: FALSE],
	Any: [canMarshal: FALSE, newBlock: FALSE],
	Other: [canMarshal: FALSE, newBlock: FALSE] ];


  MarshalType: PUBLIC Private.OperationProc =
    BEGIN
    -- Each marshaling routine for which MarshalOpInfo.newBlock is TRUE can
    -- generate multiple statements because BEGIN-END is written here.
    -- Otherwise the routine must generate exactly one statement.
    Explain: ARRAY {verb, adverb} OF ARRAY Private.Direction OF STRING = [
      verb:   [toPkt: "Marshal"--L--, fromPkt: "Unmarshal"--L--],
      adverb: [toPkt: "to"--L--, fromPkt: "from"--L--] ];
    typeInfo: ST.TypeInfo = ST.GetTypeInfo[type: type];
    passingMethod: ParamPassingMethod = marshalInfo.paramFieldInfo.passingMethod;
    marshalInfo.depth ← marshalInfo.depth + 1;
    IF MarshalOpInfo[typeInfo.type].newBlock
      THEN BEGIN
	WFSL[ Indent[nest], "BEGIN  -- "--L--,
	  Explain[verb][marshalInfo.direction], " "--L--, name, ": "--L-- ];
	Declare.WriteTypeName[type];
	WFSL[ " "--L--, Explain[adverb][marshalInfo.direction], " "--L--,
	  marshalInfo.varNames.pkt, ".data["--L--, marshalInfo.varNames.pktLength,
  	  "].*N"--L-- ];
	nest ← nest + 1;
	END;
    IF MarshalOpInfo[typeInfo.type].canMarshal
     THEN SELECT passingMethod FROM
      Handle, InterMds =>
	BEGIN
	-- Handle and InterMds should only happen at the top (param) level.
	IF marshalInfo.depth # 1 THEN ERROR;
	Private.CopyUninterpreted[ nest: nest,
	  variableName: name,  variableInfo: typeInfo,
	  parentInfo: parentInfo,  marshalInfo: marshalInfo ];
	END;
      Var, Value, Result =>
	SELECT TRUE FROM
	  (passingMethod = Result OR passingMethod = Var) AND
	      Private.ContainsEmbeddedPtrs[type] =>
	    -- Exactly one pointer depth is permitted in VAR and RESULT.
	    Private.Error[code: EmbeddedRESULT, type: type];
	  Private.IsExplicitHandle[typeInfo] =>
	    -- A handle embedded in something else still a handle.
	    Private.CopyUninterpreted[ nest: nest,
	      variableName: name,  variableInfo: typeInfo,
	      parentInfo: parentInfo,  marshalInfo: marshalInfo ];
	  ENDCASE => 
	    BEGIN
	    marshalNameString: AllocString = [Private.MaxGeneratedIdLength];
	    marshalName: String = StartReadonly[ nest: nest,
	      name: name, typeInfo: typeInfo,
	      marshalNameString: marshalNameString, marshalInfo: marshalInfo ];
	    WITH typeInfo: typeInfo SELECT FROM
	      Null, Basic, RelativePtr, Opaque =>
		-- These basic types just need copying.
		Private.CopyType[ nest: nest,
		  variableName: marshalName,  variableInfo: typeInfo,
		  parentInfo: parentInfo,  marshalInfo: marshalInfo ];
	      Transfer => Private.MarshalTransfer[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      Record => Private.MarshalRecord[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      VariantPart => Private.MarshalVariantPart[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      Pointer => Private.MarshalPointer[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      Ref => Private.MarshalRef[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      List => Private.MarshalList[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      String => MarshalString[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      Rope => MarshalRope[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      Atom => MarshalAtom[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      StringBody => MarshalStringBody[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      Text => MarshalText[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      Array => Private.MarshalArray[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      Descriptor => Private.MarshalDescriptor[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      Sequence => Private.MarshalSequence[
		  marshalName, typeInfo, parentInfo, marshalInfo, nest];
	      Zone =>  -- Zones must be handles.
		Private.Error[code: ImproperPassingMethod, type: type];
	      Definition, Any, Other =>
		ERROR;  -- Unimplemented marshalings should be caught above.
	      ENDCASE => ERROR;
	    StopReadonly[ nest: nest,
	      name: name, typeInfo: typeInfo,
	      marshalName: marshalName,  marshalInfo: marshalInfo ];
	    END;
      ENDCASE => ERROR
     ELSE Private.Error[code: UnimplementedMarshaling, type: type];
    IF MarshalOpInfo[typeInfo.type].newBlock
      THEN WFLL[nest, "END;  -- "--L--, Explain[verb][marshalInfo.direction],
 	" "--L--, name, "."--L-- ];
    marshalInfo.depth ← marshalInfo.depth - 1;
    END;


-- Marshaling routines for nontrivial builtin types (e.g., strings).

  MarshalString: PROCEDURE [
	name: String,
	stringInfo: String ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    INLINE BEGIN
    MarshalStringTypes[ nest: nest,
      stringInfo: stringInfo,
      parentInfo: [name, stringInfo],
      marshalInfo: marshalInfo ];
    END;

  MarshalStringBody: PROCEDURE [
	name: String,
	stringBodyInfo: StringBody ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    INLINE BEGIN
    MarshalStringTypes[ nest: nest,
      stringInfo: stringBodyInfo,
      parentInfo: parentInfo,
      marshalInfo: marshalInfo ];
    END;

  MarshalText: PROCEDURE [
	name: String,
	textInfo: Text ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    INLINE BEGIN
    MarshalStringTypes[ nest: nest,
      stringInfo: textInfo,
      parentInfo: parentInfo,
      marshalInfo: marshalInfo ];
    END;

  MarshalStringTypes: PROCEDURE [
	stringInfo: ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    DotMaxLength: String = SELECT stringInfo.type FROM
        String, StringBody => ".maxlength"L,
	Text => ".maxLength"L,
	ENDCASE => ERROR;
    TextBase: Private.SubStrings = SELECT stringInfo.type FROM
        String, StringBody => ["BASE["L, parentInfo.name, ".text]"L],
	Text => ["BASE[DESCRIPTOR["L, parentInfo.name, ".text]]"L],
	ENDCASE => ERROR;
    VerifyPassingMethods[all: TRUE, marshalInfo: marshalInfo];
    SELECT parentInfo.typeInfo.type FROM
	String, Pointer, Ref => NULL; ENDCASE => ERROR;
    SELECT marshalInfo.direction FROM
      toPkt =>
	BEGIN
	Private.CopyOne[ nest: nest,
	  wordsNeeded: 3,
	  value: [parentInfo.name, "=NIL"L],
	  marshalInfo: marshalInfo ];
	WFL[nest, "IF "L, parentInfo.name, " # NIL"L];
	WFL1[nest+1, "THEN BEGIN"L];
	WFL1[nest+2, "stringHeader: Lupine.StringHeader = ["L];
	WFLL[nest+3,
	  "Lengths[length: "L, parentInfo.name, ".length, maxLength: "L,
	  parentInfo.name, DotMaxLength, "]];"L ];
	IF Private.IsShortString[stringInfo.self]
	  THEN BEGIN
	    WFL[nest+2,
	     "IF stringHeader.length > RpcPrivate.maxShortStringLength"L];
	    WFL1[nest+3, "THEN Lupine.MarshalingError;"L];
	    END;
	Private.CopyTwo[ nest: nest+2,
	  wordsNeeded: 0,
	  value: ["stringHeader.all"L],
	  marshalInfo: marshalInfo ];
	IF Private.Passing[Result, argument, marshalInfo]
	  THEN WFL1[nest+2, "NULL;  -- Call by result, send header only."L]
	  ELSE Private.CopyCharacters[ nest: nest+2,
	    textName: TextBase,
	    numCharsName: "stringHeader.length"L,
	    marshalInfo: marshalInfo ];
	WFL[nest+2, "END;  -- IF "L, parentInfo.name, " # NIL."L];
	END;
      fromPkt =>
	BEGIN
	WFL1[nest, "stringIsNIL: Lupine.NilHeader;"L];
	Private.CopyOne[ nest: nest,
	  wordsNeeded: 3,
	  value: ["stringIsNIL"L],
	  marshalInfo: marshalInfo ];
	IF Private.Passing[Result, result, marshalInfo] OR
	    Private.Passing[Var,result,marshalInfo]
	  THEN WFL[nest,
	      "IF stringIsNIL # ("L, parentInfo.name,
	      "=NIL) THEN Lupine.UnmarshalingError;"L];
	WFL1[nest, "IF stringIsNIL"L];
	WFL[nest+1, "THEN "L, parentInfo.name, " ← NIL"L];
	WFL1[nest+1, "ELSE BEGIN"L];
 	WFL1[nest+2, "stringHeader: Lupine.StringHeader;"L];
	Private.CopyTwo[ nest: nest+2,
	  wordsNeeded: 0,
	  value: ["stringHeader.all"L],
	  marshalInfo: marshalInfo ];
	IF Private.IsShortString[stringInfo.self]
	  THEN BEGIN
	    WFL[nest+2,
	     "IF stringHeader.length > RpcPrivate.maxShortStringLength"L];
	    WFL1[nest+3, "THEN Lupine.UnmarshalingError;"L];
	    END;
	IF Private.Passing[Result, result, marshalInfo] OR
	     Private.Passing[Var,result,marshalInfo]
	  THEN BEGIN
	    WFLL[nest+2,
	      "IF stringHeader.maxLength # "L, parentInfo.name, DotMaxLength,
	      " THEN Lupine.UnmarshalingError;"L ];
	    WFL1[nest+2,
	      "NULL;  -- Call by var or result, use existing string."L];
	    END
	  ELSE BEGIN
	    WFS[Indent[nest+2], parentInfo.name, " ← ("L];
	    Private.WriteNEW[ptrInfo: parentInfo.typeInfo, marshalInfo: marshalInfo];
	    WFS1["["L];
	    SELECT stringInfo.type FROM
		String => WFS1["StringBody"L];
		ENDCASE => Declare.WriteTypeName[stringInfo.self];
	    WFS1["[stringHeader.maxLength]]);*N"L];
	    END;
	IF Private.Passing[Result, argument, marshalInfo]
	  THEN WFL1[nest+2, "NULL;  -- Call by result, use uninitialized body."L]
	  ELSE BEGIN
	    WFL[nest+2, parentInfo.name, ".length ← stringHeader.length;"L];
	    Private.CopyCharacters[ nest: nest+2,
	      textName: TextBase,
	      numCharsName: "stringHeader.length"L,
	      marshalInfo: marshalInfo ];
	    END;
	WFL[nest+2, "END;  -- IF stringIsNIL."L];
	END;
      ENDCASE => ERROR;
    END;

  MarshalRope: PROCEDURE [
	name: String,
	ropeInfo: Rope ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    VerifyPassingMethods[value: TRUE, handle: TRUE, marshalInfo: marshalInfo];
    SELECT marshalInfo.direction FROM
      toPkt =>
	BEGIN
	Private.CopyOne[ nest: nest,
	  wordsNeeded: 2,
	  value: [name, "=NIL"L],
	  marshalInfo: marshalInfo ];
	WFL[nest, "IF "L, name, " # NIL"L];
	WFL1[nest+1, "THEN BEGIN"L];
	WFL[nest+2,
	  "textRope: Rope.Text = RopeInline.InlineFlatten[r: "L, name, "];"L];
	IF Private.IsShortString[ropeInfo.self] THEN BEGIN
	    WFL1[nest+2, "IF textRope.length > RpcPrivate.maxShortStringLength"L];
	    WFL1[nest+3, "THEN Lupine.MarshalingError;"L];
	    END;
	Private.CopyOne[ nest: nest+2,
	  wordsNeeded: 0,
	  value: ["textRope.length"L],
	  marshalInfo: marshalInfo ];
	Private.CopyCharacters[ nest: nest+2,
	  textName: ["BASE[DESCRIPTOR[textRope.text]]"L],
	  numCharsName: "textRope.length"L,
	  marshalInfo: marshalInfo ];
	WFL[nest+2, "END;  -- IF "L, name, " # NIL."L];
	END;
      fromPkt =>
	BEGIN
	WFL1[nest, "ropeIsNIL: Lupine.NilHeader;"L];
	Private.CopyOne[ nest: nest,
	  wordsNeeded: 2,
	  value: ["ropeIsNIL"L],
	  marshalInfo: marshalInfo ];
	WFL1[nest, "IF ropeIsNIL"L];
	WFL[nest+1, "THEN "L, name, " ← NIL"L];
	WFL1[nest+1, "ELSE BEGIN"L];
 	WFL1[nest+2, "ropeLength: Lupine.RopeHeader;"L];
	WFL1[nest+2, "textRope: Rope.Text;"L];
	Private.CopyOne[ nest: nest+2,
	  wordsNeeded: 0,
	  value: ["ropeLength"L],
	  marshalInfo: marshalInfo ];
	WFL[nest+2,
	  "IF ropeLength > "L,
	  (IF Private.IsShortString[ropeInfo.self]
	    THEN "RpcPrivate.maxShortStringLength"L ELSE "LAST[NAT]"L) ];
	WFL1[nest+3, "THEN Lupine.UnmarshalingError;"L];
	WFL[nest+2, name, " ← textRope ← RopeInline.NewText[size: ropeLength];"L];
	Private.CopyCharacters[ nest: nest+2,
	  textName: ["BASE[DESCRIPTOR[textRope.text]]"L],
	  numCharsName: "ropeLength"L,
	  marshalInfo: marshalInfo ];
	WFL[nest+2, "END;  -- IF ropeIsNIL."L];
	END;
      ENDCASE => ERROR;
    END;

  MarshalAtom: PROCEDURE [
	name: String,
	atomInfo: Atom ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    VerifyPassingMethods[value: TRUE, handle: TRUE, marshalInfo: marshalInfo];
    SELECT marshalInfo.direction FROM
      toPkt =>
	BEGIN
	WFL[nest, "pNameOfAtom: Rope.Text = Atom.GetPName[atom: "L, name, "];"L];
	MarshalRope[ nest: nest,
	  name: "pNameOfAtom"L,
	  ropeInfo: ST.TypeInfo[self: atomInfo.self, info: Rope[]],
	  parentInfo: parentInfo,  marshalInfo: marshalInfo ];
	END;
      fromPkt =>
	BEGIN
	WFL1[nest, "pNameOfAtom: Rope.ROPE;"L];
	MarshalRope[ nest: nest,
	  name: "pNameOfAtom"L,
	  ropeInfo: ST.TypeInfo[self: atomInfo.self, info: Rope[]],
	  parentInfo: parentInfo,  marshalInfo: marshalInfo ];
	WFL[nest, name, " ← Atom.MakeAtom[--pName:-- pNameOfAtom];"L];
	END;
      ENDCASE => ERROR;
    END;


-- Marshaling utility routines.

  VerifyPassingMethods: PUBLIC PROCEDURE [
  	var, value, result, handle, all: BOOLEAN ← FALSE,
	marshalInfo: MarshalInfo ] =
    BEGIN
    -- VerifyPassingMethods double checks that Lupine's
    -- type checker (LupineMarshalInfoImpl.DeterminePassingMethod)
    -- and code generators (the Marshal<type> routines) are in agreement.
    IF marshalInfo.depth = 1 AND
       NOT (all OR (SELECT marshalInfo.paramFieldInfo.passingMethod FROM
	  Var => var, Value => value, Result => result, Handle => handle,
	  InterMds => TRUE, ENDCASE => ERROR) )
      THEN ERROR;
    END;

  UniqueName: PUBLIC PROCEDURE [
	root: String,
	nameString: --VAR-- String,
	suffix: String ← StringNIL,
	marshalInfo: MarshalInfo ] =
    BEGIN
    CWF.SWF3[nameString, "%LS%D%LS"L, root, @marshalInfo.depth, suffix];
    END;


  StartReadonly: PROCEDURE [
	name: String,
	typeInfo: ST.TypeInfo,
	marshalNameString: --VAR-- String,
	marshalInfo: MarshalInfo,
	nest: Nest ]
      RETURNS [marshalName: String] =
    BEGIN
    marshalName ← name;  -- Usually no new name is needed.
    SELECT typeInfo.type FROM
      Pointer, Ref, List, Descriptor =>
	IF typeInfo.readonly THEN BEGIN
	  Private.UniqueName[
	    root: "writeOk"L, nameString: marshalNameString,
	    marshalInfo: marshalInfo ];
	  marshalName ← marshalNameString;
	  WFL1[nest,
	    "-- Declare readwrite pointer for marshaling readonly pointer."L];
	  SELECT marshalInfo.direction FROM
	    toPkt =>
	      BEGIN
	      WFS[Indent[nest], marshalName, ": "L];
	      Declare.WriteTypeName[type: typeInfo.self, includeReadonly: FALSE];
	      WFS[" = LOOPHOLE["L, name, "];*N"L];
	      END;
	    fromPkt =>
	      BEGIN
	      WFS[Indent[nest], marshalName, ": "L];
	      Declare.WriteTypeName[type: typeInfo.self, includeReadonly: FALSE];
	      WFS1[";*N"L];
	      END;
	    ENDCASE => ERROR;
	  END;
      ENDCASE => IF typeInfo.readonly THEN ERROR;    
    END;

  StopReadonly: PROCEDURE [
	name: String,
	typeInfo: ST.TypeInfo,
	marshalName: String,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    SELECT typeInfo.type FROM
      Pointer, Ref, List, Descriptor =>
	IF typeInfo.readonly THEN
	  SELECT marshalInfo.direction FROM
	    toPkt => NULL;
	    fromPkt => WFLL[nest, name, " ← "L, marshalName, ";"L];
	    ENDCASE => ERROR;
      ENDCASE => IF typeInfo.readonly THEN ERROR;
    END;


END.  -- LupineMarshalTypeManagerImpl.