-- File [Ivy]<Nelson>Lupine>LupineMarshalInfoImpl.mesa.
-- Last edited by BZM on  9-Mar-82 18:19:55.

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


DIRECTORY
  Heap USING [systemZone],
  LupineManagerPrivate USING [
	ErrorCode, ModuleName, Options, ParamPassingMethod,
	SHORT, String ],
  LupineMarshal USING [
	AddressInfo, AllocZone, AllocDetails, AllocInfo,
	EnumerateParams, FalseAddressInfo,
	FieldInfo, NoAllocations, OverlayParamType,
	ParamIndex, ParamInfo, ParamInfoNIL, ParamLocation,
	ParamProcedure, ParamRecordKind, ParamInfoObject, PktSite,
	SizeInfo, TransferDeclaration, TransferSite,
	Words, ZeroSizeInfo ],
  LupineMarshalPrivate USING [
	HasEmptyIndex, HasDynamicIndex,
	MaxPointerDepth, MaxDataSize, MaxShortStringLength,
	NeedsOperationProc, Warning ],
  LupineSymbolTable USING [
	ComponentProcedure, ComputeArraySize, EnumerateRecord,
	EnumerateVariants, FullTypeName,
	GetTypeInfo, SearchTypeDefinition, Size, TransferTypes,
	TypeHandle, TypeInfo, Types, VariantProcedure ];


LupineMarshalInfoImpl: PROGRAM
  IMPORTS
	Heap, LupineManagerPrivate, Marshal: LupineMarshal,
	Private: LupineMarshalPrivate, ST: LupineSymbolTable
  EXPORTS LupineMarshal, LupineMarshalPrivate
  SHARES LupineMarshal
  = BEGIN OPEN LupineManagerPrivate, LupineMarshal;


 -- Types and constants from the internal LupineManagerPrivate interface.

  NeedsOperationProc: TYPE = Private.NeedsOperationProc;
  MaxDataSize: Words = Private.MaxDataSize;
  MaxPointerDepth: INTEGER = Private.MaxPointerDepth;


-- The ParamInfo routines pull together all the information needed for
-- parameter marshaling and efficient one-packet-call stub generation.

  MakeParamInfo: PUBLIC PROCEDURE [
	paramRecord: ST.TypeHandle,
	paramRecordKind: ParamRecordKind,
	pktSite: PktSite,
	RESULTsParamInfo: ParamInfo←ParamInfoNIL,
	transferType: ST.TransferTypes,
	transferDeclaration: TransferDeclaration,
	transferSite: TransferSite,
	options: Options ] 
      RETURNS[paramInfo: ParamInfo] =
    BEGIN
    IsRESULT: PROCEDURE [typeInfo: ST.TypeInfo] RETURNS[--yes:-- BOOLEAN] =
      BEGIN
      RETURN[
	SELECT DeterminePassingMethod [
	  typeInfo: typeInfo, paramKind: paramRecordKind, options: options ] FROM
	    Var, Result => TRUE,
	    Value, Handle, InterMds => FALSE,
	    ENDCASE => ERROR ];
      END;  -- IsRESULT.
    paramCount, RESULTSubCount: ParamIndex ← 0;
    BEGIN
    CountParams: ST.ComponentProcedure = 
      BEGIN
      paramCount ← componentIndex;
      IF IsRESULT[ST.GetTypeInfo[componentType]]
        THEN RESULTSubCount ← RESULTSubCount + 1;
      END;
    [] ← ST.EnumerateRecord[recordType: paramRecord, proc: CountParams];
    END;
    SELECT paramRecordKind FROM
      argument => IF RESULTsParamInfo # ParamInfoNIL THEN ERROR;
      result =>
	IF RESULTsParamInfo = ParamInfoNIL OR
	  RESULTsParamInfo.paramRecordKind # argument THEN ERROR;
      ENDCASE => ERROR;
    paramInfo ← Heap.systemZone.NEW[
      ParamInfoObject [
	    1		  -- The zeroth parameter is unassigned.
	  + paramCount   -- Explicit parameters.
	  + (IF RESULTsParamInfo=NIL THEN 0 ELSE RESULTsParamInfo.RESULTCount)
			  -- Implicit VARs and RESULTs in result record.
	  ] ← [
	paramRecord: paramRecord,
	paramRecordKind: paramRecordKind,
	pktSite: pktSite, 
	transferType: transferType,
	transferDeclaration: transferDeclaration,
	transferSite: transferSite,
	options: options,
	alwaysOnePkt: TRUE, alwaysMultiplePkts: FALSE,
	hasOverlayParams: FALSE,
	hasOverlayParamType: ALL[FALSE],
	hasConversation: FALSE,
	adrInfo: FalseAddressInfo,
	allocInfo: NoAllocations,
	sizeOf: ZeroSizeInfo,
	paramCount: paramCount,
	RESULTCount: RESULTSubCount,
	RESULTsParamInfo: RESULTsParamInfo,
	fields: NULL ]];
    BEGIN ENABLE UNWIND => Heap.systemZone.FREE[@paramInfo];
    overlaySize: Words ← OverlayHeaderLength[paramInfo];
      -- GetFieldInfo and UpdateParamInfo cooperatively maintain this variable.
    minDataSize, maxDataSize: Words ← overlaySize;
    GetFieldInfo: PROCEDURE [
	  typeInfo: ST.TypeInfo, fieldNumber: ParamIndex,
	  passingMethod: ParamPassingMethod ]
	RETURNS [fieldInfo: FieldInfo] =
      BEGIN
      location: ParamLocation;
      overlayParamType: OverlayParamType ← other;
      adrInfo: AddressInfo = ParamAddressInfo[
	typeInfo: typeInfo,
	passingMethod: passingMethod, paramKind: paramRecordKind ];
      allocInfo: AllocInfo ← NoAllocations;
      fieldSize: Words = Size[typeInfo.self];
      minFlatSize, maxFlatSize: Words ← 0;
      IF passingMethod=InterMds AND adrInfo.hasShortPtrs
	THEN Private.Warning[code: ShortInterMdsPointers, type: typeInfo.self];
      IF fieldNumber=1 AND paramRecordKind=argument AND IsConversation[typeInfo.self]
	THEN location ← inConversation
	ELSE BEGIN
	  SELECT TRUE FROM
	    adrInfo.isStatic AND overlaySize+fieldSize <= MaxDataSize =>
	      {location ← inPktOverlay; overlayParamType ← static};
	    adrInfo.isAddress =>
	      {location ← inFrame; overlayParamType ← address};
	    ENDCASE => {location ← inFrame};
 	  [minFlatSize, maxFlatSize] ← MarshaledFlatSize[
	    type: typeInfo.self,
	    passingMethod: passingMethod, paramKind: paramRecordKind ];
	  allocInfo ← GetAllocInfo[
	    type: typeInfo.self,
	    passingMethod: passingMethod, paramKind: paramRecordKind ];
	  END;
      RETURN[ FieldInfo [
	  type: typeInfo.type,
	  passingMethod: passingMethod,
	  location: location,
	  overlayParamType: overlayParamType,
	  adrInfo: adrInfo,
	  allocInfo: allocInfo,
	  size: fieldSize,
	  minFlatSize: minFlatSize, maxFlatSize: maxFlatSize ]  ];
      END;  -- GetFieldInfo.
    UpdateParamInfo: PROCEDURE [
	paramInfo: --VAR-- ParamInfo,
	fieldInfo: FieldInfo,
	staticIsError: BOOLEAN ← FALSE ] =
      BEGIN OPEN adrInfo: fieldInfo.adrInfo;
      IF staticIsError AND adrInfo.isStatic THEN ERROR;
      SELECT fieldInfo.location FROM
	inConversation => paramInfo.hasConversation ← TRUE;
	inPktOverlay, inFrameAndOverlay =>
	  BEGIN
	  paramInfo.hasOverlayParams ← TRUE;
	  paramInfo.hasOverlayParamType[fieldInfo.overlayParamType] ← TRUE;
	  overlaySize ← overlaySize + fieldInfo.size;
	  IF overlaySize > MaxDataSize THEN ERROR;
	  END;
	inFrame, inStream => NULL;
	ENDCASE => ERROR;
      --IF adrInfo.isAddress OR adrInfo.isTransfer
	--THEN BEGIN
	  -- Don't send the addresses of top-level AC params, just the referents.
	  --minDataSize ← minDataSize + fieldInfo.minFlatSize - fieldInfo.size;
	  --maxDataSize ← maxDataSize + fieldInfo.maxFlatSize - fieldInfo.size;
	  --END
	--ELSE BEGIN
      minDataSize ← minDataSize + fieldInfo.minFlatSize;
      maxDataSize ← maxDataSize + fieldInfo.maxFlatSize;
	  --END;
      paramInfo.allocInfo ← AddAllocInfo[paramInfo.allocInfo, fieldInfo.allocInfo];
      paramInfo.adrInfo ← AddressInfo[
	  hasStatics: adrInfo.hasStatics OR paramInfo.adrInfo.hasStatics,
	  hasAddresses: adrInfo.hasAddresses OR paramInfo.adrInfo.hasAddresses,
	  hasDynamics: adrInfo.hasDynamics OR paramInfo.adrInfo.hasDynamics,
	  hasTransfers: adrInfo.hasTransfers OR paramInfo.adrInfo.hasTransfers,
	  hasGC: adrInfo.hasGC OR paramInfo.adrInfo.hasGC,
	  hasHeap: adrInfo.hasHeap OR paramInfo.adrInfo.hasHeap,
	  hasMds: adrInfo.hasMds OR paramInfo.adrInfo.hasMds,
	  hasShortPtrs: adrInfo.hasShortPtrs OR paramInfo.adrInfo.hasShortPtrs ];
      END;  -- UpdateParamInfo.
    IF paramCount > 0
      THEN BEGIN
        ComputeFieldInfo: ST.ComponentProcedure =
	  BEGIN
	  typeInfo: ST.TypeInfo = ST.GetTypeInfo[componentType];
	  passingMethod: ParamPassingMethod = DeterminePassingMethod[
	    typeInfo: typeInfo, paramKind: paramRecordKind, options: options,
	    reportErrors: TRUE ];
	  fieldInfo: FieldInfo = GetFieldInfo[typeInfo, componentIndex, passingMethod];
	  paramInfo.fields[componentIndex] ← fieldInfo;
	  UpdateParamInfo[paramInfo, fieldInfo];
	  END;  -- ComputeFieldInfo.
	[] ← ST.EnumerateRecord[recordType: paramRecord, proc: ComputeFieldInfo];
	END;  -- Of parameter record processing.
    IF paramRecordKind=result AND RESULTsParamInfo.RESULTCount > 0
      THEN BEGIN
	resultIndex: ParamIndex ← paramCount;
	ComputeResultInfo: ParamProcedure =
	  BEGIN
	  SELECT paramFieldInfo.passingMethod FROM
	    Var, Result =>
	      BEGIN
	      resultFieldInfo: FieldInfo = GetFieldInfo[
		ST.GetTypeInfo[paramType], paramIndex, paramFieldInfo.passingMethod ];
	      paramInfo.fields[(resultIndex←resultIndex+1)] ← resultFieldInfo;
	      UpdateParamInfo[paramInfo, resultFieldInfo];
	      END;
	    ENDCASE => NULL;
	  END;  -- ComputeResultInfo.
	Marshal.EnumerateParams[
	  paramInfo: RESULTsParamInfo, paramProc: ComputeResultInfo ];
	IF resultIndex # paramCount+RESULTsParamInfo.RESULTCount THEN ERROR;
	END;  -- Of implicit results processing.
    paramInfo.sizeOf ← [
      overlayHeader: OverlayHeaderLength[paramInfo],
      overlayParamRecord: overlaySize,
      pktToAllocate: IF paramInfo.adrInfo.hasDynamics 
	THEN MaxDataSize ELSE MIN[MaxDataSize, maxDataSize] ];
    paramInfo.alwaysOnePkt ←
	~paramInfo.adrInfo.hasDynamics AND maxDataSize <= MaxDataSize;
    paramInfo.alwaysMultiplePkts ← minDataSize > MaxDataSize;
    END;
    RETURN[--READONLY-- paramInfo];
    END;


  FreeParamInfo: PUBLIC PROCEDURE [paramInfo: ParamInfo] =
    BEGIN
    IF paramInfo # ParamInfoNIL THEN Heap.systemZone.FREE[@paramInfo];
    END;

  EnumerateParams: PUBLIC PROCEDURE [
	paramInfo: ParamInfo,
	paramProc: ParamProcedure,
	includeRESULTs: BOOLEAN←FALSE ] =
    BEGIN
    -- This enumerator is in this module because it cooperates intimately
    -- with MakeParamInfo in the handling of VAR and RESULT parameters.
    resultInfo: ParamInfo = paramInfo.RESULTsParamInfo;
    IF paramInfo.paramCount > 0
      THEN BEGIN
	DoParam: ST.ComponentProcedure =
	  BEGIN
	  RETURN[
	    stop: paramProc[
		paramName: component,
		paramType: componentType,
		paramIndex: componentIndex,
		paramFieldInfo: paramInfo.fields[componentIndex]].stop ];
	  END;  -- DoParam.
	[] ← ST.EnumerateRecord[recordType: paramInfo.paramRecord, proc: DoParam];
	END;
    IF includeRESULTs AND
	  paramInfo.paramRecordKind=result AND resultInfo.RESULTCount > 0
      THEN BEGIN
	resultIndex: ParamIndex ← paramInfo.paramCount;
	DoParam: ParamProcedure =
	  BEGIN
	  SELECT paramFieldInfo.passingMethod FROM
	    Var, Result => RETURN[
	      stop: paramProc[
		paramName: paramName,
		paramType: paramType,
		paramIndex: paramIndex,
		paramFieldInfo:
		  paramInfo.fields[(resultIndex←resultIndex+1)]].stop ];
	    ENDCASE => NULL;
	  END;  -- DoParam.
	EnumerateParams[resultInfo, DoParam];
	END;
    END;


-- Address Information Routines.

  ParamAddressInfo: PROCEDURE [
	typeInfo: ST.TypeInfo,
	passingMethod: ParamPassingMethod,
	paramKind: ParamRecordKind ]
      RETURNS[adrInfo: AddressInfo←FalseAddressInfo] =
    BEGIN
    IsAdr: BOOLEAN = SELECT passingMethod FROM
      Handle, InterMds => FALSE, Var, Value, Result => TRUE, ENDCASE => ERROR;
    IsTransfer: BOOLEAN =
      passingMethod # Handle AND typeInfo.passingMethod # handle;
    --IsDynamic, IsStatic: BOOLEAN = TRUE;
    TraceAddresses: PROC [type: ST.TypeHandle, ptrDepth: INTEGER] =
      BEGIN
      info: ST.TypeInfo = ST.GetTypeInfo[type: type];
      NoteAddress: PROC [kind: {pointer, ref}]
	  RETURNS [traceAddressFurther: BOOLEAN] =
	BEGIN
	IF IsAdr
	  THEN BEGIN
	    adrInfo.hasAddresses ← TRUE;
	    SELECT kind FROM
	      pointer => IF info.long
		  THEN adrInfo.hasHeap ← TRUE
		  ELSE adrInfo.hasMds ← TRUE;
	      ref => adrInfo.hasGC ← TRUE;
	      ENDCASE => ERROR;
	    END
	  ELSE NoteStatic;
	IF kind=pointer AND ~info.long THEN adrInfo.hasShortPtrs ← TRUE;
	RETURN[ IsAdr AND ~(passingMethod=Result AND paramKind=argument) ];
	END;  -- NoteAddress.
      NoteStatic: PROC = INLINE {adrInfo.hasStatics ← TRUE};
      WITH info: info SELECT FROM
        Null, Basic, RelativePtr, Opaque =>
	  NoteStatic;
	Transfer =>
	  IF IsTransfer THEN adrInfo.hasTransfers ← TRUE ELSE NoteStatic;
        Record =>
	  BEGIN  -- Monitored is reported elsewhere.
	  CheckField: ST.ComponentProcedure =
	    {TraceAddresses[componentType, ptrDepth]};
	  NoteStatic;
	  [] ← ST.EnumerateRecord[recordType: type, proc: CheckField];
	  END;
        VariantPart =>
	  BEGIN  -- Computed is reported elsewhere.
	  CheckVariant: ST.VariantProcedure =
	    {TraceAddresses[variantRecordType, ptrDepth]};
	  [] ← ST.EnumerateVariants[variantPartType: type, proc: CheckVariant];
	  END;
	Text, StringBody =>
	  adrInfo.hasDynamics ← TRUE;
	String =>
	  IF NoteAddress[pointer] THEN
	    adrInfo.hasDynamics ← adrInfo.hasDynamics OR ~IsShortString[type];
	Rope, Atom =>
	  IF NoteAddress[ref] THEN
	    adrInfo.hasDynamics ← adrInfo.hasDynamics OR ~IsShortString[type];
	Pointer =>
	  BEGIN
	  IF ptrDepth > MaxPointerDepth THEN RETURN;
	  IF NoteAddress[pointer] THEN TraceAddresses[info.referentType, ptrDepth+1];
	  END;
	Ref =>
	  BEGIN
	  IF ptrDepth > MaxPointerDepth THEN RETURN;
	  IF NoteAddress[ref] THEN TraceAddresses[info.referentType, ptrDepth+1];
	  END;
        List =>
	  IF NoteAddress[ref]
	    THEN BEGIN
	    adrInfo.hasDynamics ← TRUE;
	    TraceAddresses[info.firstType, ptrDepth];
	    END;
        Array =>
	  BEGIN
	  IF Private.HasEmptyIndex[index: info.indexType]
	    THEN adrInfo.hasDynamics ← TRUE  -- Obsolete sequence idiom.
	    ELSE NoteStatic;  -- Normal, static array.
	  TraceAddresses[info.elementType, ptrDepth];
	  END;
        Descriptor =>
	  IF NoteAddress[pointer]
	    THEN BEGIN
	    adrInfo.hasDynamics ←
	      adrInfo.hasDynamics OR Private.HasDynamicIndex[vectorInfo: info];
	    TraceAddresses[info.elementType, ptrDepth];
	    END;
	Sequence =>
	  BEGIN  -- Computed is reported elsewhere.
	  IF info.kind = Computed THEN RETURN;
	  IF Private.HasDynamicIndex[vectorInfo: info]
	    THEN adrInfo.hasDynamics ← TRUE ELSE NoteStatic;
	  TraceAddresses[info.elementType, ptrDepth];
	  END;
	Zone =>  -- Zones must be handles.
	  {NoteStatic; IF info.mdsZone THEN adrInfo.hasShortPtrs ← TRUE};
        Definition, Any, Other =>
	  adrInfo.hasAddresses ← TRUE;  -- Will cause an error during marshaling.
        ENDCASE => ERROR;
      END;  -- TraceAddresses.
    TraceAddresses[type: typeInfo.self, ptrDepth: 0];
    WITH typeInfo: typeInfo SELECT FROM
      Null, Basic, Record, VariantPart,
	RelativePtr, Zone, Opaque => NULL;
      Transfer => adrInfo.isTransfer ← IsTransfer;
      Text, StringBody => adrInfo.isDynamic ← TRUE;
      String, Rope, Atom, Pointer, Ref, 
	List, Descriptor => adrInfo.isAddress ← IsAdr;
      Array =>
        adrInfo.isDynamic ← Private.HasEmptyIndex[typeInfo.indexType];
      Sequence =>
        adrInfo.isDynamic ← Private.HasDynamicIndex[vectorInfo: typeInfo];
      Definition, Any, Other => adrInfo.isAddress ← TRUE;
        -- Will cause an error during marshaling.
      ENDCASE => ERROR;
    adrInfo.isStatic ← NOT (
      adrInfo.isAddress OR adrInfo.isDynamic OR adrInfo.isTransfer OR
      adrInfo.hasAddresses OR adrInfo.hasDynamics OR adrInfo.hasTransfers );
    IF adrInfo.isStatic AND ~adrInfo.hasStatics THEN ERROR;
    END;


-- Size Information Routines.

  HeaderWords: TYPE = Words[0..3];

  HeaderInfo: TYPE = RECORD [
	protocolHeaderSize: HeaderWords ← 0,
	useStandardSize: BOOLEAN ← FALSE] ← [0, TRUE];

  NullHeader: HeaderWords = 0;
  NilHeader: HeaderWords = SIZE[BOOLEAN];
  StringHeader: HeaderWords = SIZE[ RECORD[maxLength,length:CARDINAL] ];
  RopeHeader: HeaderWords = SIZE[CARDINAL];
  SequenceHeader: HeaderWords = SIZE[LONG CARDINAL];

  ProtocolHeaderInfo: PACKED ARRAY ST.Types OF HeaderInfo = [
    Transfer: [NullHeader],
	-- Callback transfers are statically numbered and therefore have
	-- no per-transfer overhead.  One set of CallbackDispatcherDetails
	-- is used to marshal all the transfer parameters in a given routine.
    Pointer: [NilHeader],
	-- Pointers are marshaled by prefixing their referents with an
	-- isNIL tag.  If true, nothing follows because the pointer is NIL.
	-- If false, then the referent follows.
    Ref: [NilHeader],
	-- Refs are the same as pointers.
    String: [NilHeader+StringHeader],
	-- Strings are like Text, but the full CARDINAL range can be used.
	-- NIL is mapped correctly.
    Text: [StringHeader],
	-- Text is marshaled by preceding the characters with the
	-- NAT (a subset of CARDINAL) maxLength and length.
    StringBody: [StringHeader],
	-- StringBody is the same as string proper, but there's no pointer.
    Rope: [NilHeader+RopeHeader],
	-- A rope is marshaled by preceding the rope's characters with
	-- the rope's NAT (subset of CARDINAL) length.
	-- NIL is mapped correctly.
    Atom: [NilHeader+RopeHeader],
	-- An atom's PName is marshaled like a rope.
    List: [SequenceHeader],
	-- A list is preceded by its LONG CARDINAL number of nodes.
	-- NIL is equivalent to zero nodes.
    Descriptor: [SequenceHeader],
	-- A descriptor's elements are preceded by the descriptor's
	-- LONG CARDINAL LENGTH (# of elements).  LONG is used for
	-- future compatibility.
	-- NIL is equivalent to zero elements.
    Sequence: [SequenceHeader],
	-- Sequences are similar to descriptors, BUT THE LENGTH APPEARS
	-- IN FRONT OF THE CONTAINING RECORD, NOT BEFORE THE SEQUENCE PART.
	-- There's never NIL, although the (separate) record pointer might be.
    Zone: []  ];
	-- Zone pointers must always be handles.


  MinFlatSize: PROCEDURE [typeInfo: ST.TypeInfo, zeroIfStandard: BOOLEAN]
      RETURNS [--minimumSize:-- Words] =
     INLINE BEGIN
     RETURN[ IF ProtocolHeaderInfo[typeInfo.type].useStandardSize 
	THEN (IF zeroIfStandard THEN 0 ELSE Size[typeInfo.self])
	ELSE ProtocolHeaderInfo[typeInfo.type].protocolHeaderSize ];
     END;

  OverlayHeaderLength: PROCEDURE [paramInfo: ParamInfo]
	RETURNS [--length:-- Words] =
    BEGIN
    TransferIndexSize: INTEGER = SIZE[WORD];
    CallbackBindingDetailsSize: INTEGER = 0;--SIZE[RpcLupine.CallbackBindingDetails];
    RETURN[
      SELECT paramInfo.paramRecordKind FROM
	argument =>
	  SELECT paramInfo.transferDeclaration FROM
	    inInterface, inRoutine =>
	      TransferIndexSize + (IF paramInfo.adrInfo.hasTransfers
		THEN CallbackBindingDetailsSize ELSE 0),
	    ENDCASE => ERROR,
	result => 0,
	ENDCASE => ERROR ];
    END;


  MarshaledFlatSize: PROCEDURE [
	type: ST.TypeHandle,
	passingMethod: ParamPassingMethod,
	paramKind: ParamRecordKind ]
      RETURNS [minFlatSize, maxFlatSize: Words] =
    BEGIN
    dynamic: BOOLEAN ← FALSE;
    stopAtAddresses: BOOLEAN = SELECT passingMethod FROM
	Var, Value => FALSE,
	Result => paramKind=argument,
	Handle, InterMds => TRUE,
	ENDCASE => ERROR;
    TraceSize: PROCEDURE [
	  type: ST.TypeHandle,
	  include: {referentsOnly, bodyToo},
	  ptrDepth: INTEGER ]
	RETURNS [minSize, maxSize: Words←0] =
      BEGIN
      TraceArray: PROCEDURE [
	    arrayInfo: ST.TypeInfo,
	    indexType, elementType: ST.TypeHandle,
	    indexRange: {fixed, dynamic},
	    include: {referentsOnly, arrayToo} ]
	  RETURNS [minSize, maxSize: Words] =
	BEGIN
	numElements: LONG INTEGER = Cardinality[indexType];
	minSubElement, maxSubElement: Words;
	arraySize: Words = SELECT include FROM
	  arrayToo => VectorSize[arrayInfo], referentsOnly => 0,
	  ENDCASE => ERROR;
	[minSubElement, maxSubElement] ←
	  TraceSize[elementType, referentsOnly, ptrDepth];	      
	IF indexRange=dynamic
	  THEN dynamic ← Private.HasDynamicIndex[vectorInfo: arrayInfo];
	RETURN[
	  minSize: IF indexRange=dynamic
		      THEN 0 ELSE (numElements*minSubElement)+arraySize,
	  maxSize: (numElements*maxSubElement)+arraySize ];
	END;  -- TraceArray.
      typeInfo: ST.TypeInfo = ST.GetTypeInfo[type: type];
      baseSize: Words = SELECT passingMethod FROM
	Handle, InterMds => IF include=referentsOnly THEN 0 ELSE Size[type],
	ENDCASE => MinFlatSize[
	    typeInfo: typeInfo, zeroIfStandard: include=referentsOnly ];
      minExtra, maxExtra: Words ← 0;
      WITH info: typeInfo SELECT FROM
        Null, Definition, Basic, Transfer,
	    Any, RelativePtr, Opaque, Zone, Other => NULL;
	Text, StringBody => dynamic ← TRUE;
	String, Rope, Atom =>
	  SELECT TRUE FROM
	    stopAtAddresses => NULL;
	    ~IsShortString[type] => dynamic ← TRUE;
	    ENDCASE =>
	      BEGIN  -- Small strings have bounded length:
	      maxExtra←SIZE[
	        PACKED ARRAY [0..Private.MaxShortStringLength) OF CHARACTER ];
	      minExtra ← 0;
	      END;
	Record =>
	  BEGIN
	  CheckComponent: ST.ComponentProcedure =
	    BEGIN
	    min, max: Words;
	    [min, max] ← TraceSize[componentType, referentsOnly, ptrDepth];
	    minExtra ← minExtra + min;  maxExtra ← maxExtra + max;
	    END;  -- CheckComponent.
	  [] ← ST.EnumerateRecord[recordType: type, proc: CheckComponent];
	  END;
        VariantPart =>
	  BEGIN
	  CheckVariant: ST.VariantProcedure =
	    BEGIN
	    min, max: Words;
	    [min, max] ← TraceSize[variantRecordType, referentsOnly, ptrDepth];
	    minExtra ← MAX[minExtra, min];  maxExtra ← MAX[maxExtra, max];
	    END;  -- CheckVariant.
	  [] ← ST.EnumerateVariants[variantPartType: type, proc: CheckVariant];
	  END;
        Pointer =>
	  BEGIN
	  IF ptrDepth > MaxPointerDepth THEN RETURN;
	  IF ~stopAtAddresses THEN {
	      maxExtra←TraceSize[info.referentType, bodyToo, ptrDepth+1].maxSize;
	      minExtra ← 0; }  -- Pointer can be NIL => no extra.
	  END;
        Ref =>
	  BEGIN
	  IF ptrDepth > MaxPointerDepth THEN RETURN;
	  IF ~stopAtAddresses THEN {
	      maxExtra←TraceSize[info.referentType, bodyToo, ptrDepth+1].maxSize;
	      minExtra ← 0; }  -- Ref can be NIL => no extra.
	  END;
        List => IF ~stopAtAddresses THEN dynamic ← TRUE;
        Array =>
	  [minExtra, maxExtra] ← TraceArray[
	    arrayInfo: info,
	    indexType: info.indexType, elementType: info.elementType,
	    indexRange: fixed, include: referentsOnly ];
        Descriptor =>
 	  SELECT TRUE FROM
	    stopAtAddresses => NULL;
	    ENDCASE =>
	      [minExtra, maxExtra] ← TraceArray[
		arrayInfo: info,
		indexType: info.indexType, elementType: info.elementType,
		indexRange: dynamic, include: arrayToo ];
        Sequence =>
	  [minExtra, maxExtra] ← TraceArray[
	    arrayInfo: info,
	    indexType: info.indexType, elementType: info.elementType,
	    indexRange: dynamic, include: arrayToo ];
        ENDCASE => ERROR;
      RETURN[baseSize+minExtra, baseSize+maxExtra];
      END;  -- TraceSize.
    [minFlatSize, maxFlatSize] ← TraceSize[
	type: type, include: bodyToo, ptrDepth: 0];
    RETURN[minFlatSize, (IF dynamic THEN LAST[Words] ELSE maxFlatSize)];
    END;


-- Explicit storage allocation information routines.

  GetAllocInfo: PROCEDURE [
	type: ST.TypeHandle,
	passingMethod: ParamPassingMethod,
	paramKind: ParamRecordKind]
      RETURNS [allocInfo: AllocInfo] =
    BEGIN
    includeThisAlloc: BOOLEAN = SELECT passingMethod FROM
	Var, Result => paramKind=argument,
	Value => TRUE,
	Handle, InterMds => FALSE,
	ENDCASE => ERROR;
    TracePointers: PROCEDURE [
	  type: ST.TypeHandle,
	  ptrDepth: INTEGER ]
	RETURNS [allocInfo: AllocInfo←NoAllocations] =
      BEGIN
      TraceArray: PROCEDURE [
	    arrayTypeInfo: ST.TypeInfo,
	    indexType, elementType: ST.TypeHandle,
	    indexRange: {fixed, dynamic} ]
	  RETURNS [arrayInfo: AllocInfo] =
	BEGIN
	elementInfo: AllocInfo = TracePointers[elementType, ptrDepth];
	dynamicIndex: BOOLEAN = SELECT indexRange FROM
	  dynamic => Private.HasDynamicIndex[vectorInfo: arrayTypeInfo],
	  fixed => Private.HasEmptyIndex[indexType],
	  ENDCASE => ERROR;
	numElements: LONG INTEGER =
	  IF dynamicIndex THEN 1 ELSE Cardinality[indexType];
	FOR zone: AllocZone IN AllocZone DO
	  arrayInfo[zone] ← AllocDetails[
	    number:    SHORT[numElements*elementInfo[zone].number],
	    isDynamic: elementInfo[zone].isDynamic AND numElements > 0
	        OR dynamicIndex AND elementInfo[zone].number > 0 ];
	  ENDLOOP;
	END;  -- TraceArray.
      OneAlloc: ARRAY --isLongPointer:-- BOOLEAN OF AllocInfo = [
        TRUE: AllocInfo[heap: [number: 1]], FALSE: AllocInfo[mds: [number: 1]] ];
	typeInfo: ST.TypeInfo = ST.GetTypeInfo[type: type];
      WITH info: typeInfo SELECT FROM
        Null, Definition, Basic, Transfer,
	    Any, RelativePtr, Opaque, Zone, Other,
	    Text, StringBody, Rope, Atom => NULL;
	String =>
	  IF includeThisAlloc THEN allocInfo ← OneAlloc[info.long];
	Record =>
	  BEGIN
	  CheckComponent: ST.ComponentProcedure =
	    BEGIN
	    allocInfo ← AddAllocInfo[
	      allocInfo, TracePointers[componentType, ptrDepth].allocInfo ];
	    END;  -- CheckComponent.
	  [] ← ST.EnumerateRecord[recordType: type, proc: CheckComponent];
	  END;
        VariantPart =>
	  BEGIN
	  CheckVariant: ST.VariantProcedure =
	    BEGIN
	    variantInfo: AllocInfo ← TracePointers[variantRecordType, ptrDepth];
	    FOR zone: AllocZone IN AllocZone DO
	      allocInfo[zone] ← AllocDetails[
		number: MAX[allocInfo[zone].number, variantInfo[zone].number],
		isDynamic: allocInfo[zone].isDynamic OR variantInfo[zone].isDynamic ];
	      ENDLOOP;
	    END;  -- CheckVariant.
	  [] ← ST.EnumerateVariants[variantPartType: type, proc: CheckVariant];
	  END;
        Pointer =>
	  BEGIN
	  IF ptrDepth > MaxPointerDepth THEN RETURN;
	  IF includeThisAlloc
	    THEN BEGIN
	      allocInfo ← AddAllocInfo[
		OneAlloc[info.long],
		TracePointers[info.referentType, ptrDepth+1].allocInfo ];
	      END;
	  END;
        Ref =>
	  BEGIN
	  IF ptrDepth > MaxPointerDepth THEN RETURN;
	  IF includeThisAlloc
	    THEN allocInfo ← TracePointers[info.referentType, ptrDepth+1];
	  END;
        List =>
	  IF includeThisAlloc
	    THEN BEGIN 
	      bodyInfo: AllocInfo = TracePointers[info.firstType, ptrDepth];
	      FOR zone: AllocZone IN AllocZone DO
		allocInfo[zone] ← AllocDetails[
		  number: bodyInfo[zone].number,  -- Assume list has 1 element.
		  isDynamic: bodyInfo[zone].number > 0 ];  -- Catch multiple elements.
	        ENDLOOP;
	      END;
        Array =>
	  allocInfo ← TraceArray[info, info.indexType, info.elementType, fixed];
        Descriptor =>
 	  IF includeThisAlloc
	    THEN BEGIN
	      allocInfo ← AddAllocInfo[
		OneAlloc[info.long],
		TraceArray[info, info.indexType, info.elementType, dynamic] ];
	      END;
        Sequence =>
 	  allocInfo ← TraceArray[info, info.indexType, info.elementType, dynamic];
        ENDCASE => ERROR;
      END;  -- TracePointers.
    RETURN[ TracePointers[type: type, ptrDepth: 0] ];
    END;

  AddAllocInfo: PROCEDURE [a, b: AllocInfo] RETURNS [aPlusB: AllocInfo] =
    BEGIN
    FOR zone: AllocZone IN AllocZone DO
      aPlusB[zone] ← AllocDetails[
	number:    a[zone].number + b[zone].number,
	isDynamic: a[zone].isDynamic OR b[zone].isDynamic ];
      ENDLOOP;
    END;


-- Routines that determine whether marshaling is needed for various types.

  NeedsMarshaling: PUBLIC Private.NeedsOperationProc =
    BEGIN
    RecordNeedsMarshaling: PROC [record: ST.TypeHandle] RETURNS [BOOLEAN] =
	INLINE BEGIN
	CheckComponent: ST.ComponentProcedure =
	  {RETURN[ stop: NeedsMarshaling[componentType] ]};
	RETURN[ ST.EnumerateRecord[record, CheckComponent].stopped ];
	END;
    VariantNeedsMarshaling: PROC [variantPart: ST.TypeHandle] RETURNS [BOOLEAN] =
	INLINE BEGIN
	CheckVariant: ST.VariantProcedure =
	  {RETURN[ stop: NeedsMarshaling[variantRecordType] ]};
	RETURN[ ST.EnumerateVariants[variantPart, CheckVariant].stopped ];
	END;
    typeInfo: ST.TypeInfo = ST.GetTypeInfo[type: type];
    RETURN [
     WITH typeInfo: typeInfo SELECT FROM
      Null, Basic, RelativePtr, Opaque => FALSE,
      Text, StringBody => TRUE,  -- Dynamic length requires computing.
      Record => --WRONG: typeInfo.hasSequences OR-- RecordNeedsMarshaling[type],
      VariantPart => VariantNeedsMarshaling[type],
      Transfer, Pointer, Ref, List, String, Rope, Atom, Descriptor, Zone =>
        typeInfo.passingMethod # handle,  -- All these contain addresses.
      Array =>
	NeedsMarshaling[typeInfo.elementType] OR
	Private.HasEmptyIndex[index: typeInfo.indexType], -- For error reporting.
      Sequence =>
	NeedsMarshaling[typeInfo.elementType] OR
	typeInfo.kind = Computed,  -- For error reporting.
      Definition, Any, Other => TRUE,  -- So that an error will be issued.
      ENDCASE => ERROR ];
    END;

  ContainsRefs: PUBLIC Private.NeedsOperationProc =
    BEGIN
    RecordContainsRefs: PROC [record: ST.TypeHandle] RETURNS [BOOLEAN] =
	--INLINE-- BEGIN
	CheckComponent: ST.ComponentProcedure =
	  {RETURN[ stop: ContainsRefs[componentType] ]};
	RETURN[ ST.EnumerateRecord[record, CheckComponent].stopped ];
	END;
    VariantContainsRefs: PROC [variantPart: ST.TypeHandle] RETURNS [BOOLEAN] =
	--INLINE-- BEGIN
	CheckVariant: ST.VariantProcedure =
	  {RETURN[ stop: ContainsRefs[variantRecordType] ]};
	RETURN[ ST.EnumerateVariants[variantPart, CheckVariant].stopped ];
	END;
    typeInfo: ST.TypeInfo = ST.GetTypeInfo[type: type];
    RETURN [
     WITH typeInfo: typeInfo SELECT FROM
      Ref, List, Rope, Atom, Transfer => typeInfo.passingMethod # handle,
      Null, Definition, Basic,
	  Text, String, StringBody, Pointer, RelativePtr,
	  Any, Zone, Opaque, Other => FALSE,
      Record => RecordContainsRefs[type],
      VariantPart => VariantContainsRefs[type],
      Array => ContainsRefs[typeInfo.elementType],
      Descriptor => ContainsRefs[typeInfo.elementType],
      Sequence => ContainsRefs[typeInfo.elementType],
      ENDCASE => ERROR ];
    END;

  ContainsEmbeddedPtrs: PUBLIC Private.NeedsOperationProc =
    BEGIN
    ContainsPtrs: PROCEDURE [type: ST.TypeHandle, topLevel: BOOLEAN←FALSE]
	RETURNS [BOOLEAN] =
      BEGIN
      RecordContainsPtrs: PROC [record: ST.TypeHandle] RETURNS [BOOLEAN] =
	--INLINE-- BEGIN
	CheckComponent: ST.ComponentProcedure =
	  {RETURN[ stop: ContainsPtrs[componentType] ]};
	RETURN[ ST.EnumerateRecord[record, CheckComponent].stopped ];
	END;
      VariantContainsPtrs: PROC [variantPart: ST.TypeHandle] RETURNS [BOOLEAN] =
	--INLINE-- BEGIN
	CheckVariant: ST.VariantProcedure =
	  {RETURN[ stop: ContainsPtrs[variantRecordType] ]};
	RETURN[ ST.EnumerateVariants[variantPart, CheckVariant].stopped ];
	END;
      typeInfo: ST.TypeInfo = ST.GetTypeInfo[type: type];
      RETURN [
       WITH typeInfo: typeInfo SELECT FROM
        Null, Definition, Basic, StringBody, Text, RelativePtr,
	  Any, Opaque, Other => FALSE,
        Transfer, Pointer, Ref, List, String, Rope, Atom, Zone =>
	  ~topLevel AND typeInfo.passingMethod # handle,
        Record => RecordContainsPtrs[type],
        VariantPart => VariantContainsPtrs[type],
        Array => ContainsPtrs[typeInfo.elementType],
        Descriptor => typeInfo.passingMethod # handle
	  AND (~topLevel OR ContainsPtrs[typeInfo.elementType]),
        Sequence => ContainsPtrs[typeInfo.elementType],
        ENDCASE => ERROR ];
      END;  -- ContainsPtrs.
    RETURN[ ContainsPtrs[type: type, topLevel: TRUE] ];
    END;

  ContainsStatics: PUBLIC Private.NeedsOperationProc =
    BEGIN
    RecordContainsStatics: PROC [recordType: ST.TypeHandle] RETURNS [BOOLEAN] =
      --INLINE-- BEGIN
      CheckField: ST.ComponentProcedure =
	{RETURN[ stop: ContainsStatics[componentType] ]};
      RETURN[ ST.EnumerateRecord[recordType, CheckField].stopped ];
      END;  -- RecordContainsStatics.
    typeInfo: ST.TypeInfo = ST.GetTypeInfo[type];
    RETURN[
      WITH typeInfo: typeInfo SELECT FROM
	Null, Definition, Other => FALSE,
	Basic, RelativePtr, Zone, Opaque => TRUE,
	VariantPart => typeInfo.kind # Computed, -- So tag(s) get copied.
	Text, StringBody => FALSE,
	Transfer, String, Rope, Atom, Pointer, Ref, Any,
	    List, Descriptor, Zone =>
	  typeInfo.passingMethod = handle,
	Array => ContainsStatics[typeInfo.elementType],
	Sequence => ContainsStatics[typeInfo.elementType],
	Record => RecordContainsStatics[type],
	ENDCASE => ERROR ];      
    END;

  ContainsSequences: PUBLIC Private.NeedsOperationProc =
    BEGIN
    RecordContainsSequences: PROC [record: ST.TypeHandle] RETURNS [BOOLEAN] =
	--INLINE-- BEGIN
	CheckComponent: ST.ComponentProcedure =
	  {RETURN[ stop: ContainsSequences[componentType] ]};
	RETURN[ ST.EnumerateRecord[record, CheckComponent].stopped ];
	END;
    VariantContainsSequences: PROC [variantPart:ST.TypeHandle] RETURNS[BOOLEAN] =
	--INLINE-- BEGIN
	CheckVariant: ST.VariantProcedure =
	  {RETURN[ stop: ContainsSequences[variantRecordType] ]};
	RETURN[ ST.EnumerateVariants[variantPart, CheckVariant].stopped ];
	END;
    typeInfo: ST.TypeInfo = ST.GetTypeInfo[type: type];
    RETURN [
     WITH typeInfo: typeInfo SELECT FROM
      Sequence => TRUE,
      Null, Definition, Basic, Transfer,
	  Text, String, StringBody, Pointer, RelativePtr,
	  Ref, List, Rope, Atom,
	  Descriptor, Any, Zone, Opaque, Other => FALSE,
      Record => typeInfo.hasSequences,
      VariantPart => VariantContainsSequences[type],
      Array => ContainsSequences[typeInfo.elementType],
      ENDCASE => ERROR ];
    END;


-- Information Utility Routines.

  Cardinality: PUBLIC PROCEDURE [index: ST.TypeHandle] RETURNS [LONG INTEGER] =
    --INLINE-- BEGIN
    WITH indexInfo: ST.GetTypeInfo[type: index] SELECT FROM
      Basic => RETURN[indexInfo.cardinality];
      ENDCASE => ERROR;
    END;

  Size: PROCEDURE [type: ST.TypeHandle] RETURNS [--size:-- Words] =
    INLINE BEGIN RETURN[ST.Size[type: type]]; END;

  VectorSize: PUBLIC PROCEDURE [vectorInfo: ST.TypeInfo]
      RETURNS [--size:-- Words] =
    BEGIN
    RETURN[ WITH  info: vectorInfo SELECT FROM
      Array => ST.Size[info.self],  -- PACKED is handled automatically.
      Descriptor => ST.ComputeArraySize[
	index: info.indexType, elements: info.elementType, packed: info.packed],
      Sequence => ST.ComputeArraySize[
	index: info.indexType, elements: info.elementType, packed: info.packed],
      ENDCASE => ERROR ];
    END;

  IsShortString: PUBLIC PROCEDURE [candidate: ST.TypeHandle]
      RETURNS [--yes:-- BOOLEAN] =
    BEGIN
    shortStringTypes: ARRAY [0..3) OF ST.FullTypeName ← [
	[module: ModuleName[rpcPublic], name: "ShortSTRING"L],
	[module: ModuleName[rpcPublic], name: "ShortROPE"L],
	[module: ModuleName[rpcPublic], name: "ShortATOM"L] ];
    matchIndex: INTEGER = ST.SearchTypeDefinition[
      rootDef: candidate, candidateDefs: DESCRIPTOR[shortStringTypes] ];
    RETURN[ matchIndex IN [0..LENGTH[shortStringTypes]) ];
    END;

  IsConversation: PROCEDURE [candidate: ST.TypeHandle]
      RETURNS [--yes:-- BOOLEAN] =
    BEGIN
    conversationTypes: ARRAY [0..1) OF ST.FullTypeName ← [
	[module: ModuleName[rpcPublic], name: "Conversation"L] ];
    matchIndex: INTEGER = ST.SearchTypeDefinition[
      rootDef: candidate, candidateDefs: DESCRIPTOR[conversationTypes] ];
    RETURN[ matchIndex IN [0..LENGTH[conversationTypes]) ];
    END;

  IsExplicitHandle: PUBLIC PROC [typeInfo: ST.TypeInfo]
      RETURNS [--yes:-- BOOLEAN] =
    BEGIN
    -- IsExplicitHandle locates programer-declared HANDLETypes.
    -- It uses logic similar to DeterminePassingMethod,
    -- but cannot call it (the ideal) because the caller's context
    -- is awkwardly different.  Changing one <=> changing the other.
    SELECT typeInfo.type FROM
      Null, Definition, Basic, Record, VariantPart, RelativePtr,
	   Text, StringBody, Array, Sequence, Any, Opaque, Other =>
	RETURN[FALSE];
      String, Pointer, Descriptor, Transfer, Zone =>
	RETURN[typeInfo.passingMethod = handle];
      Ref, Rope, Atom, List =>
	IF typeInfo.passingMethod = handle
	  THEN BEGIN
	    Private.Warning[code: HandleREF, type: typeInfo.self];
	    RETURN[TRUE];
	    END
	  ELSE RETURN[FALSE];
      ENDCASE => ERROR;
    END;

  DeterminePassingMethod: PROCEDURE [
	typeInfo: ST.TypeInfo,
	paramKind: ParamRecordKind,
	options: Options,
	reportErrors: BOOLEAN←FALSE ]
      RETURNS[method: ParamPassingMethod] =
    BEGIN
    Error: PROCEDURE [
	  errorCode: ErrorCode←ImproperPassingMethod,
	  default: ParamPassingMethod ]
	RETURNS [--default:-- ParamPassingMethod] =
      BEGIN
      IF reportErrors THEN Private.Warning[code: errorCode, type: typeInfo.self];
      RETURN[default];
      END;
    GetAddressMethod: PROCEDURE [hasRef: BOOLEAN←FALSE]
	  RETURNS [method: ParamPassingMethod] =
	BEGIN
	method ←
	  (SELECT typeInfo.passingMethod FROM
	      var => Var, value => Value, result => Result, handle => Handle,
	      standard => SELECT paramKind FROM
		argument => options.defaultParamPassing,
		result => SELECT options.defaultParamPassing FROM
	            Var, Result => Value,
		    ENDCASE => options.defaultParamPassing,
		ENDCASE => ERROR,
	      ENDCASE => ERROR);
	IF hasRef AND method=Handle THEN method ← Error[HandleREF, Handle];
	SELECT method FROM
	  Var, Result =>
	    BEGIN
	    IF typeInfo.readonly THEN method ← Error[ImproperReadonlyRESULT, Value];
	    IF paramKind=result THEN method ← Error[ImproperRESULTResult, Value];
	    END;
	  ENDCASE => NULL;
	END;  -- GetAddressMethod.
    RETURN[
      IF options.defaultParamPassing = InterMds
	THEN InterMds
	ELSE SELECT typeInfo.type FROM
	  Null, Definition, Basic, Record, VariantPart, RelativePtr,
	      Text, StringBody, Array, Sequence, Any, Opaque, Other =>
	    SELECT typeInfo.passingMethod FROM
		standard, value => Value,
		ENDCASE => Error[default: Value],
	  String, Pointer, Descriptor =>
	    GetAddressMethod[],
	  Ref =>
	    GetAddressMethod[hasRef: TRUE],
	  Rope, Atom, List =>
	    SELECT GetAddressMethod[hasRef: TRUE] FROM
		Value => Value, Handle => Handle,
		ENDCASE => Error[default: Value],
	  Transfer =>
	    SELECT GetAddressMethod[hasRef: FALSE --For now.--] FROM
		Value => Value, Handle => Handle,
		ENDCASE => Error[default: Value],
	  Zone =>
	    SELECT GetAddressMethod[] FROM
		Handle => Handle,
		ENDCASE => Error[default: Handle],
	  ENDCASE => ERROR ];
    END;
	

END.  -- LupineMarshalInfoImpl.