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