-- File [Ivy]<Nelson>Lupine>LupineMarshalUtilityImpl.mesa. -- Last edited by BZM on 11-May-82 16:15:50. -- This module cooperates with LupineMarshal*Impl to export LupineMarshal. DIRECTORY CWF USING [WF1, SWF1], LupineDeclare USING [WriteTypeName], LupineManagerPrivate USING [ AllocString, ErrorCode, ErrorType, ExplanationProc, GiveExplanation, Indent, IsNull, MaxIdentifierLength, Nest, Options, ReportError, String, StringNIL, WFS, WFS1, WFSL, WFL, WFL1, WFLL ], LupineMarshal USING [ AllocDetails, AllocInfo, AllocZone, EnumerateParams, FieldInfo, ParamIndex, ParamInfo, ParamLocation, ParamProcedure, PktSite, VariableNames, Words ], LupineMarshalPrivate USING [ AllocationOperation, Direction, MarshalInfo, MaxDataLength, ParentInfo, SubStrings ], LupineSymbolTable USING [ PutTypeName, Size, SymbolHandle, SymbolHandleNIL, SymbolName, TypeHandle, TypeHandleNIL, TypeInfo, Types ]; LupineMarshalUtilityImpl: PROGRAM IMPORTS CWF, Declare: LupineDeclare, LupineManagerPrivate, Marshal: LupineMarshal, ST: LupineSymbolTable EXPORTS LupineMarshal, LupineMarshalPrivate = BEGIN OPEN LupineManagerPrivate, LupineMarshal, Private: LupineMarshalPrivate; -- Types from the internal LupineManagerPrivate interface. ParentInfo: TYPE = Private.ParentInfo; MarshalInfo: TYPE = Private.MarshalInfo; SubStrings: TYPE = Private.SubStrings; -- Routines for handling parameter storage allocation and deallocation. ZoneString: TYPE = STRING ← NIL; ZoneStrings: ARRAY {ptrType, vector, counter, maxCount, zone} OF PACKED ARRAY AllocZone OF ZoneString = [ --prefix: ["gc", "heap", "mds"], ptrType: ["REF", "LONG POINTER", "POINTER"], vector: [heap: "heapAllocVector", mds: "mdsAllocVector"], counter: [heap: "heapAllocs", mds: "mdsAllocs"], maxCount:[heap: "MaxHeapAllocs", mds: "MaxMdsAllocs"], zone: ["paramZones.gc", "paramZones.heap", "paramZones.mds"] ]; BeginAllocation: PUBLIC PROCEDURE [ paramInfo: ParamInfo, nest: Nest ] RETURNS [newNest: Nest] = BEGIN OPEN p: paramInfo; newNest ← nest; IF p.allocInfo[heap].number > 0 OR p.allocInfo[mds].number > 0 THEN BEGIN DeclareArray[heap, p.allocInfo[heap], paramInfo.options, nest]; DeclareArray[mds, p.allocInfo[mds], paramInfo.options, nest]; WFL1[nest, "BEGIN ENABLE UNWIND => BEGIN -- Free storage."L]; newNest ← nest + 1; Deallocate[heap, p.allocInfo[heap], newNest+1]; Deallocate[mds, p.allocInfo[mds], newNest+1]; WFL1[newNest+1, "END; -- Free storage."L]; END; END; EndAllocation: PUBLIC PROCEDURE [ paramInfo: ParamInfo, justCloseBlocks: BOOLEAN←FALSE, nest: Nest ] RETURNS [newNest: Nest] = BEGIN OPEN p: paramInfo; newNest ← nest; IF p.allocInfo[heap].number > 0 OR p.allocInfo[mds].number > 0 THEN BEGIN WFL1[nest, "END; -- ENABLE UNWIND => Free storage."L]; newNest ← nest - 1; SELECT TRUE FROM justCloseBlocks => NULL; -- ALWAYS do nothing. ~paramInfo.options.freeServerArguments => NULL; -- Asked to do nothing. ENDCASE => BEGIN Deallocate[heap, p.allocInfo[heap], newNest]; Deallocate[mds, p.allocInfo[mds], newNest]; END; END; END; WriteNEW: PUBLIC PROCEDURE [ allocOp: Private.AllocationOperation ← new, ptrInfo: ST.TypeInfo, marshalInfo: --VAR-- MarshalInfo] = BEGIN -- For gc storage, write "zone.NEW". -- For heap and MDS storage, write "allocVector[index] ← zone.NEW". -- Index is either a number (eg, 3) or a conditional expression that -- checks overflow (eg, IF (index←index+1) < Max THEN index ELSE ERROR). zone: AllocZone; AllocOpCode: PACKED ARRAY Private.AllocationOperation OF STRING = [ new: "NEW"L, cons: "CONS"L, list: "LIST"L ]; WITH ptrInfo: ptrInfo SELECT FROM Ref, Rope, Atom, List => zone ← gc; Pointer, String, Descriptor => BEGIN maxAllocInfo: AllocInfo = marshalInfo.paramInfo.allocInfo; zone ← IF ptrInfo.long THEN heap ELSE mds; IF marshalInfo.paramInfo.transferSite=callee THEN BEGIN WFS[ZoneStrings[vector][zone], "["L]; IF maxAllocInfo[zone].isDynamic THEN WFSL[ "(IF ("L, ZoneStrings[counter][zone], " ← "L, ZoneStrings[counter][zone], "+1) <= "L, ZoneStrings[maxCount][zone], " THEN "L, ZoneStrings[counter][zone], " ELSE Lupine.UnmarshalingExprError[])"L ] ELSE BEGIN allocVectorIndex: LONG INTEGER ← IF marshalInfo.numAllocs[zone] < maxAllocInfo[zone].number THEN (marshalInfo.numAllocs[zone] ← marshalInfo.numAllocs[zone] + 1) ELSE ERROR; CWF.WF1["%LD"L, @allocVectorIndex]; END; WFS1["] ← "L]; END; -- IF marshalInfo.paramInfo.transferSite=callee. END; -- Pointer, String, Descriptor. ENDCASE => ERROR; WFS[ZoneStrings[zone][zone], "."L, AllocOpCode[allocOp]]; END; DeclareArray: PROCEDURE [ zone: AllocZone, allocDetails: AllocDetails, options: Options, nest: Nest ] = BEGIN totalAllocs: LONG INTEGER ← allocDetails.number + (IF allocDetails.isDynamic THEN SELECT zone FROM heap => options.maxHeapAllocations, mds => options.maxMdsAllocations, ENDCASE => ERROR ELSE 0); IF totalAllocs <= 0 THEN RETURN; WFS[Indent[nest], ZoneStrings[maxCount][zone], ": CARDINAL = "L]; CWF.WF1["%LD;*N"L, @totalAllocs]; IF allocDetails.isDynamic THEN WFL[nest, ZoneStrings[counter][zone], ": CARDINAL ← 0;"L]; WFSL[Indent[nest], ZoneStrings[vector][zone], ": ARRAY [1.."L, ZoneStrings[maxCount][zone], "] OF "L, ZoneStrings[ptrType][zone], "←ALL[NIL];*N"L]; END; -- DeclareArray. Deallocate: PROCEDURE [ zone: AllocZone, allocDetails: AllocDetails, nest: Nest] = BEGIN IF allocDetails.number <= 0 THEN RETURN; WFSL[Indent[nest], "FOR ptr: CARDINAL IN [1..LENGTH["L, ZoneStrings[vector][zone], "]] DO*N"L, Indent[nest+1], "IF "L, ZoneStrings[vector][zone], "[ptr] = NIL*N"L, Indent[nest+2], "THEN EXIT*N"L, Indent[nest+2], "ELSE "L, ZoneStrings[zone][zone], ".FREE[@"L, ZoneStrings[vector][zone], "[ptr]];*N"L, Indent[nest+1], "ENDLOOP;*N"L]; END; -- Deallocate. -- Code generation for runtime copying routines. CopyOne: PUBLIC PROCEDURE [ wordsNeeded: Words, value: SubStrings, nullValue, oneStmt: BOOLEAN ← FALSE, marshalInfo: MarshalInfo, nest: Nest ] = BEGIN OPEN varNames: marshalInfo.varNames; IF oneStmt THEN WFL1[nest, "BEGIN"L]; InsureContiguousWords[wordsNeeded, marshalInfo, nest]; IF nullValue THEN WFL1[nest, "-- Ignore value and skip ahead in pkt."L] ELSE SELECT marshalInfo.direction FROM toPkt => WFSL[ Indent[nest], varNames.pkt, ".data["L, varNames.pktLength, "] ← "L, value.s1, value.s2, value.s3, "; "L ]; fromPkt => WFSL[ Indent[nest], value.s1, value.s2, value.s3, " ← "L, varNames.pkt, ".data["L, varNames.pktLength, "]; "L ]; ENDCASE => ERROR; WFSL[varNames.pktLength, " ← "L, varNames.pktLength, "+1;*N"L]; IF oneStmt THEN WFL1[nest, "END;"L]; END; CopyTwo: PUBLIC PROCEDURE [ wordsNeeded: Words, value: SubStrings, nullValue, oneStmt: BOOLEAN ← FALSE, marshalInfo: MarshalInfo, nest: Nest ] = BEGIN OPEN varNames: marshalInfo.varNames; DoubleWordRoutine: PACKED ARRAY PktSite OF STRING = [ stubFrame: "StubPktDoubleWord"L, rpcRuntime: "RpcPktDoubleWord"L ]; IF oneStmt THEN WFL1[nest, "BEGIN"L]; InsureContiguousWords[wordsNeeded, marshalInfo, nest]; IF nullValue THEN WFL1[nest, "-- Ignore value and skip ahead in pkt."L] ELSE SELECT marshalInfo.direction FROM toPkt => WFSL[ Indent[nest], "Lupine."L, DoubleWordRoutine[marshalInfo.paramInfo.pktSite], "["L, varNames.pkt, ", "L, varNames.pktLength, "]↑ ← "L, value.s1, value.s2, value.s3, ";*N"L ]; fromPkt => WFSL[ Indent[nest], value.s1, value.s2, value.s3, " ← Lupine."L, DoubleWordRoutine[marshalInfo.paramInfo.pktSite], "["L, varNames.pkt, ", "L, varNames.pktLength, "]↑;*N"L ]; ENDCASE => ERROR; WFLL[nest, varNames.pktLength, " ← "L, varNames.pktLength, " + 2;"L]; IF oneStmt THEN WFL1[nest, "END;"L]; END; InsureContiguousWords: PROCEDURE [ wordsNeeded: Words, marshalInfo: MarshalInfo, nest: Nest ] = BEGIN OPEN varNames: marshalInfo.varNames; SELECT TRUE FROM wordsNeeded > Private.MaxDataLength => ERROR; wordsNeeded=0 OR marshalInfo.paramInfo.alwaysOnePkt => NULL; ENDCASE => BEGIN words: AllocString = [10]; CWF.SWF1[words, "%LD"L, @wordsNeeded]; WFLL[nest, "IF "L, varNames.pktLength, "+"L, words, " > RpcPrivate.maxDataLength"L]; WFSL[Indent[nest+1], "THEN "L, varNames.pktLength, " ← Lupine."L, (SELECT marshalInfo.direction FROM toPkt => "StartNextPkt"L, fromPkt => "FinishThisPkt"L, ENDCASE => ERROR), "[pkt: "L, varNames.pkt, ", pktLength: "L, varNames.pktLength, "];*N"L ]; END; END; CopyCharacters: PUBLIC PROCEDURE [ textName: SubStrings, numCharsName: String, marshalInfo: MarshalInfo, nest: Nest ] = BEGIN OPEN varNames: marshalInfo.varNames; WFSL[Indent[nest], varNames.pktLength, " ← Lupine."L, Routine[marshalInfo.direction], "[pkt: "L, varNames.pkt, ", pktLength: "L, varNames.pktLength, ", dataAdr: "L, textName.s1, textName.s2, textName.s3, ", dataLength: Lupine.WordsForChars["L, numCharsName, "], alwaysOnePkt: "L, Truth[marshalInfo.paramInfo.alwaysOnePkt], "];*N"L]; END; CopyUninterpreted: PUBLIC PROCEDURE [ variableName: String, variableInfo: ST.TypeInfo, parentInfo: ParentInfo, marshalInfo: MarshalInfo, nest: Nest ] = BEGIN -- CopyUninterpreted must generate exactly one statement. WITH variableInfo: variableInfo SELECT FROM Null, Basic, Record, Array, RelativePtr, Opaque => CopyType[variableName, variableInfo, parentInfo, marshalInfo, nest]; Transfer, Pointer, Ref, List, String, Rope, Atom, Descriptor, Zone => -- Treat these as handles by copying just the pointer. Copy[ nest: nest, name: variableName, typeInfo: variableInfo, interpret: FALSE, parentInfo: parentInfo, marshalInfo: marshalInfo ]; VariantPart, Sequence, Text, StringBody => ERROR; -- These should always be embedded in something else. Definition, Any, Other => ERROR; -- These are impossible. ENDCASE => ERROR; END; CopyType: PUBLIC PROCEDURE [ variableName: String, variableInfo: ST.TypeInfo, parentInfo: ParentInfo, marshalInfo: MarshalInfo, nest: Nest ] = BEGIN -- CopyType must generate exactly one statement. WITH variableInfo: variableInfo SELECT FROM Null => WFL1[nest, "NULL;"L]; Basic, RelativePtr, Opaque, Array => Copy[ nest: nest, name: variableName, typeInfo: variableInfo, interpret: FALSE, parentInfo: parentInfo, marshalInfo: marshalInfo ]; Record => Copy[ nest: nest, name: variableName, typeInfo: variableInfo, interpret: variableInfo.hasSequences, parentInfo: parentInfo, marshalInfo: marshalInfo ]; Descriptor, Sequence => Copy[ nest: nest, name: variableName, typeInfo: variableInfo, interpret: TRUE, parentInfo: parentInfo, marshalInfo: marshalInfo ]; Transfer, Pointer, Ref, List, String, Rope, Atom, Descriptor, Zone, VariantPart, Sequence, Text, StringBody => ERROR; -- Previous special treatment required. Definition, Any, Other => ERROR; -- These are impossible. ENDCASE => ERROR; END; Copy: PRIVATE PROCEDURE [ name: String, typeInfo: ST.TypeInfo, interpret: BOOLEAN, parentInfo: ParentInfo, marshalInfo: MarshalInfo, nest: Nest ] = BEGIN IF interpret THEN WriteCopy[name, typeInfo, TRUE, parentInfo, marshalInfo, nest] ELSE SELECT ST.Size[typeInfo.self] FROM 0 => ERROR; 1 => CopyOne[ nest: nest, wordsNeeded: 1, value: [name], oneStmt: TRUE, marshalInfo: marshalInfo ]; 2 => CopyTwo[ nest: nest, wordsNeeded: 2, value: [name], oneStmt: TRUE, marshalInfo: marshalInfo ]; ENDCASE => WriteCopy[name, typeInfo, FALSE, parentInfo, marshalInfo, nest]; END; Routine: PACKED ARRAY Private.Direction OF STRING = [ toPkt: "CopyToPkt", fromPkt: "CopyFromPkt"]; Truth: PACKED ARRAY BOOLEAN OF STRING = [TRUE: "TRUE", FALSE: "FALSE"]; WriteCopy: PROCEDURE [ name: String, typeInfo: ST.TypeInfo, interpret: BOOLEAN, parentInfo: ParentInfo, marshalInfo: MarshalInfo, nest: Nest ] = BEGIN OPEN varNames: marshalInfo.varNames; WFSL[Indent[nest], varNames.pktLength, " ← Lupine."L, Routine[marshalInfo.direction], "[pkt: "L, varNames.pkt, ", pktLength: "L, varNames.pktLength, ", dataAdr: "L]; WriteAddress[name, typeInfo.type, interpret, parentInfo]; WFS1[ ", dataLength: "L]; WriteSize[name, typeInfo, interpret]; WFS[ ", alwaysOnePkt: "L, Truth[marshalInfo.paramInfo.alwaysOnePkt], "];*N"L]; END; WriteAddress: PROCEDURE [ name: String, type: ST.Types, interpret: BOOLEAN, parentInfo: ParentInfo] = BEGIN SELECT type FROM Array, Descriptor => IF interpret THEN WFS["BASE["L, name, "]"L] ELSE WFS["@"L, name]; Sequence => IF interpret THEN WFS["BASE[DESCRIPTOR["L, name, "]]"L] ELSE WFS["@"L, name]; ENDCASE => -- Check for @name↑, which can have type REF and not LONG POINTER. IF name[name.length-1] = '↑ THEN SELECT parentInfo.typeInfo.type FROM Ref => WFS["LOOPHOLE["L, parentInfo.name, "]"L]; Pointer => WFS1[parentInfo.name]; ENDCASE => ERROR ELSE WFS["@"L, name]; END; WriteSize: PROCEDURE [ name: String, typeInfo: ST.TypeInfo, interpret: BOOLEAN ] = BEGIN DynamicArraySize: PROC [ arrayName: SubStrings, elementType: ST.TypeHandle, packed: BOOLEAN] = BEGIN WFS1["SIZE["L]; Declare.WriteTypeName[elementType]; IF packed THEN WFSL[", LENGTH["L, arrayName.s1, arrayName.s2, arrayName.s3, "]]"L] ELSE WFSL["]**LENGTH["L, arrayName.s1, arrayName.s2, arrayName.s3, "]"L]; END; -- DynamicArraySize. IF interpret THEN WITH typeInfo: typeInfo SELECT FROM Record => BEGIN -- Sequences can complicate record lengths. But variants don't -- (yet) because everything is done in terms of unbound variants. WFS1["SIZE["L]; Declare.WriteTypeName[typeInfo.self]; IF typeInfo.hasSequences THEN WFS["[LENGTH[DESCRIPTOR["L, name, "]]]"L]; WFS1["]"L]; END; Descriptor => -- Variable length and packing make the size tricky. DynamicArraySize[ [name], typeInfo.elementType, typeInfo.packed]; Sequence => -- Ditto. DynamicArraySize[ SubStrings["DESCRIPTOR"L, name, "]"L], typeInfo.elementType, typeInfo.packed]; ENDCASE => {WFS1["SIZE["L]; Declare.WriteTypeName[typeInfo.self]; WFS1["]"L]} ELSE {WFS1["SIZE["L]; Declare.WriteTypeName[typeInfo.self]; WFS1["]"L]}; END; -- Marshaling Error Routines. Error: PUBLIC PROCEDURE [ code: ErrorCode, symbol: ST.SymbolHandle, type: ST.TypeHandle, string: String, causeError: BOOLEAN ] = BEGIN ReportMarshalError [ errorType: error, errorCode: code, symbol: symbol, type: type, string: string, causeError: causeError ]; END; Warning: PUBLIC PROCEDURE [ code: ErrorCode, symbol: ST.SymbolHandle, type: ST.TypeHandle, string: String, causeError: BOOLEAN ] = BEGIN ReportMarshalError [ errorType: warning, errorCode: code, symbol: symbol, type: type, string: string, causeError: causeError ]; END; ReportMarshalError: PROCEDURE [ errorType: ErrorType, errorCode: ErrorCode, symbol: ST.SymbolHandle, type: ST.TypeHandle, string: String, causeError: BOOLEAN ] = BEGIN problemString: AllocString = [MaxIdentifierLength]; problemText: String ← NIL; SELECT TRUE FROM symbol # ST.SymbolHandleNIL => problemText ← ST.SymbolName[symbol, problemString]; type # ST.TypeHandleNIL => BEGIN FillProblem: PROC [chr: CHARACTER] = BEGIN IF problemString.length >= problemString.maxlength THEN RETURN; problemString[problemString.length] ← chr; problemString.length ← problemString.length + 1; END; ST.PutTypeName[type: type, putProc: FillProblem]; IF problemString.length > problemString.maxlength-3 THEN BEGIN problemString[problemString.maxlength-3] ← problemString[problemString.maxlength-2] ← problemString[problemString.maxlength-1] ← '.; problemString.length ← problemString.maxlength; END; problemText ← problemString; END; ENDCASE => problemText ← string; ReportError[type: errorType, code: errorCode, problemText: problemText]; BEGIN CodeFileMessage: ExplanationProc = BEGIN WFSL[ "-- ##### "L, (SELECT errorType FROM error => "Error: "L, warning => "Warning: "L, abort => "Abort: "L, ENDCASE => ERROR), explanation, "*BIdentifier or type = "L, (IF IsNull[problemText] THEN "<Not Available>"L ELSE problemText), ". --"L, (IF causeError THEN " Lupine.TranslationError;"L ELSE ""L), "*N"L ]; END; -- CodeFileMsg. GiveExplanation[code: errorCode, explainer: CodeFileMessage]; END; END; -- Utility enumerators. EnumerateSomeParams: PUBLIC PROCEDURE [ paramInfo: ParamInfo, proc: ParamProcedure, place1, place2: ParamLocation ] = BEGIN DoParam: ParamProcedure = BEGIN SELECT paramFieldInfo.location FROM place1, place2 => RETURN[stop: proc[paramName, paramType, paramIndex, paramFieldInfo] ]; ENDCASE => NULL; END; Marshal.EnumerateParams[ paramInfo: paramInfo, paramProc: DoParam, includeRESULTs: TRUE ]; END; END. -- LupineMarshalUtilityImpl. TransferParamProcedure: TYPE = PROCEDURE [ transferName: String, -- Is unique within the top-level transfer. transferSymbol: ST.SymbolHandle, transferType: ST.TypeHandle, transferIndex: ParamIndex, transferArgInfo, transferResultInfo: ParamInfo ] RETURNS [stop: BOOLEAN←FALSE]; EnumerateTransferParams: PROCEDURE [ paramRecord: ST.TypeHandle, transferProc: TransferParamProcedure, all, procs, signals, errors: BOOLEAN←FALSE ] = BEGIN index: ST.Index ← 0; TraceTransfers: PROCEDURE [ thisType: ST.TypeHandle, prefixName: String, ptrDepth: INTEGER←0 ] = BEGIN info: ST.TypeInfo = ST.GetTypeInfo[thisType]; WITH info: info SELECT FROM Definition, Basic, Text, String, StringBody, Rope, Atom, Any, RelativePtr, Zone, Opaque, Null, Other => NULL; List, Array, Descriptor, Sequence => Private.Error[code: Other, type: thisType]; Transfer => IF all OR (procs AND info.kind=Procedure) OR (signals AND info.kind=Signal) OR (errors AND info.kind=Error) THEN transferProc [ transfer: LOOPHOLE--DANGER--[info.transferType], kind: info.kind, argumentRecordType: info.argumentType, resultRecordType: info.resultType, transferIndex: (index ← index+1) ]; Record => BEGIN CheckFieldForProc: ST.ComponentProcedure = {TraceTransfers[componentType, ??, ptrDepth]}; [] ← ST.EnumerateRecord[ recordType: info.recordType, proc: CheckFieldForProc]; END; VariantPart => BEGIN CheckVariantForProc: ST.VariantProcedure = {TraceTransfers[variantRecordType, ??, ptrDepth]}; IF info.kind = Computed THEN RETURN; [] ← ST.EnumerateVariants[ variantPartType: info.variantPartType, proc: CheckVariantForProc]; END; Pointer => BEGIN IF ptrDepth > MaxPointerDepth THEN RETURN; TraceTransfers[info.referentType, ???, ptrDepth+1]; END; Ref => BEGIN IF ptrDepth > MaxPointerDepth THEN RETURN; TraceTransfers[info.referentType, ???, ptrDepth+1]; END; ENDCASE => ERROR; END; -- TraceTransfers. TraceTransfers[thisType: paramRecord]; END;