Home My Page Projects Code Snippets Project Openings SML/NJ
Summary Activity Forums Tracker Lists Tasks Docs Surveys News SCM Files

SCM Repository

[smlnj] Annotation of /sml/trunk/src/cm/stable/stabilize.sml
ViewVC logotype

Annotation of /sml/trunk/src/cm/stable/stabilize.sml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 310 - (view) (download)

1 : blume 309 (*
2 :     * Reading, generating, and writing stable groups.
3 :     *
4 :     * (C) 1999 Lucent Technologies, Bell Laboratories
5 :     *
6 :     * Author: Matthias Blume (blume@kurims.kyoto-u.ac.jp)
7 :     *)
8 :     local
9 : blume 304 structure DG = DependencyGraph
10 : blume 306 structure GG = GroupGraph
11 :     structure EM = GenericVC.ErrorMsg
12 : blume 309 structure GP = GeneralParams
13 :     structure E = GenericVC.Environment
14 : blume 310
15 :     type statenvgetter = GP.info -> DG.bnode -> E.staticEnv
16 : blume 309 in
17 : blume 304
18 : blume 309 signature STABILIZE = sig
19 :    
20 :     val loadStable :
21 : blume 310 GP.info * (AbsPath.t -> GG.group option) * bool ref ->
22 :     AbsPath.t -> GG.group option
23 : blume 309
24 :     val stabilize :
25 :     GP.info ->
26 : blume 310 { group: GG.group, gpath: AbsPath.t, anyerrors: bool ref } ->
27 :     GG.group option
28 : blume 309 end
29 :    
30 : blume 310 functor StabilizeFn (val bn2statenv : statenvgetter) :> STABILIZE = struct
31 : blume 309
32 : blume 307 datatype pitem =
33 :     PSS of SymbolSet.set
34 :     | PS of Symbol.symbol
35 :     | PSN of DG.snode
36 :     | PAP of AbsPath.t
37 : blume 304
38 : blume 307 datatype uitem =
39 :     USS of SymbolSet.set
40 :     | US of Symbol.symbol
41 :     | UBN of DG.bnode
42 :     | UAP of AbsPath.t
43 : blume 304
44 : blume 307 fun compare (PS s, PS s') = SymbolOrdKey.compare (s, s')
45 :     | compare (PS _, _) = GREATER
46 :     | compare (_, PS _) = LESS
47 :     | compare (PSS s, PSS s') = SymbolSet.compare (s, s')
48 :     | compare (PSS _, _) = GREATER
49 :     | compare (_, PSS _) = LESS
50 :     | compare (PSN (DG.SNODE n), PSN (DG.SNODE n')) =
51 :     SmlInfo.compare (#smlinfo n, #smlinfo n')
52 :     | compare (PSN _, _) = GREATER
53 :     | compare (_, PSN _) = LESS
54 :     | compare (PAP p, PAP p') = AbsPath.compare (p, p')
55 :    
56 : blume 304 structure Map =
57 :     BinaryMapFn (struct
58 : blume 307 type ord_key = pitem
59 : blume 304 val compare = compare
60 :     end)
61 :    
62 : blume 308 fun genStableInfoMap (exports, group) = let
63 :     (* find all the exported bnodes that are in the same group: *)
64 :     fun add (((_, DG.SB_BNODE (n as DG.BNODE b)), _), m) = let
65 :     val i = #bininfo b
66 :     in
67 :     if AbsPath.compare (BinInfo.group i, group) = EQUAL then
68 :     IntBinaryMap.insert (m, BinInfo.offset i, n)
69 :     else m
70 :     end
71 :     | add (_, m) = m
72 :     in
73 :     SymbolMap.foldl add IntBinaryMap.empty exports
74 :     end
75 :    
76 : blume 310 fun deleteFile n = OS.FileSys.remove n
77 :     handle e as Interrupt.Interrupt => raise e
78 :     | _ => ()
79 :    
80 :     fun stabilize gp { group = g as GG.GROUP grec, gpath, anyerrors } =
81 : blume 306 case #stableinfo grec of
82 : blume 310 GG.STABLE _ => SOME g
83 : blume 306 | GG.NONSTABLE granted => let
84 : blume 304
85 : blume 310 val bname = AbsPath.name o SmlInfo.binpath
86 :     val bsz = OS.FileSys.fileSize o bname
87 :     fun cpb s i = let
88 :     val ins = BinIO.openIn (bname i)
89 :     fun cp () =
90 :     if BinIO.endOfStream ins then ()
91 :     else (BinIO.output (s, BinIO.input ins); cp ())
92 :     in
93 :     cp () handle e => (BinIO.closeIn ins; raise e);
94 :     BinIO.closeIn ins
95 :     end
96 :     val delb = deleteFile o bname
97 :    
98 : blume 309 val grpSrcInfo = (#errcons gp, anyerrors)
99 : blume 308
100 : blume 306 val exports = #exports grec
101 : blume 308 val islib = #islib grec
102 :     val required = StringSet.difference (#required grec,
103 :     granted)
104 :     val grouppath = #grouppath grec
105 :     val subgroups = #subgroups grec
106 : blume 304
107 : blume 306 (* The format of a stable archive is the following:
108 :     * - It starts with the size s of the pickled dependency
109 :     * graph. This size itself is written as four-byte string.
110 :     * - The pickled dependency graph. This graph contains
111 :     * integer offsets of the binfiles for the individual ML
112 :     * members. These offsets need to be adjusted by adding
113 :     * s + 4. The pickled dependency graph also contains integer
114 :     * offsets relative to other stable groups. These offsets
115 :     * need no further adjustment.
116 :     * - Individual binfile contents (concatenated).
117 :     *)
118 : blume 304
119 : blume 307 val members = ref []
120 : blume 308 val (registerOffset, getOffset) = let
121 :     val dict = ref SmlInfoMap.empty
122 : blume 307 val cur = ref 0
123 :     fun reg (i, sz) = let
124 :     val os = !cur
125 :     in
126 :     cur := os + sz;
127 : blume 308 dict := SmlInfoMap.insert (!dict, i, os);
128 : blume 307 members := i :: (!members);
129 :     os
130 :     end
131 : blume 308 fun get i = valOf (SmlInfoMap.find (!dict, i))
132 : blume 306 in
133 : blume 308 (reg, get)
134 : blume 306 end
135 : blume 304
136 : blume 307 fun w_list w_item [] k m =
137 :     "0" :: k m
138 :     | w_list w_item [a] k m =
139 :     "1" :: w_item a k m
140 :     | w_list w_item [a, b] k m =
141 :     "2" :: w_item a (w_item b k) m
142 : blume 306 | w_list w_item [a, b, c] k m =
143 :     "3" :: w_item a (w_item b (w_item c k)) m
144 :     | w_list w_item [a, b, c, d] k m =
145 :     "4" :: w_item a (w_item b (w_item c (w_item d k))) m
146 :     | w_list w_item (a :: b :: c :: d :: e :: r) k m =
147 :     "5" :: w_item a (w_item b (w_item c (w_item d (w_item e
148 : blume 307 (w_list w_item r k))))) m
149 : blume 304
150 : blume 306 fun w_option w_item NONE k m = "n" :: k m
151 :     | w_option w_item (SOME i) k m = "s" :: w_item i k m
152 : blume 304
153 : blume 306 fun int_encode i = let
154 :     (* this is the same mechanism that's also used in
155 :     * TopLevel/batch/binfile.sml (maybe we should share it) *)
156 :     val n = Word32.fromInt i
157 :     val // = LargeWord.div
158 :     val %% = LargeWord.mod
159 :     val !! = LargeWord.orb
160 :     infix // %% !!
161 :     val toW8 = Word8.fromLargeWord
162 :     fun r (0w0, l) = Word8Vector.fromList l
163 :     | r (n, l) =
164 :     r (n // 0w128, toW8 ((n %% 0w128) !! 0w128) :: l)
165 :     in
166 :     Byte.bytesToString (r (n // 0w128, [toW8 (n %% 0w128)]))
167 :     end
168 : blume 304
169 : blume 306 fun w_int i k m = int_encode i :: k m
170 : blume 304
171 : blume 306 fun w_share w C v k (i, m) =
172 :     case Map.find (m, C v) of
173 :     SOME i' => "o" :: w_int i' k (i, m)
174 :     | NONE => "n" :: w v k (i + 1, Map.insert (m, C v, i))
175 : blume 304
176 : blume 306 fun w_symbol_raw s k m = let
177 :     val ns = case Symbol.nameSpace s of
178 :     Symbol.SIGspace => "'"
179 :     | Symbol.FCTspace => "("
180 :     | Symbol.FSIGspace => ")"
181 :     | Symbol.STRspace => ""
182 :     | _ => GenericVC.ErrorMsg.impossible "stabilize:w_symbol"
183 :     in
184 :     ns :: Symbol.name s :: "." :: k m
185 :     end
186 : blume 304
187 : blume 307 val w_symbol = w_share w_symbol_raw PS
188 : blume 304
189 : blume 307 val w_ss = w_share (w_list w_symbol o SymbolSet.listItems) PSS
190 : blume 306
191 :     val w_filter = w_option w_ss
192 :    
193 :     fun w_string s k m = let
194 :     fun esc #"\\" = "\\\\"
195 :     | esc #"\"" = "\\\""
196 :     | esc c = String.str c
197 : blume 304
198 : blume 306 in
199 :     String.translate esc s :: "\"" :: k m
200 :     end
201 :    
202 :     fun w_sharing NONE k m = "n" :: k m
203 :     | w_sharing (SOME true) k m = "t" :: k m
204 :     | w_sharing (SOME false) k m = "f" :: k m
205 :    
206 : blume 307 fun w_si i k = let
207 : blume 306 val spec = AbsPath.spec (SmlInfo.sourcepath i)
208 :     val locs = SmlInfo.errorLocation gp i
209 : blume 310 val offset = registerOffset (i, bsz i)
210 : blume 306 in
211 :     w_string spec
212 :     (w_string locs
213 :     (w_int offset
214 :     (w_sharing (SmlInfo.share i) k)))
215 :     end
216 :    
217 :     fun w_primitive p k m = String.str (Primitive.toIdent p) :: k m
218 :    
219 :     fun w_abspath_raw p k m =
220 :     w_list w_string (AbsPath.pickle p) k m
221 :    
222 : blume 307 val w_abspath = w_share w_abspath_raw PAP
223 : blume 306
224 :     fun w_bn (DG.PNODE p) k m = "p" :: w_primitive p k m
225 :     | w_bn (DG.BNODE { bininfo = i, ... }) k m =
226 :     "b" :: w_abspath (BinInfo.group i)
227 :     (w_int (BinInfo.offset i) k) m
228 :    
229 : blume 307 fun w_sn_raw (DG.SNODE n) k =
230 : blume 306 w_si (#smlinfo n)
231 :     (w_list w_sn (#localimports n)
232 :     (w_list w_fsbn (#globalimports n) k))
233 :    
234 : blume 307 and w_sn n = w_share w_sn_raw PSN n
235 :    
236 : blume 306 and w_sbn (DG.SB_BNODE n) k m = "b" :: w_bn n k m
237 :     | w_sbn (DG.SB_SNODE n) k m = "s" :: w_sn n k m
238 :    
239 :     and w_fsbn (f, n) k = w_filter f (w_sbn n k)
240 :    
241 :     fun w_impexp (s, (n, _)) k = w_symbol s (w_fsbn n k)
242 :    
243 :     fun w_exports e = w_list w_impexp (SymbolMap.listItemsi e)
244 :    
245 :     fun w_bool true k m = "t" :: k m
246 :     | w_bool false k m = "f" :: k m
247 :    
248 :     fun w_privileges p = w_list w_string (StringSet.listItems p)
249 :    
250 : blume 308 fun pickle_group () = let
251 : blume 306 fun w_sg (GG.GROUP g) = w_abspath (#grouppath g)
252 :     fun k0 m = []
253 :     val m0 = (0, Map.empty)
254 :     in
255 :     concat
256 : blume 308 (w_exports exports
257 :     (w_bool islib
258 :     (w_privileges required
259 :     (w_list w_sg subgroups k0))) m0)
260 : blume 306 end
261 : blume 308
262 :     val pickle = pickle_group ()
263 : blume 306 val sz = size pickle
264 : blume 308 val offset_adjustment = sz + 4
265 :    
266 :     fun mkStableGroup () = let
267 :     val m = ref SmlInfoMap.empty
268 :     fun sn (DG.SNODE (n as { smlinfo, ... })) =
269 :     case SmlInfoMap.find (!m, smlinfo) of
270 :     SOME n => n
271 :     | NONE => let
272 :     val li = map sn (#localimports n)
273 :     val gi = map fsbn (#globalimports n)
274 :     val sourcepath = SmlInfo.sourcepath smlinfo
275 :     val spec = AbsPath.spec sourcepath
276 :     val offset =
277 :     getOffset smlinfo + offset_adjustment
278 :     val share = SmlInfo.share smlinfo
279 :     val locs = SmlInfo.errorLocation gp smlinfo
280 :     val error = EM.errorNoSource grpSrcInfo locs
281 :     val i = BinInfo.new { group = grouppath,
282 :     spec = spec,
283 :     offset = offset,
284 :     share = share,
285 :     error = error }
286 :     val n = DG.BNODE { bininfo = i,
287 :     localimports = li,
288 :     globalimports = gi }
289 :     in
290 :     m := SmlInfoMap.insert (!m, smlinfo, n);
291 :     n
292 :     end
293 :    
294 :     and sbn (DG.SB_SNODE n) = sn n
295 :     | sbn (DG.SB_BNODE n) = n
296 :    
297 :     and fsbn (f, n) = (f, sbn n)
298 :    
299 :     fun impexp ((f, n), e) = ((f, DG.SB_BNODE (sbn n)), e)
300 :    
301 :     val exports = SymbolMap.map impexp (#exports grec)
302 :     val simap = genStableInfoMap (exports, grouppath)
303 :     in
304 :     GG.GROUP { exports = exports,
305 :     islib = islib,
306 :     required = required,
307 :     grouppath = grouppath,
308 :     subgroups = subgroups,
309 :     stableinfo = GG.STABLE simap }
310 :     end
311 :    
312 :     fun writeInt32 (s, i) = let
313 :     val a = Word8Array.array (4, 0w0)
314 :     val _ = Pack32Big.update (a, 0, LargeWord.fromInt i)
315 :     in
316 :     BinIO.output (s, Word8Array.extract (a, 0, NONE))
317 :     end
318 : blume 310 val memberlist = rev (!members)
319 :    
320 :     val policy = #fnpolicy (#param gp)
321 :     val spath = FilenamePolicy.mkStablePath policy gpath
322 :     fun delete () = deleteFile (AbsPath.name spath)
323 :     val outs = AbsPath.openBinOut spath
324 :     fun try () =
325 :     (Say.vsay ["[stabilizing ", AbsPath.name gpath, "]\n"];
326 :     writeInt32 (outs, sz);
327 :     BinIO.output (outs, Byte.stringToBytes pickle);
328 :     app (cpb outs) memberlist;
329 :     app delb memberlist;
330 :     BinIO.closeOut outs;
331 :     SOME (mkStableGroup ()))
332 : blume 306 in
333 : blume 310 Interrupt.guarded try
334 :     handle e as Interrupt.Interrupt => (BinIO.closeOut outs;
335 :     delete ();
336 :     raise e)
337 :     | exn => (BinIO.closeOut outs; NONE)
338 : blume 306 end
339 :    
340 : blume 310 fun loadStable (gp, getGroup, anyerrors) group = let
341 : blume 306
342 : blume 310 fun bn2env n = Statenv2DAEnv.cvtMemo (fn () => bn2statenv gp n)
343 : blume 308
344 : blume 309 val grpSrcInfo = (#errcons gp, anyerrors)
345 :    
346 : blume 306 exception Format
347 :    
348 : blume 310 val policy = #fnpolicy (#param gp)
349 :     val spath = FilenamePolicy.mkStablePath policy group
350 :     val _ = Say.vsay ["[checking stable ", AbsPath.name group, "]\n"]
351 :     val s = AbsPath.openBinIn spath
352 :    
353 :     fun getGroup' p =
354 :     case getGroup p of
355 :     SOME g => g
356 :     | NONE => raise Format
357 :    
358 : blume 306 (* for getting sharing right... *)
359 :     val m = ref IntBinaryMap.empty
360 :     val next = ref 0
361 :    
362 :     fun bytesIn n = let
363 :     val bv = BinIO.inputN (s, n)
364 : blume 304 in
365 : blume 306 if n = Word8Vector.length bv then bv
366 :     else raise Format
367 : blume 304 end
368 :    
369 : blume 306 val sz = LargeWord.toIntX (Pack32Big.subVec (bytesIn 4, 0))
370 :     val pickle = bytesIn sz
371 :     val offset_adjustment = sz + 4
372 : blume 304
373 : blume 306 val rd = let
374 :     val pos = ref 0
375 :     fun rd () = let
376 :     val p = !pos
377 :     in
378 :     pos := p + 1;
379 :     Byte.byteToChar (Word8Vector.sub (pickle, p))
380 :     handle _ => raise Format
381 :     end
382 : blume 304 in
383 : blume 306 rd
384 : blume 304 end
385 :    
386 : blume 306 fun r_list r () =
387 :     case rd () of
388 :     #"0" => []
389 :     | #"1" => [r ()]
390 :     | #"2" => [r (), r ()]
391 :     | #"3" => [r (), r (), r ()]
392 :     | #"4" => [r (), r (), r (), r ()]
393 :     | #"5" => r () :: r () :: r () :: r () :: r () :: r_list r ()
394 :     | _ => raise Format
395 : blume 304
396 : blume 306 fun r_bool () =
397 :     case rd () of
398 :     #"t" => true
399 :     | #"f" => false
400 :     | _ => raise Format
401 : blume 304
402 : blume 306 fun r_option r_item () =
403 :     case rd () of
404 :     #"n" => NONE
405 :     | #"s" => SOME (r_item ())
406 :     | _ => raise Format
407 : blume 304
408 : blume 306 fun r_int () = let
409 :     fun loop n = let
410 :     val w8 = Byte.charToByte (rd ())
411 :     val n' = n * 0w128 + Word8.toLargeWord (Word8.andb (w8, 0w127))
412 :     in
413 :     if Word8.andb (w8, 0w128) = 0w0 then n' else loop n'
414 :     end
415 :     in
416 :     LargeWord.toIntX (loop 0w0)
417 :     end
418 : blume 304
419 : blume 306 fun r_share r_raw C unC () =
420 :     case rd () of
421 :     #"o" => (case IntBinaryMap.find (!m, r_int ()) of
422 :     SOME x => unC x
423 :     | NONE => raise Format)
424 :     | #"n" => let
425 :     val i = !next
426 :     val _ = next := i + 1
427 :     val v = r_raw ()
428 :     in
429 :     m := IntBinaryMap.insert (!m, i, C v);
430 :     v
431 :     end
432 :     | _ => raise Format
433 : blume 304
434 : blume 306 fun r_string () = let
435 :     fun loop l =
436 :     case rd () of
437 :     #"\"" => String.implode (rev l)
438 :     | #"\\" => loop (rd () :: l)
439 :     | c => loop (c :: l)
440 :     in
441 :     loop []
442 :     end
443 : blume 304
444 : blume 306 val r_abspath = let
445 :     fun r_abspath_raw () =
446 :     case AbsPath.unpickle (r_list r_string ()) of
447 :     SOME p => p
448 :     | NONE => raise Format
449 : blume 307 fun unUAP (UAP x) = x
450 :     | unUAP _ = raise Format
451 : blume 306 in
452 : blume 307 r_share r_abspath_raw UAP unUAP
453 : blume 306 end
454 : blume 304
455 : blume 306 val r_symbol = let
456 :     fun r_symbol_raw () = let
457 :     val (ns, first) =
458 :     case rd () of
459 :     #"`" => (Symbol.sigSymbol, rd ())
460 :     | #"(" => (Symbol.fctSymbol, rd ())
461 :     | #")" => (Symbol.fsigSymbol, rd ())
462 :     | c => (Symbol.strSymbol, c)
463 :     fun loop (#".", l) = String.implode (rev l)
464 :     | loop (c, l) = loop (rd (), c :: l)
465 :     in
466 :     ns (loop (first, []))
467 :     end
468 : blume 307 fun unUS (US x) = x
469 :     | unUS _ = raise Format
470 : blume 306 in
471 : blume 307 r_share r_symbol_raw US unUS
472 : blume 306 end
473 : blume 304
474 : blume 306 val r_ss = let
475 :     fun r_ss_raw () =
476 :     SymbolSet.addList (SymbolSet.empty, r_list r_symbol ())
477 : blume 307 fun unUSS (USS s) = s
478 :     | unUSS _ = raise Format
479 : blume 306 in
480 : blume 307 r_share r_ss_raw USS unUSS
481 : blume 306 end
482 : blume 304
483 : blume 306 val r_filter = r_option r_ss
484 : blume 305
485 : blume 306 fun r_primitive () =
486 :     case Primitive.fromIdent (rd ()) of
487 :     NONE => raise Format
488 :     | SOME p => p
489 : blume 305
490 : blume 306 fun r_sharing () =
491 :     case rd () of
492 :     #"n" => NONE
493 :     | #"t" => SOME true
494 :     | #"f" => SOME false
495 :     | _ => raise Format
496 : blume 305
497 : blume 307 fun r_si () = let
498 :     val spec = r_string ()
499 :     val locs = r_string ()
500 :     val offset = r_int () + offset_adjustment
501 :     val share = r_sharing ()
502 :     val error = EM.errorNoSource grpSrcInfo locs
503 : blume 306 in
504 : blume 307 BinInfo.new { group = group,
505 :     error = error,
506 :     spec = spec,
507 :     offset = offset,
508 :     share = share }
509 : blume 306 end
510 : blume 305
511 : blume 306 fun r_bn () =
512 :     case rd () of
513 :     #"p" => DG.PNODE (r_primitive ())
514 : blume 307 | #"b" => let
515 :     val p = r_abspath ()
516 :     val os = r_int ()
517 :     in
518 : blume 310 case getGroup' p of
519 :     GG.GROUP { stableinfo = GG.STABLE im, ... } =>
520 : blume 307 (case IntBinaryMap.find (im, os) of
521 :     NONE => raise Format
522 :     | SOME n => n)
523 : blume 310 | _ => raise Format
524 : blume 307 end
525 : blume 306 | _ => raise Format
526 :    
527 :     (* this is the place where what used to be an
528 :     * SNODE changes to a BNODE! *)
529 : blume 307 fun r_sn_raw () =
530 : blume 306 DG.BNODE { bininfo = r_si (),
531 :     localimports = r_list r_sn (),
532 :     globalimports = r_list r_fsbn () }
533 :    
534 : blume 307 and r_sn () =
535 :     r_share r_sn_raw UBN (fn (UBN n) => n | _ => raise Format) ()
536 :    
537 : blume 306 (* this one changes from farsbnode to plain farbnode *)
538 :     and r_sbn () =
539 :     case rd () of
540 :     #"b" => r_bn ()
541 :     | #"s" => r_sn ()
542 :     | _ => raise Format
543 :    
544 :     and r_fsbn () = (r_filter (), r_sbn ())
545 :    
546 :     fun r_impexp () = let
547 :     val sy = r_symbol ()
548 :     val (f, n) = r_fsbn () (* really reads farbnodes! *)
549 : blume 307 val e = bn2env n
550 : blume 310 (* put a filter in front to avoid having the FCTENV being
551 :     * queried needlessly (this avoids spurious module loadings) *)
552 :     val e' = DAEnv.FILTER (SymbolSet.singleton sy, e)
553 : blume 305 in
554 : blume 310 (sy, ((f, DG.SB_BNODE n), e')) (* coerce to farsbnodes *)
555 : blume 305 end
556 : blume 306
557 :     fun r_exports () =
558 :     foldl SymbolMap.insert' SymbolMap.empty (r_list r_impexp ())
559 :    
560 :     fun r_privileges () =
561 :     StringSet.addList (StringSet.empty, r_list r_string ())
562 :    
563 :     fun unpickle_group () = let
564 :     val exports = r_exports ()
565 :     val islib = r_bool ()
566 :     val required = r_privileges ()
567 : blume 310 val subgroups = r_list (getGroup' o r_abspath) ()
568 : blume 308 val simap = genStableInfoMap (exports, group)
569 : blume 306 in
570 :     GG.GROUP { exports = exports,
571 :     islib = islib,
572 :     required = required,
573 : blume 308 grouppath = group,
574 : blume 306 subgroups = subgroups,
575 : blume 307 stableinfo = GG.STABLE simap }
576 : blume 310 before BinIO.closeIn s
577 : blume 306 end
578 : blume 304 in
579 : blume 310 SOME (unpickle_group ())
580 :     handle Format => (BinIO.closeIn s; NONE)
581 :     | exn => (BinIO.closeIn s; raise exn)
582 :     end handle IO.Io _ => NONE
583 : blume 304 end
584 : blume 309
585 :     end (* local *)

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0