-- File [Ivy]<Nelson>Lupine>LupineMarshalTypeConstructorImpl.mesa.
-- Last edited by BZM on March 18, 1982  11:18 AM.

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


DIRECTORY
  CWF USING [SWF1, SWF2],
  LupineDeclare USING [WriteSymbolName, WriteTypeName],
  LupineManagerPrivate USING [
	AllocString, Indent, MaxIdentifierLength,
	Nest, ParamPassingMethod, String, StringNIL,
	WFS, WFS1, WFSL, WFL, WFL1, WFLL ],
  LupineMarshal USING [FieldInfo, ParamInfo, ParamRecordKind],
  LupineMarshalPrivate USING [
	ContainsRefs, ContainsSequences, ContainsStatics,
	CopyOne, CopyTwo, CopyType, Direction, Error, HasEmptyIndex,
	MarshalInfo, MarshalType, MaxGeneratedIdLength, MaxPointerDepth,
	NeedsMarshaling, NeedsOperationProc, OperationProc,
	ParentInfo, Passing,
	SubStrings, UniqueName, VerifyPassingMethods, WriteNEW ],
  LupineSymbolTable USING [
	ComponentProcedure, EnumerateRecord,
	EnumerateVariants, GetTypeInfo, IsAnonymous,
	SymbolHandle, SymbolName,
	TypeHandle, TypeInfo, Types, VariantProcedure ];


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



 -- Types from the internal LupineManagerPrivate interface.

  ParentInfo: TYPE = Private.ParentInfo;
  MarshalInfo: TYPE = Private.MarshalInfo;
  NeedsOperationProc: TYPE = Private.NeedsOperationProc;
  OperationProc: TYPE = Private.OperationProc;
  SubStrings: TYPE = Private.SubStrings;

  MaxGeneratedIdLength: CARDINAL = Private.MaxGeneratedIdLength;



  -- 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-'-.

 

-- Marshaling routines for type constructors (i.e., nonbuiltin types).

  MarshalTransfer: PUBLIC PROCEDURE [
	name: String,
	transferInfo: Transfer ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    Private.VerifyPassingMethods[value: TRUE, handle: TRUE, marshalInfo: marshalInfo];
    Private.Error[code: TransferParameter, type: transferInfo.self];
    END;

  MarshalRecord: PUBLIC PROCEDURE [
	name: String,
	recInfo: Record ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    Private.VerifyPassingMethods[value: TRUE, marshalInfo: marshalInfo];
    IF recInfo.hasSequences
      THEN MarshalSequenceRecord[ nest: nest,
	      recName: name,  recInfo: recInfo,
	      parentInfo: parentInfo,  marshalInfo: marshalInfo ]
      ELSE MarshalFixedRecord[ nest: nest,
	      recName: name,  recInfo: recInfo,
	      parentInfo: parentInfo,  marshalInfo: marshalInfo ];
    END;

  MarshalFixedRecord: PROCEDURE [
	recName: String,
	recInfo: Record ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    -- MarshalFixedRecord needs storage for the record to already be allocated.
    -- Thus, sequence containing records are separately handled by
    -- MarshalSequenceRecord, which calls us to do some work.
    IF (SELECT parentInfo.typeInfo.type FROM
	  Record, VariantPart => FALSE, ENDCASE => TRUE)
      AND Private.ContainsStatics[recInfo.self]
	THEN BEGIN
	  Private.CopyType[ nest: nest,
	    variableName: recName,  variableInfo: recInfo,
	    parentInfo: parentInfo,  marshalInfo: marshalInfo ];
	  -- After a blind copy, always reset any Refs to NIL immediately.
	  -- This consistency-making must be done atomically, ie, there
	  -- must be no possibility of a fault (UNWIND) between the copying
	  -- and the NIL-making.
	  MakeRefsNil[ nest: nest,
	    name: recName,
	    type: recInfo.self,
	    parentInfo: parentInfo,
	    marshalInfo: marshalInfo ];
	  END;
    DoRecordOperation[ nest: nest,
      opProc: Private.MarshalType,  needsOpProc: Private.NeedsMarshaling,
      recName: recName,  recInfo: recInfo,
      marshalInfo: marshalInfo ];
    END;

  MarshalSequenceRecord: PROCEDURE [
	recName: String,
	recInfo: Record ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    IF ~recInfo.hasSequences OR
	(SELECT parentInfo.typeInfo.type FROM
	  Pointer, Ref => FALSE, ENDCASE => TRUE)
      THEN 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 THEN BEGIN"--L--];
	  WFL1[nest+1, "-- Record has a sequence, put its length up front."--L--];
	  Private.CopyTwo[ nest: nest+1,
	    wordsNeeded: 0,
	    value: SubStrings["LENGTH[DESCRIPTOR["--L--, recName, "]]"--L--],
	    marshalInfo: marshalInfo ];
	  IF Private.Passing[Result, argument, marshalInfo]
	    THEN WFL1[nest+1, "NULL;  -- Call by result, don't send record."--L--]
	    ELSE MarshalFixedRecord[ nest: nest+1,
		recName: recName, recInfo: recInfo,
		parentInfo: parentInfo, marshalInfo: marshalInfo ];
	  WFL[nest+1, "END;  -- IF "--L--, parentInfo.name, " # NIL."--L--];
	  END;
	fromPkt =>
	  BEGIN
	  WFL1[nest, "recordIsNIL: Lupine.NilHeader;"--L--];
	  Private.CopyOne[ nest: nest,
	    wordsNeeded: 3,
	    value: ["recordIsNIL"--L--],
	    marshalInfo: marshalInfo ];
	  IF Private.Passing[Result, result, marshalInfo]
	      OR Private.Passing[Var, result, marshalInfo]
	    THEN WFL[nest,
	      "IF recordIsNIL # ("--L--, parentInfo.name,
	      "=NIL) THEN Lupine.UnmarshalingError;"--L--];
	  WFL1[nest, "IF recordIsNIL"--L--];
	  WFL[nest+1, "THEN "--L--, parentInfo.name, " ← NIL"--L--];
	  WFL1[nest+1, "ELSE BEGIN"--L--];
	  WFL1[nest+2, "seqLength: Lupine.SequenceHeader;"--L--];
	  Private.CopyTwo[ nest: nest+2,
	    wordsNeeded: 0,
	    value: ["seqLength"--L--],
	    marshalInfo: marshalInfo ];
	  IF Private.Passing[Result, result, marshalInfo]
	      OR Private.Passing[Var, result, marshalInfo]
	    THEN BEGIN
	      WFL[nest+2,
		"IF seqLength # LENGTH[DESCRIPTOR["--L--, recName,
		"]] THEN Lupine.UnmarshalingError;"--L-- ];
	      WFL1[nest+2,
		"NULL;  -- Call by var or result, use existing record."--L--];
	      END
	    ELSE BEGIN
	      WFS[Indent[nest+2], parentInfo.name, " ← ("--L--];
	      Private.WriteNEW[
		ptrInfo: parentInfo.typeInfo, marshalInfo: marshalInfo ];
	      WFS1["["--L--];
	      Declare.WriteTypeName[recInfo.self];
	      WFS1["[Lupine.SHORT[seqLength]]]);*N"--L--];
	      END;
	  IF Private.Passing[Result, argument, marshalInfo]
	    THEN WFL1[nest+2,
	      "NULL;  -- Call by result, use uninitialized record."--L--]
	    ELSE MarshalFixedRecord[ nest: nest+2,
		recName: recName, recInfo: recInfo,
		parentInfo: parentInfo, marshalInfo: marshalInfo ];
	  WFL[nest+2, "END;  -- IF recordIsNIL."--L--];
	  END;
	ENDCASE => ERROR;
    END;

  DoRecordOperation: PROCEDURE [
	opProc: OperationProc,
	needsOpProc: NeedsOperationProc,
	recName: String,
	recInfo: Record ST.TypeInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    MarshalField: ST.ComponentProcedure =
	BEGIN
	fieldString: AllocString = [MaxIdentifierLength];
	IF ~needsOpProc[componentType] THEN RETURN;
	-- Monitored records will be caught because of the LOCK field.
	opProc[ nest: nest,
	    name: FieldName[
	    	fieldSymbol: component,
		fieldString: fieldString,
		recordType: recInfo.self,
		anonOk:  -- Single component variant records don't need names.
		  componentIndex = 1 AND
		  recInfo.hasVariants AND
		  ST.GetTypeInfo[componentType].type = VariantPart ],
	    type: componentType,
	    parentInfo: [recName, recInfo],
	    marshalInfo: marshalInfo ];
	END;
    IF needsOpProc[recInfo.self] THEN BEGIN
      WFL[nest, "BEGIN OPEN record: "--L--, recName, ";"--L--];
      [] ← ST.EnumerateRecord[recordType: recInfo.self, proc: MarshalField];
      WFL[nest, "END;  -- OPEN record: "--L--, recName, "."--L--];
      END;
    END;

  MarshalVariantPart: PUBLIC PROCEDURE [
	name: String,
	varInfo: VariantPart ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    Private.VerifyPassingMethods[value: TRUE, marshalInfo: marshalInfo];
    IF Private.ContainsSequences[varInfo.self]
      THEN Private.Error[code: SequenceInsideVariant, type: varInfo.self]
      ELSE DoVariantOperation[ nest: nest,
	opProc: Private.MarshalType,  needsOpProc: Private.NeedsMarshaling,
	varName: name,  varInfo: varInfo,
	marshalInfo: marshalInfo ];
    END;

  DoVariantOperation: PROCEDURE [
	opProc: OperationProc,
	needsOpProc: NeedsOperationProc,
	varName: String,
	varInfo: VariantPart ST.TypeInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    MarshalVariant: ST.VariantProcedure =
      BEGIN
      IF ~needsOpProc[variantRecordType] THEN RETURN;
      WFS1[Indent[nest+1]];
      Declare.WriteSymbolName[variantTag];
      WFS1[" =>*N"--L--];
      opProc[ nest: nest+2,
	name: "variant"--L--,
	type: variantRecordType,
	parentInfo: [varName, varInfo],
	marshalInfo: marshalInfo ];
      END;  -- MarshalVariant.
    SELECT TRUE FROM
      ~needsOpProc[varInfo.self] => NULL;
      varInfo.kind = Computed =>
	Private.Error[code: ComputedVariant, type: varInfo.self];
      ENDCASE =>
	BEGIN
	WFL1[nest, "WITH variant: record SELECT FROM"--L--];
 	[] ← ST.EnumerateVariants[
	  variantPartType: varInfo.self, proc: MarshalVariant];
	WFL1[nest+1, "ENDCASE => NULL;  -- WITH variant: record."--L--];
	END;
    END;

  MarshalPointer: PUBLIC PROCEDURE [
	name: String,
	pointerInfo: Pointer ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    MarshalPointerTypes[ nest: nest,
      ptrName: name, ptrInfo: pointerInfo,
      referentType: pointerInfo.referentType,
      parentInfo: parentInfo, marshalInfo: marshalInfo ];
    END;

  MarshalRef: PUBLIC PROCEDURE [
	name: String,
	refInfo: Ref ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    MarshalPointerTypes[ nest: nest,
      ptrName: name, ptrInfo: refInfo,
      referentType: refInfo.referentType,
      parentInfo: parentInfo, marshalInfo: marshalInfo ];
    END;

  MarshalPointerTypes: PROCEDURE [
	ptrName: String,
	ptrInfo: ST.TypeInfo,
	referentType: ST.TypeHandle,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    referentInfo: ST.TypeInfo = ST.GetTypeInfo[referentType];
    referentName: AllocString = [MaxIdentifierLength];
    Private.VerifyPassingMethods[all: TRUE, marshalInfo: marshalInfo];
    CWF.SWF1[referentName, "%LS↑"--L--, ptrName];
    marshalInfo.ptrDepth ← marshalInfo.ptrDepth + 1;
    SELECT TRUE FROM
      marshalInfo.ptrDepth > Private.MaxPointerDepth =>
	Private.Error[code: ProbablePointerRecursion, type: ptrInfo.self];
      (WITH refInfo: referentInfo SELECT FROM
	  Basic => SELECT refInfo.kind FROM
	    Unspecified, Other => TRUE, ENDCASE => FALSE,
	  Any => TRUE,
	  Opaque => ~refInfo.lengthKnown,
	  ENDCASE => FALSE) =>
	Private.Error[code: InvalidHandle, type: ptrInfo.self];
      (WITH refInfo: referentInfo SELECT FROM
	  Text, StringBody => TRUE,
	  Record => refInfo.hasSequences,
	  ENDCASE => FALSE) =>
	Private.MarshalType[ nest: nest,
	  name: referentName,
	  type: referentType,
	  parentInfo: [ptrName, ptrInfo],
	  marshalInfo: marshalInfo ];
      ENDCASE =>
	MarshalFixedReferent[ nest: nest,
	  ptrName: ptrName, ptrInfo: ptrInfo,
	  referentName: referentName,  referentInfo: referentInfo,
	  parentInfo: parentInfo, marshalInfo: marshalInfo ];
    marshalInfo.ptrDepth ← marshalInfo.ptrDepth - 1;
    END;

  MarshalFixedReferent: PROCEDURE [
	ptrName: String,  ptrInfo: ST.TypeInfo,
	referentName: String,  referentInfo: ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    -- This routine expects that the referentType has a fixed,
    -- nondynamic length.  MarshalPointerTypes should have screened this.
    SELECT marshalInfo.direction FROM
      toPkt =>
	BEGIN
	Private.CopyOne[ nest: nest,
	  wordsNeeded: 1,
	  value: [ptrName, "=NIL"--L--],
	  marshalInfo: marshalInfo ];
	WFL[nest, "IF "--L--, ptrName, " # NIL THEN"--L--];
	IF Private.Passing[Result, argument, marshalInfo]
	  THEN WFL1[nest+1, "NULL;  -- Call by result, send nothing."--L--]
	  ELSE Private.MarshalType[ nest: nest+1,
		name: referentName, type: referentInfo.self,
		parentInfo: [ptrName, ptrInfo], marshalInfo: marshalInfo ];
	END;
      fromPkt =>
	BEGIN
	WFL1[nest, "isNIL: Lupine.NilHeader;"--L--];
	Private.CopyOne[ nest: nest,
	  wordsNeeded: 1,
	  value: ["isNIL"--L--],
	  marshalInfo: marshalInfo ];
	IF Private.Passing[Result, result, marshalInfo] OR
	    Private.Passing[Var,result,marshalInfo]
	  THEN BEGIN
	    WFL[nest,
	      "IF isNIL # ("--L--, ptrName,
	      "=NIL) THEN Lupine.UnmarshalingError;"--L--];
	    WFL1[nest, "IF ~isNIL THEN"--L--];
	    WFL1[nest+1, "-- Call by var or result, use existing referent."--L--];
	    Private.MarshalType[ nest: nest+1,
		name: referentName, type: referentInfo.self,
		parentInfo: [ptrName, ptrInfo], marshalInfo: marshalInfo ];
	    END
	  ELSE BEGIN
	    WFL1[nest, "IF isNIL"--L--];
	    WFL[nest+1, "THEN "--L--, ptrName, " ← NIL"--L--];
	    WFL1[nest+1, "ELSE BEGIN"--L--];
	    WFS[Indent[nest+2], ptrName, " ← ("--L--];
	    Private.WriteNEW[ptrInfo: ptrInfo, marshalInfo: marshalInfo];
	    WFS1["["--L--];
	    Declare.WriteTypeName[referentInfo.self];
	    WFS1["]);*N"--L--];
	    IF Private.Passing[Result, argument, marshalInfo]
	      THEN WFL1[nest+2,
		"NULL;  -- Call by result, use uninitialized referent."--L--]
	      ELSE Private.MarshalType[ nest: nest+2,
		name: referentName, type: referentInfo.self,
		parentInfo: [ptrName, ptrInfo], marshalInfo: marshalInfo ];
	    WFL1[nest+2, "END;  -- IF isNIL."--L--];
	    END;
	END;
      ENDCASE => ERROR;
    END;

  MarshalList: PUBLIC PROCEDURE [
	name: String,
	listInfo: List ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    Private.VerifyPassingMethods[value: TRUE, handle: TRUE, marshalInfo: marshalInfo];
    DoListOperation[ nest: nest,
      opProc: Private.MarshalType,  needsOpProc: Private.NeedsMarshaling,
      listName: name,  listInfo: listInfo,
      marshalInfo: marshalInfo ];
    END;

  DoListOperation: PROCEDURE [
	opProc: OperationProc,
	needsOpProc: NeedsOperationProc,
	listName: String,
	listInfo: List ST.TypeInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    thisNode: AllocString = [MaxGeneratedIdLength];
    thisNodeFirst: AllocString = [MaxGeneratedIdLength];
    IF opProc # Private.MarshalType THEN ERROR;
      -- DoListOperation does only marshaling correctly for now.
    Private.UniqueName[
	root: "thisNode"--L--,
	nameString: thisNode, marshalInfo: marshalInfo ];
    Private.UniqueName[
	root: "thisNode"--L--,  suffix: ".first"--L--,
	nameString: thisNodeFirst, marshalInfo: marshalInfo ];
    SELECT marshalInfo.direction FROM
      toPkt =>
	BEGIN
	WFS[Indent[nest], thisNode, ": "--L--];
	Declare.WriteTypeName[type: listInfo.self, includeReadonly: FALSE];
	WFS1[";*N"--L--];
	WFL1[nest, "listLength: Lupine.ListHeader ← 0;"--L--];
	WFSL[ Indent[nest], "FOR "--L--, thisNode, " ← "--L--,
	  listName, ", "--L--, thisNode, ".rest UNTIL "--L--,
	  thisNode, " = NIL DO*N"--L--];
	WFL1[nest+1, "listLength ← listLength + 1;  ENDLOOP;"--L--];
	Private.CopyTwo[ nest: nest,
	  wordsNeeded: 2,
	  value: SubStrings["listLength"--L--],
	  marshalInfo: marshalInfo ];
	WFSL[ Indent[nest], "FOR "--L--, thisNode, " ← "--L--,
	  listName, ", "--L--, thisNode, ".rest UNTIL "--L--,
	  thisNode, " = NIL DO*N"--L--];
	opProc[ nest: nest+1,
	  name: thisNodeFirst,
	  type: listInfo.firstType,
	  parentInfo: [listName, listInfo],
	  marshalInfo: marshalInfo ];
	WFL[nest+1, "ENDLOOP;  -- FOR "--L--, thisNode, "."--L--];
	END;
      fromPkt =>
	BEGIN
	--lastNode: AllocString = [MaxGeneratedIdLength];
	--UniqueName[
	--  root: "lastNode"L,
	--  nameString: lastNode, marshalInfo: marshalInfo ];
	WFS[Indent[nest], "lastNode: "--L--];
	Declare.WriteTypeName[type: listInfo.self, includeReadonly: FALSE];
	WFS[" ← ("--L--, listName, " ← NIL);*N"--L--];
	WFL1[nest, "listLength: Lupine.ListHeader;"--L--];
	Private.CopyTwo[ nest: nest,
	  wordsNeeded: 2,
	  value: SubStrings["listLength"--L--],
	  marshalInfo: marshalInfo ];
	WFL1[nest, "WHILE listLength > 0 DO"--L--];
	WFS[Indent[nest+1], thisNode, ": "--L--];
	Declare.WriteTypeName[type: listInfo.self, includeReadonly: FALSE];
	WFS1[" = "--L--];
	Private.WriteNEW[
	  allocOp: cons, ptrInfo: listInfo, marshalInfo: marshalInfo];
	WFS1["[--DefaultValue--,NIL];*N"--L--];
	opProc[ nest: nest+1,
	  name: thisNodeFirst,
	  type: listInfo.firstType,
	  parentInfo: [listName, listInfo],
	  marshalInfo: marshalInfo ];
	WFL1[nest+1, "IF lastNode # NIL"--L--];
	WFSL[
	  Indent[nest+2], "THEN lastNode ← (lastNode.rest ← "--L--,
	    thisNode, ")*N"--L--,
	  Indent[nest+2], "ELSE lastNode ← ("--L--, listName, " ← "--L--,
	    thisNode, ");*N"--L-- ];
	WFL1[nest+1, "listLength ← listLength - 1;"--L--];
	WFL1[nest+1, "ENDLOOP;  -- WHILE listLength > 0."--L--];
	END;
      ENDCASE => ERROR;
    END;

  MarshalArray: PUBLIC PROCEDURE [
	name: String,
	arrayInfo: Array ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    Private.VerifyPassingMethods[value: TRUE, marshalInfo: marshalInfo];
    IF Private.HasEmptyIndex[index: arrayInfo.indexType]
      THEN Private.Error[code: EmptyArray, type: arrayInfo.self]
      ELSE MarshalVector[ nest: nest,
	vectorName: name,  vectorInfo: arrayInfo,
	indexType: arrayInfo.indexType, elementType: arrayInfo.elementType,
	parentInfo: parentInfo, marshalInfo: marshalInfo ];
    END;

  MarshalDescriptor: PUBLIC PROCEDURE [
	name: String,
	descInfo: Descriptor ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    lengthString: AllocString = [2*MaxIdentifierLength];
    Private.VerifyPassingMethods[all: TRUE, marshalInfo: marshalInfo];
    CWF.SWF2[ lengthString,
      "(IF BASE[%LS]=NIL THEN 0 ELSE LENGTH[%LS])"--L--,  name, name ];
    SELECT marshalInfo.direction FROM
      toPkt =>
	BEGIN
	Private.CopyTwo[ nest: nest,
	  wordsNeeded: 2,
	  value: [lengthString],
	  marshalInfo: marshalInfo ];
	WFL[nest, "IF BASE["--L--, name, "] # NIL THEN"--L--];
	IF Private.Passing[Result, argument, marshalInfo]
	  THEN WFL1[nest+1, "NULL;  -- Call by result, send length only."--L--]
	  ELSE BEGIN
	    MarshalVector[ nest: nest+1,
		vectorName: name,  vectorInfo: descInfo,
		indexType: descInfo.indexType, elementType: descInfo.elementType,
		parentInfo: parentInfo, marshalInfo: marshalInfo ];
	    END;
	END;
      fromPkt =>
	BEGIN
	WFSL[Indent[nest],
	  "DescriptorType: TYPE = RECORD ["--L--,
	  (IF descInfo.packed THEN "PACKED "--L-- ELSE ""--L--),
	  "SEQUENCE COMPUTED CARDINAL OF "--L--];
	Declare.WriteTypeName[descInfo.elementType];
	WFS1["];*N"--L--];
	WFL1[nest, "descLength: Lupine.SequenceHeader;"--L--];
	Private.CopyTwo[ nest: nest,
	  wordsNeeded: 2,
	  value: ["descLength"--L--],
	  marshalInfo: marshalInfo];
	IF Private.Passing[Result, result, marshalInfo] OR
	    Private.Passing[Var,result,marshalInfo]
	  THEN BEGIN
	    WFL[nest, "IF descLength # "--L--, lengthString];
	    WFL[nest+1, "THEN Lupine.UnmarshalingError;"--L--];
	    WFL1[nest,
	      "NULL;  -- Call by var or result, use existing descriptor."--L--];
	    END
	  ELSE BEGIN
	    WFSL[Indent[nest], name, " ← DESCRIPTOR[*N"--L--,
	      Indent[nest+1], "("--L--];
	    Private.WriteNEW[ptrInfo: descInfo, marshalInfo: marshalInfo];
	    WFS["[DescriptorType[Lupine.SHORT[descLength]]]),*N"--L--,
	      Indent[nest+1], "Lupine.SHORT[descLength]];*N"--L--];
	    END;
	IF Private.Passing[Result, argument, marshalInfo]
	  THEN WFL1[nest,
	    "NULL;  -- Call by result, use uninitialized descriptor."--L--]
	  ELSE BEGIN
	    MarshalVector[ nest: nest,
		vectorName: name,  vectorInfo: descInfo,
		indexType: descInfo.indexType, elementType: descInfo.elementType,
		parentInfo: parentInfo, marshalInfo: marshalInfo ]
	    END;
	END;
      ENDCASE => ERROR;
    END;

  MarshalSequence: PUBLIC PROCEDURE [
	name: String,
	seqInfo: Sequence ST.TypeInfo,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    Private.VerifyPassingMethods[value: TRUE, marshalInfo: marshalInfo];
    WITH tagName: seqInfo SELECT FROM
      Computed => Private.Error[code: ComputedSequence, type: seqInfo.self];
      Named =>
	BEGIN
	WFL1[nest, "-- The sequence's length was carried by its record."--L--];
	IF ~Private.NeedsMarshaling[seqInfo.elementType] THEN ERROR;
	MarshalVector[ nest: nest,
	  vectorName: name,  vectorInfo: seqInfo,
	  indexType: seqInfo.indexType, elementType: seqInfo.elementType,
	  parentInfo: parentInfo, marshalInfo: marshalInfo ];
	END;
      ENDCASE => ERROR;
    END;

  MarshalVector: PROCEDURE [
	vectorName: String,
	vectorInfo: ST.TypeInfo,
	indexType, elementType: ST.TypeHandle,
	parentInfo: ParentInfo,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    -- MarshalVector must generate exactly one statement.
    -- Caller guarantees that the vector has >= 0 elements in it, and that
    -- any header information for sequences and descriptors has been sent.
    IF ~Private.NeedsMarshaling[elementType]
      THEN Private.CopyType[ nest: nest,
	      variableName: vectorName,  variableInfo: vectorInfo,
	      parentInfo: parentInfo,  marshalInfo: marshalInfo ]
      ELSE DoVectorOperation[ nest: nest,
	      opProc: Private.MarshalType,  needsOpProc: Private.NeedsMarshaling,
	      vectorName: vectorName,  vectorInfo: vectorInfo,
	      indexType: indexType,  elementType: elementType,
	      marshalInfo: marshalInfo ];
    END;

  DoVectorOperation: PROCEDURE [
	opProc: OperationProc,
	needsOpProc: NeedsOperationProc,
	vectorName: String,
	vectorInfo: ST.TypeInfo,
	indexType, elementType: ST.TypeHandle,
	marshalInfo: MarshalInfo,
	nest: Nest ] =
    BEGIN
    -- Must generate one statement.
    IF needsOpProc[elementType]
      THEN BEGIN
	indexName: AllocString = [MaxGeneratedIdLength];
	elementName: AllocString = [MaxIdentifierLength+MaxGeneratedIdLength];
	Private.UniqueName[
	  root: "element"--L--, nameString: indexName, marshalInfo: marshalInfo];
	CWF.SWF2[elementName, "%LS[%S]"--L--, vectorName, indexName];
	WFSL[Indent[nest], "FOR "--L--, indexName, ": "--L--];
	Declare.WriteTypeName[indexType];
	WFS1[" IN "--L--];
	SELECT vectorInfo.type FROM
	  Array => Declare.WriteTypeName[indexType];
	  Descriptor, Sequence =>
	    BEGIN
	    WFS1["[FIRST["--L--]; Declare.WriteTypeName[indexType];
	    WFS1["]..FIRST["--L--]; Declare.WriteTypeName[indexType];
	    WFSL["]+LENGTH["--L--,
	      (IF vectorInfo.type=Sequence THEN "DESCRIPTOR["--L-- ELSE ""--L--),
	      vectorName,
	      (IF vectorInfo.type=Sequence THEN "]"--L-- ELSE ""--L--),
	      "])"--L--];
	    END;
	  ENDCASE => ERROR;
	WFS1[" DO*N"--L--];
	opProc[ nest: nest+1,
	  name: elementName, type: elementType,
	  parentInfo: [vectorName, vectorInfo],
	  marshalInfo: marshalInfo ];
	WFL[nest+1, "ENDLOOP;  -- FOR "--L--, indexName, "."--L--];
	END;
    END;


-- Marshaling utility routines.

  MakeRefsNil: OperationProc =
    BEGIN
    -- Make only refs in safe storage NIL, not those in pkts.
    IF marshalInfo.direction=fromPkt AND Private.ContainsRefs[type: type]
      THEN BEGIN
	WFL1[nest, "-- Restore garbled REFs to NIL following copy."--L--];
	AssignNilToRefs[ nest: nest,
	  name: name,  type: type,
	  parentInfo: parentInfo,  marshalInfo: marshalInfo ];
	END;
    END;

  AssignNilToRefs: OperationProc =
    BEGIN
    typeInfo: ST.TypeInfo = ST.GetTypeInfo[type: type];
    SELECT marshalInfo.paramFieldInfo.passingMethod FROM
      Handle => NULL;
      InterMds => ERROR;
      Var, Value, Result =>
	WITH typeInfo: typeInfo SELECT FROM
	  Null, Definition, Basic,
	      Pointer, RelativePtr, String, StringBody, Text,
	      Any, Opaque, Zone, Other => NULL;
	  Ref, List, Rope, Atom, Transfer =>
	    WFLL[nest,
 		"LOOPHOLE["--L--, name, ", "--L--,
		(IF typeInfo.type#Transfer THEN "LONG "--L-- ELSE ""--L--),
		"POINTER] ← NIL;"--L-- ];
	  Record =>
	    DoRecordOperation[ nest: nest,
	      opProc: AssignNilToRefs,  needsOpProc: Private.ContainsRefs,
	      recName: name,
	      recInfo: typeInfo,
	      marshalInfo: marshalInfo ];
	  VariantPart =>
	    DoVariantOperation[ nest: nest,
	      opProc: AssignNilToRefs,  needsOpProc: Private.ContainsRefs,
	      varName: name,
	      varInfo: typeInfo,
	      marshalInfo: marshalInfo ];
	  Array =>
	    DoVectorOperation[ nest: nest,
	      opProc: AssignNilToRefs,  needsOpProc: Private.ContainsRefs,
	      vectorName: name,
	      vectorInfo: typeInfo,
	      indexType: typeInfo.indexType, elementType: typeInfo.elementType,
	      marshalInfo: marshalInfo ];
	  Descriptor =>
	    DoVectorOperation[ nest: nest,
	      opProc: AssignNilToRefs,  needsOpProc: Private.ContainsRefs,
	      vectorName: name,
	      vectorInfo: typeInfo,
	      indexType: typeInfo.indexType, elementType: typeInfo.elementType,
	      marshalInfo: marshalInfo ];
	  Sequence =>
	    DoVectorOperation[ nest: nest,
	      opProc: AssignNilToRefs,  needsOpProc: Private.ContainsRefs,
	      vectorName: name,
	      vectorInfo: typeInfo,
	      indexType: typeInfo.indexType, elementType: typeInfo.elementType,
	      marshalInfo: marshalInfo ];
	  ENDCASE => ERROR;
      ENDCASE => ERROR;
    END;

  FieldName: PROCEDURE [
	recordName: String ← "record"L,
	recordType: ST.TypeHandle,
	fieldSymbol: ST.SymbolHandle,
	fieldString: String,
	anonOk: BOOLEAN ← FALSE ]
      RETURNS [recordDotField: String ← NULL] =
    BEGIN
    recordDotField ← fieldString;
    IF anonOk OR ~ST.IsAnonymous[symbol: fieldSymbol]
      THEN BEGIN
	symbolString: AllocString = [MaxIdentifierLength];
        CWF.SWF2[
          recordDotField, "%LS.%LS"L,
	  recordName,  ST.SymbolName[fieldSymbol, symbolString] ];
	END
      ELSE BEGIN
	Private.Error[code: AnonymousIdentifier, type: recordType];
	CWF.SWF1[recordDotField, "%S"L, "record.unnamedField"L];
	END;
    END;


END.  -- LupineMarshalTypeConstructorImpl.