LupineMarshalInfoImpl.mesa.
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by
BZM on 9-Mar-82 18:19:55.
Birrell, September 9, 1983 3:45 pm
Bob Hagmann February 8, 1985 5:04:22 pm PST
This module cooperates with LupineMarshal*Impl to export LupineMarshal.
DIRECTORY
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
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 ← 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
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: BOOLEANFALSE ] =
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
NULL;
END;
EnumerateParams: PUBLIC PROCEDURE [
paramInfo: ParamInfo,
paramProc: ParamProcedure,
includeRESULTs: BOOLEANFALSE ] =
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�lseAddressInfo] =
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: BOOLEANFALSE] ← [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: BOOLEANFALSE;
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𡤀] =
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: BOOLEANFALSE]
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"],
[module: ModuleName[rpcPublic], name: "ShortROPE"],
[module: ModuleName[rpcPublic], name: "ShortATOM"] ];
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"] ];
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: BOOLEANFALSE ]
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: BOOLEANFALSE]
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.