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