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; NeedsOperationProc: TYPE = Private.NeedsOperationProc; MaxDataSize: Words = Private.MaxDataSize; MaxPointerDepth: INTEGER = Private.MaxPointerDepth; 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) ] _ [ 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]; 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; minDataSize _ minDataSize + fieldInfo.minFlatSize; maxDataSize _ maxDataSize + fieldInfo.maxFlatSize; 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: BOOLEAN_FALSE ] = BEGIN 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; 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; 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; 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; 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], Pointer: [NilHeader], Ref: [NilHeader], String: [NilHeader+StringHeader], Text: [StringHeader], StringBody: [StringHeader], Rope: [NilHeader+RopeHeader], Atom: [NilHeader+RopeHeader], List: [SequenceHeader], Descriptor: [SequenceHeader], Sequence: [SequenceHeader], Zone: [] ]; 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; 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; 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; 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 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. ÚFile [Ivy]Lupine>LupineMarshalInfoImpl.mesa. Last edited by BZM on 9-Mar-82 18:19:55. This module cooperates with LupineMarshal*Impl to export LupineMarshal. Last Edited by: Birrell, September 9, 1983 3:45 pm Types and constants from the internal LupineManagerPrivate interface. The ParamInfo routines pull together all the information needed for parameter marshaling and efficient one-packet-call stub generation. Implicit VARs and RESULTs in result record. GetFieldInfo and UpdateParamInfo cooperatively maintain this variable. 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 END; This enumerator is in this module because it cooperates intimately with MakeParamInfo in the handling of VAR and RESULT parameters. Address Information Routines. IsDynamic, IsStatic: BOOLEAN = TRUE; Will cause an error during marshaling. Size Information Routines. 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. 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. Refs are the same as pointers. Strings are like Text, but the full CARDINAL range can be used. NIL is mapped correctly. Text is marshaled by preceding the characters with the NAT (a subset of CARDINAL) maxLength and length. StringBody is the same as string proper, but there's no pointer. A rope is marshaled by preceding the rope's characters with the rope's NAT (subset of CARDINAL) length. NIL is mapped correctly. An atom's PName is marshaled like a rope. A list is preceded by its LONG CARDINAL number of nodes. NIL is equivalent to zero nodes. 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. 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 pointers must always be handles. Explicit storage allocation information routines. Routines that determine whether marshaling is needed for various types. Information Utility Routines. 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. Ê#U˜Jšœ4™4Jšœ)™)J˜JšœG™GJ™2J˜šÏk ˜ šœœ˜J˜3Jšœ ˜—šœœ˜J˜0J˜"J˜+J˜3J˜:J˜,J˜—šœœ˜J˜J˜3J˜—šœœ˜J˜6J˜ J˜7J˜0J˜J˜——šœ˜š˜J˜-Jšœœ˜4—Jšœ$˜+Jšœ˜Jšœœœ%˜1J˜J˜JšœE™E˜Jšœœ˜6J˜)Jšœœ˜3J˜——J˜JšœC™CJšœC™C˜šÏn œœ œ˜!Jšœ œ ˜J˜!J˜J˜)Jšœœ˜J˜)J˜J˜Jšœ˜Jš˜š žœ œ œ œÏcœœ˜GJš˜šœ˜šœ˜šœC˜GJšœœ˜Jšœœ˜!Jšœœ˜———JšœŸ ˜—J˜+Jš˜šœ œ˜%Jš˜J˜šœ œ˜*Jšœ%˜)—Jšœ˜—Jšœœ=˜DJšœ˜šœ˜Jšœ œ!œœ˜:˜ šœ!˜%Jšœ,œœ˜7——Jšœœ˜—šœ œ˜˜JšœŸ&˜9JšœŸ˜&š œœœœœ˜DJšœ+™+—J˜J˜J˜!J˜J˜J˜)J˜J˜Jšœœœ˜.Jšœœ˜Jšœœœ˜ Jšœœ˜J˜J˜J˜J˜J˜J˜#Jšœœ˜——Jš˜˜4JšœF™F—J˜.šž œ œ˜Jšœ œ#˜/J˜#Jšœ˜ Jš˜J˜J˜+˜(J˜J˜;—J˜%J˜'J˜$šœœ˜2JšœC˜G—šœœœ˜OJšœ˜šœ˜ šœœ˜šœœ(˜šœ˜ J˜%˜#Jš˜šœ˜(˜Jš˜˜*JšœD˜F—J˜@J˜,Jšœ˜—Jšœœ˜—JšœŸ˜—˜J˜<—Jšœ7œœ˜DJšœŸ"˜(——˜J˜.J˜ šœœ˜0Jšœ œœ˜6——˜Jšœœ˜>—J˜9Jšœ˜JšœŸ œ ˜Jšœ˜J˜J˜—šž œœ œ˜8Jš˜Jšœ˜Jšœ˜J˜—šžœœ œ˜#J˜J˜Jšœœœ˜!Jš˜JšœB™BJšœ@™@J˜3šœ˜šœ˜ šœ œ˜ Jš˜šœ˜˜J˜J˜J˜J˜9——JšœŸ ˜—JšœœC˜JJšœ˜——šœ˜Jšœ!œ˜?šœ˜ J˜/˜Jš˜šœ˜(šœœ˜˜J˜J˜J˜˜J˜6———Jšœœ˜—JšœŸ ˜—J˜%Jšœ˜——Jšœ˜J˜——J˜Jšœ™˜šžœ œ˜Jšœ œ ˜J˜"J˜Jšœ)˜0Jš˜šœœœ˜*Jš œœœœœ˜H—šœ œ˜Jšœœ!˜;—Jšœ$™$šžœœœœ˜?Jš˜Jšœœ œ˜/šž œœ˜(Jšœœ˜(Jš˜šœ˜šœ˜ Jšœœ˜šœ˜šœ œ ˜Jšœ˜Jšœœ˜—Jšœœ˜Jšœœ˜—Jš˜—Jšœ ˜—Jšœœ œœ˜@Jšœœœ˜CJšœŸ˜—Jšž œœœœ˜6šœ œ˜˜#J˜ —˜ Jšœ œœœ ˜?—˜ JšœŸ#˜*šœ œ˜#J˜*—J˜ Jšœœ5˜˜EJšœ˜—˜Jšœœ˜—˜ šœ˜Jšœ*œ˜B——˜ šœ˜Jšœ*œ˜B——˜ Jš˜Jšœœœ˜*Jšœœ/˜KJšœ˜—˜Jš˜Jšœœœ˜*Jšœœ/˜GJšœ˜—˜šœ˜Jšœ˜ Jšœœ˜J˜)Jšœ˜——˜Jš˜šœ-˜/JšœœŸ˜—˜ J˜B—šœ.œ˜3Jšœ&™&—Jšœœ˜—šœœ˜Jšœœœ˜?Jšœœœ˜F—Jšœœœœ˜7Jšœ˜J˜——J˜Jšœ™˜Jšœ œ˜ J˜šœ œœ˜J˜$Jšœœœœ˜.J˜—J˜Jšœœœ˜'Jšœœœœ˜FJšœœœ˜)Jšœœœœ˜2J˜š œœœœœ˜;˜Jšœ=™=Jšœ?™?JšœB™B—˜Jšœ;™;Jšœ@™@Jšœ$™$—˜Jšœ™—˜!Jšœ?™?Jšœ™—˜Jšœ6™6Jšœ0™0—˜Jšœ@™@—˜Jšœ;™;Jšœ+™+Jšœ™—˜Jšœ)™)—˜Jšœ8™8Jšœ ™ —˜Jšœ8™8Jšœ7™7Jšœ™Jšœ#™#—˜Jšœ<™—šœ˜šœ œ˜Jšœœ'˜2—J˜1—JšœŸ˜—Jšœ œ œ˜3šœœ˜+Jšœœœœ ˜Dšœ˜J˜<——J˜šœœ˜˜"Jšœ)œ˜.—Jšœœ˜#˜šœœ˜Jšœœ˜Jšœ"œ˜'šœ˜ JšœŸ%˜,šœ œ˜Jšœœ#œ œ˜>—J˜ Jšœ˜———˜ Jš˜šœœ˜'Jš˜J˜J˜?J˜6JšœŸ˜—Jšœœ9˜@Jšœ˜—˜Jš˜šœœ˜#Jš˜J˜J˜CJšœ œœ˜>JšœŸ˜—Jšœœ>˜EJšœ˜—˜ Jš˜Jšœœœ˜*šœœ˜J˜CJšœŸ"˜3—Jšœ˜—˜Jš˜Jšœœœ˜*šœœ˜J˜CJšœŸ˜/—Jšœ˜—Jšœœœ œ˜0˜˜"J˜J˜9J˜,——˜ šœœ˜Jšœœ˜šœ˜ ˜"J˜J˜9J˜)————˜ ˜"J˜J˜9J˜)——Jšœœ˜—Jšœ'˜-JšœŸ ˜—˜'J˜+—Jš œœ œœœ˜DJšœ˜J˜——J˜Jšœ1™1˜šž œ œ˜Jšœœ ˜J˜"J˜Jšœ˜ Jš˜šœœœ˜5J˜"Jšœ œ˜Jšœœ˜Jšœœ˜—šž œ œ˜Jšœœ ˜Jšœ œ˜Jšœ'˜.Jš˜šž œ œ˜Jšœœ ˜Jšœœ ˜&J˜Jšœ˜ Jš˜J˜>šœœœ ˜.J˜>J˜*Jšœœ˜—šœ œœ˜Jšœœœ˜3—šœœ ˜#˜Jšœ œ'˜7šœ'œ˜:Jšœœ ˜3——Jšœ˜—JšœŸ˜—š œ œŸœœœ˜;Jšœ œ ˜IJšœ œ œ˜3—šœœ˜˜"J˜&Jšœ œ˜%—˜ Jšœœ!˜9—˜ Jš˜šœœ˜'Jš˜˜J˜>—JšœŸ˜—Jšœœ9˜@Jšœ˜—˜Jš˜šœœ˜#Jš˜J˜Dšœœ ˜#˜Jšœœ3˜>Jšœ%œ˜F—Jšœ˜—JšœŸ˜—Jšœœ>˜EJšœ˜—˜ Jš˜Jšœœœ˜*šœ˜šœ˜ ˜J˜J˜9—Jšœ˜——Jšœ˜—˜Jš˜Jšœœœ˜*šœ˜Jšœ:˜>—Jšœ˜—˜šœ˜šœœ˜ J˜>šœœ ˜#˜Jšœ Ÿ˜=Jšœ)Ÿ˜D—Jšœ˜—Jšœ˜———˜J˜F—˜ šœ˜šœ˜ ˜J˜J˜>—Jšœ˜———˜ J˜H—Jšœœ˜—JšœŸ˜—Jšœ+˜1Jšœ˜J˜—šž œ œœ˜GJš˜šœœ ˜#˜J˜+Jšœœ˜4—Jšœ˜—Jšœ˜J˜——J˜JšœG™G˜šœœ˜4Jš˜š žœœ œ œœ˜GJšœ˜ šœœ˜'Jšœœ*˜1—Jšœœ3˜=Jšœ˜—š žœœœ œœ˜MJšœ˜ šœœ˜#Jšœœ.˜5—Jšœœ8˜BJšœ˜—Jšœ œ œ˜3šœ˜šœœ˜#Jšœ$œ˜*JšœœŸ%˜@Jšœ Ÿ#œ˜JJ˜,˜EJšœ"Ÿ˜A—˜Jšœ&˜(Jšœ2Ÿ˜I—˜ Jšœ&˜(JšœŸ˜2—JšœœŸ#˜DJšœœ˜——Jšœ˜J˜—šœœ˜1Jš˜š žœœ œ œœ˜DJšŸ œ˜šœœ˜'Jšœœ'˜.—Jšœœ3˜=Jšœ˜—š žœœœ œœ˜JJšŸ œ˜šœœ˜#Jšœœ+˜2—Jšœœ8˜BJšœ˜—Jšœ œ œ˜3šœ˜šœœ˜#J˜C˜J˜/Jšœœ˜"—J˜#J˜)J˜,J˜1J˜/Jšœœ˜——Jšœ˜J˜—šœœ˜9Jš˜š ž œ œœœœ˜FJšœœ˜Jš˜š žœœ œ œœ˜DJšŸ œ˜šœœ˜'Jšœœ'˜.—Jšœœ3˜=Jšœ˜—š žœœœ œœ˜JJšŸ œ˜šœœ˜#Jšœœ+˜2—Jšœœ8˜BJšœ˜—Jšœ œ œ˜3šœ˜šœœ˜#˜7Jšœœ˜—˜9Jšœ œ!˜.—J˜#J˜)J˜,˜-Jšœ œ%˜6—J˜/Jšœœ˜——JšœŸ˜—Jšœ%œ˜3Jšœ˜J˜—šœœ˜4Jš˜š žœœœ œœ˜KJšŸ œ˜šœ œ˜#Jšœœ*˜1—Jšœœ3˜=JšœŸ˜—Jšœ œ œ˜-šœ˜šœœ˜#Jšœœ˜!Jšœ$œ˜)Jšœ)Ÿ˜AJšœœ˜˜0J˜J˜ —J˜/J˜2J˜&Jšœœ ˜——Jšœ˜J˜—šœœ˜6Jš˜š žœœ œ œœ˜IJšŸ œ˜šœœ˜'Jšœœ,˜3—Jšœœ3˜=Jšœ˜—š žœœœ œœ˜MJšŸ œ˜šœœ˜#Jšœœ0˜7—Jšœœ8˜BJšœ˜—Jšœ œ œ˜3šœ˜šœœ˜#Jšœ œ˜˜"J˜/J˜Jšœ(œ˜.—J˜ J˜.J˜1Jšœœ˜——Jšœ˜J˜——J˜Jšœ™˜šž œœ œ œ œœœ˜MJšŸ œ˜šœ œœ˜7Jšœ œ˜'Jšœœ˜—Jšœ˜J˜—š žœ œœ œŸ œ ˜AJš œœœœœ˜.J˜—šž œœ œœ ˜6JšœŸ œ ˜Jš˜šœœœ˜*Jšœ œŸ#˜Ašœœ˜"J˜H—šœ œ˜ J˜H—Jšœœ˜—Jšœ˜J˜—šž œœ œ œ ˜:JšœŸœœ˜Jš˜šœœœœ˜5J˜5J˜3J˜5—šœ œœ˜.Jšœ# œ˜B—Jšœ œœ˜6Jšœ˜J˜—šžœ œ œ ˜4JšœŸœœ˜Jš˜šœœœœ˜6J˜8—šœ œœ˜.Jšœ# œ˜C—Jšœ œœ˜7Jšœ˜J˜—šžœœœ œ ˜5JšœŸœœ˜Jš˜Jšœ8™8Jšœ0™0Jšœ;™;Jšœ=™=šœ˜˜:J˜8Jšœœ˜—˜.Jšœ"˜(—˜šœ ˜"šœ˜ J˜6Jšœœ˜ Jš˜—Jšœœœ˜——Jšœœ˜—Jšœ˜J˜—šžœ œ˜#Jšœ œ ˜J˜J˜Jšœœœ˜Jšœ˜%Jš˜šžœ œ˜J˜+J˜JšœŸ œ˜+Jš˜Jšœœ7˜KJšœ ˜Jšœ˜—šžœ œ œœ˜3Jšœ˜&Jš˜˜šœœ˜#J˜?šœ œ ˜!J˜(šœ œ˜1J˜Jšœ ˜'—Jšœœ˜—Jšœœ˜——Jšœœœ#˜Cšœ˜˜Jš˜Jšœœ/˜HJšœœ-˜EJšœ˜—Jšœœ˜—JšœŸ˜—šœ˜šœ'˜)Jšœ ˜ šœœ˜˜:J˜8šœ˜"J˜Jšœ˜!——˜J˜—˜Jšœœ˜—˜šœœ˜*J˜!Jšœ˜!——˜ šœœŸ œ˜8J˜!Jšœ˜!——˜šœ˜J˜Jšœ˜"——Jšœœ˜———Jšœ˜J˜J˜——JšœŸ˜J˜—…—yb§‘