(* Copyright (C) 1992, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* File: RemoteMethods.m3 *) (* Last Modified On Tue Dec 29 16:37:40 PST 1992 By rustan *) MODULE RemoteMethods; IMPORT ParamCode, Text, Fmt, Word, Target, M3Linker, Wr, Thread; <*FATAL Wr.Failure, Thread.Alerted*> CONST ProcTypeCNamePrefix = "procType_"; REVEAL T = BRANDED REF RECORD id : CARDINAL END; TYPE ReturnValue = { None, Word, Network }; M = REF RECORD id : CARDINAL; params : TEXT; rets := ARRAY ReturnValue OF T { NIL, .. }; next : M END; VAR Ms: M := NIL; nextTid: CARDINAL := 0; nextMid: CARDINAL := 0; PROCEDURE Add( t: TEXT ): T RAISES {M3Linker.LinkError} = VAR m : M; n : INTEGER; ret : ReturnValue; BEGIN <* ASSERT t # NIL *> n := CheckParamEncoding( t ); CASE Text.GetChar( t, 0 ) OF <* NOWARN *> ParamCode.NoneCh => ret := ReturnValue.None | ParamCode.WordCh => ret := ReturnValue.Word | ParamCode.NetworkCh => ret := ReturnValue.Network END; m := AddM( Text.Sub( t, 1, n-1 )); IF m.rets[ ret ] = NIL THEN m.rets[ ret ] := NEW( T, id := nextTid ); INC( nextTid ) END; RETURN m.rets[ ret ] END Add; PROCEDURE CheckParamEncoding( t: TEXT ): INTEGER RAISES {M3Linker.LinkError} = VAR ch: CHAR; n: INTEGER := Text.Length( t ); BEGIN IF n = 0 THEN RAISE M3Linker.LinkError( "parameter encoding is empty" ) END; FOR i := 0 TO n - 1 DO ch := Text.GetChar( t, i ); CASE ch OF ParamCode.WordCh => (* skip *) | ParamCode.NetworkCh => (* skip *) ELSE IF NOT ( i = 0 AND ch = ParamCode.NoneCh ) THEN RAISE M3Linker.LinkError( "invalid parameter encoding: " & t ) END END END; RETURN n END CheckParamEncoding; PROCEDURE AddM( params: TEXT ): M = VAR m: M := Ms; BEGIN WHILE m # NIL AND NOT Text.Equal( m.params, params ) DO m := m.next END; IF m = NIL THEN m := NEW( M, id := nextMid, params := params, next := Ms ); INC( nextMid ); Ms := m END; RETURN m END AddM; PROCEDURE GenPreDecls( wr: Wr.T ) = VAR m: M := Ms; BEGIN Out( wr, "struct procType " & "{ int ret; int * params; };\n" ); WHILE m # NIL DO OutF( wr, "int methodParams_%s[] = ", Fmt.Int( m.id )); GenMethodParams( wr, m.params ); FOR r := FIRST( ReturnValue ) TO LAST( ReturnValue ) DO IF m.rets[ r ] # NIL THEN OutF( wr, "struct procType %s%s = ", ProcTypeCNamePrefix, Fmt.Int( m.rets[ r ].id )); OutF( wr, "{ %s, methodParams_%s };\n", Fmt.Int( ORD( r )), Fmt.Int( m.id )) END END; m := m.next END END GenPreDecls; PROCEDURE GenMethodParams( wr: Wr.T; params: TEXT ) = VAR w: Word.T; index: CARDINAL := 0; BEGIN (* count the number of 'word's and 'network's, and generate those numbers as the first two elements of the array *) VAR words, networks: CARDINAL := 0; BEGIN FOR i := 0 TO Text.Length( params ) - 1 DO CASE Text.GetChar( params, i ) OF <* NOWARN *> ParamCode.WordCh => INC( words ) | ParamCode.NetworkCh => INC( networks ) END END; OutF( wr, "{ %s, %s, ", Fmt.Int( words + 2*networks ), Fmt.Int( words + networks )) END; w := 0; FOR i := 0 TO Text.Length( params ) - 1 DO IF index = Target.INTSIZE THEN (* it would be nice to be able to specify the 4 in the next line as a function of Target.INTSIZE. Same for the other 4 below. *) OutF( wr, "0x%04s, ", Fmt.Int( w, 16 )); w := 0; index := 0 END; CASE Text.GetChar( params, i ) OF <* NOWARN *> ParamCode.WordCh => (* skip *) | ParamCode.NetworkCh => w := Word.Or( w, Word.Shift( 1, index )) END; INC( index ) END; OutF( wr, "0x%04s }; /* %s */\n", Fmt.Int( w, 16 ), params ) END GenMethodParams; PROCEDURE CName( t: T ): TEXT = BEGIN <* ASSERT t # NIL *> RETURN ProcTypeCNamePrefix & Fmt.Int( t.id ) END CName; PROCEDURE OutF( wr: Wr.T; f: TEXT; a, b, c, d: TEXT := NIL ) = BEGIN Out( wr, Fmt.F( f, a, b, c, d )) END OutF; PROCEDURE Out( wr: Wr.T; a, b, c, d: TEXT := NIL ) = BEGIN IF (a # NIL) THEN Wr.PutText (wr, a) END; IF (b # NIL) THEN Wr.PutText (wr, b) END; IF (c # NIL) THEN Wr.PutText (wr, c) END; IF (d # NIL) THEN Wr.PutText (wr, d) END; END Out; BEGIN END RemoteMethods.