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 384 - (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 311 structure PP = PrettyPrint
13 :     structure SM = GenericVC.SourceMap
14 : blume 309 structure GP = GeneralParams
15 :     structure E = GenericVC.Environment
16 : blume 357 structure Pid = GenericVC.PersStamps
17 : blume 310
18 :     type statenvgetter = GP.info -> DG.bnode -> E.staticEnv
19 : blume 314 type recomp = GP.info -> GG.group -> bool
20 : blume 357 type pid = Pid.persstamp
21 : blume 309 in
22 : blume 304
23 : blume 309 signature STABILIZE = sig
24 :    
25 :     val loadStable :
26 : blume 354 GP.info * (SrcPath.t -> GG.group option) * bool ref ->
27 :     SrcPath.t -> GG.group option
28 : blume 309
29 :     val stabilize :
30 : blume 311 GP.info -> { group: GG.group, anyerrors: bool ref } ->
31 : blume 310 GG.group option
32 : blume 309 end
33 :    
34 : blume 311 functor StabilizeFn (val bn2statenv : statenvgetter
35 : blume 363 val transfer_state : SmlInfo.info * BinInfo.info -> unit
36 : blume 357 val recomp : recomp) :> STABILIZE = struct
37 : blume 309
38 : blume 384 structure PU = PickleUtil
39 :     structure UU = UnpickleUtil
40 : blume 304
41 : blume 384 infix 3 $
42 :     infixr 4 &
43 :     val op & = PU.&
44 :     val % = PU.%
45 :    
46 : blume 307 datatype uitem =
47 :     USS of SymbolSet.set
48 :     | US of Symbol.symbol
49 :     | UBN of DG.bnode
50 : blume 304
51 : blume 384 structure SNMap = BinaryMapFn
52 :     (struct
53 :     type ord_key = DG.snode
54 :     fun compare (DG.SNODE n, DG.SNODE n') =
55 :     SmlInfo.compare (#smlinfo n, #smlinfo n')
56 : blume 304 end)
57 :    
58 : blume 384 val initMap = SNMap.empty
59 :     val SNs = { find = SNMap.find, insert = SNMap.insert }
60 :    
61 : blume 308 fun genStableInfoMap (exports, group) = let
62 :     (* find all the exported bnodes that are in the same group: *)
63 :     fun add (((_, DG.SB_BNODE (n as DG.BNODE b)), _), m) = let
64 :     val i = #bininfo b
65 :     in
66 : blume 354 if SrcPath.compare (BinInfo.group i, group) = EQUAL then
67 : blume 308 IntBinaryMap.insert (m, BinInfo.offset i, n)
68 :     else m
69 :     end
70 :     | add (_, m) = m
71 :     in
72 :     SymbolMap.foldl add IntBinaryMap.empty exports
73 :     end
74 :    
75 : blume 311 fun stabilize gp { group = g as GG.GROUP grec, anyerrors } = let
76 : blume 304
77 : blume 323 val primconf = #primconf (#param gp)
78 :     val policy = #fnpolicy (#param gp)
79 :    
80 : blume 340 val grouppath = #grouppath grec
81 :    
82 : blume 348 fun doit wrapped = let
83 : blume 312
84 : blume 314 val _ =
85 : blume 348 if StringSet.isEmpty wrapped then ()
86 : blume 314 else
87 :     Say.say ("$Stabilize: wrapping the following privileges:\n"
88 : blume 312 :: map (fn s => (" " ^ s ^ "\n"))
89 : blume 348 (StringSet.listItems wrapped))
90 : blume 312
91 : blume 354 val bname = SmlInfo.binname
92 : blume 311 val bsz = OS.FileSys.fileSize o bname
93 : blume 345
94 : blume 311 fun cpb s i = let
95 : blume 360 val N = 4096
96 : blume 345 fun copy ins = let
97 :     fun cp () =
98 :     if BinIO.endOfStream ins then ()
99 : blume 360 else (BinIO.output (s, BinIO.inputN (ins, N));
100 :     cp ())
101 : blume 345 in
102 :     cp ()
103 :     end
104 : blume 311 in
105 : blume 345 SafeIO.perform { openIt = fn () => BinIO.openIn (bname i),
106 :     closeIt = BinIO.closeIn,
107 :     work = copy,
108 :     cleanup = fn () => () }
109 : blume 311 end
110 : blume 310
111 : blume 311 val grpSrcInfo = (#errcons gp, anyerrors)
112 : blume 308
113 : blume 311 val exports = #exports grec
114 : blume 348 val required = StringSet.difference (#required grec, wrapped)
115 : blume 340 val sublibs = #sublibs grec
116 : blume 304
117 : blume 311 (* The format of a stable archive is the following:
118 :     * - It starts with the size s of the pickled dependency
119 :     * graph. This size itself is written as four-byte string.
120 :     * - The pickled dependency graph. This graph contains
121 :     * integer offsets of the binfiles for the individual ML
122 :     * members. These offsets need to be adjusted by adding
123 :     * s + 4. The pickled dependency graph also contains integer
124 :     * offsets relative to other stable groups. These offsets
125 :     * need no further adjustment.
126 :     * - Individual binfile contents (concatenated).
127 :     *)
128 : blume 304
129 : blume 340 (* Here we build a mapping that maps each BNODE to a number
130 :     * representing the sub-library that it came from and a
131 :     * representative symbol that can be used to find the BNODE
132 :     * within the exports of that library *)
133 :     fun oneB i (sy, ((_, DG.SB_BNODE (DG.BNODE n)), _), m) =
134 :     StableMap.insert (m, #bininfo n, (i, sy))
135 :     | oneB i (_, _, m) = m
136 : blume 380 fun oneSL ((g as GG.GROUP { exports, ... }), (m, i)) =
137 : blume 340 (SymbolMap.foldli (oneB i) m exports, i + 1)
138 :     val inverseMap = #1 (foldl oneSL (StableMap.empty, 0) sublibs)
139 : blume 330
140 : blume 311 val members = ref []
141 :     val (registerOffset, getOffset) = let
142 :     val dict = ref SmlInfoMap.empty
143 :     val cur = ref 0
144 :     fun reg (i, sz) = let
145 :     val os = !cur
146 : blume 306 in
147 : blume 311 cur := os + sz;
148 :     dict := SmlInfoMap.insert (!dict, i, os);
149 :     members := i :: (!members);
150 :     os
151 : blume 306 end
152 : blume 311 fun get i = valOf (SmlInfoMap.find (!dict, i))
153 :     in
154 :     (reg, get)
155 :     end
156 : blume 304
157 : blume 384 val int = PU.w_int
158 :     val symbol = PU.w_symbol
159 :     val share = PU.ah_share
160 :     val option = PU.w_option
161 :     val list = PU.w_list
162 :     val string = PU.w_string
163 :     val bool = PU.w_bool
164 :     val int = PU.w_int
165 : blume 304
166 : blume 384 val symbolset = list symbol o SymbolSet.listItems
167 : blume 304
168 : blume 384 val filter = option symbolset
169 : blume 304
170 : blume 384 val sh = option bool (* sharing *)
171 : blume 304
172 : blume 384 fun si i = let
173 : blume 340 (* FIXME: this is not a technical flaw, but perhaps one
174 :     * that deserves fixing anyway: If we only look at spec,
175 :     * then we are losing information about sub-grouping
176 :     * within libraries. However, the spec in BinInfo.info
177 :     * is only used for diagnostics and has no impact on the
178 :     * operation of CM itself. *)
179 : blume 354 val spec = SrcPath.specOf (SmlInfo.sourcepath i)
180 : blume 311 val locs = SmlInfo.errorLocation gp i
181 :     val offset = registerOffset (i, bsz i)
182 :     in
183 : blume 384 string spec & string locs & int offset & sh (SmlInfo.share i)
184 : blume 311 end
185 : blume 306
186 : blume 384 fun primitive p =
187 :     string (String.str (Primitive.toIdent primconf p))
188 : blume 306
189 : blume 340 fun warn_relabs p abs = let
190 :     val relabs = if abs then "absolute" else "relative"
191 : blume 330 fun ppb pps =
192 :     (PP.add_newline pps;
193 : blume 354 PP.add_string pps (SrcPath.descr p);
194 : blume 330 PP.add_newline pps;
195 :     PP.add_string pps
196 :     "(This means that in order to be able to use the result of stabilization";
197 :     PP.add_newline pps;
198 : blume 340 PP.add_string pps "the library must be in the same ";
199 : blume 330 PP.add_string pps relabs;
200 :     PP.add_string pps " location as it is now.)";
201 :     PP.add_newline pps)
202 :     in
203 :     EM.errorNoFile (#errcons gp, anyerrors) SM.nullRegion
204 :     EM.WARN
205 : blume 354 (concat [SrcPath.descr grouppath,
206 : blume 340 ": library referred to by ", relabs,
207 :     " pathname:"])
208 : blume 330 ppb
209 :     end
210 : blume 306
211 : blume 384 fun abspath p = let
212 :     val pp = SrcPath.pickle (warn_relabs p) (p, grouppath)
213 :     in
214 :     list string pp
215 :     end
216 : blume 306
217 : blume 384 val BN = 1
218 :     val op $ = PU.$ BN
219 :     fun bn (DG.PNODE p) = "1" $ primitive p
220 :     | bn (DG.BNODE { bininfo = i, ... }) = let
221 : blume 340 val (n, sy) = valOf (StableMap.find (inverseMap, i))
222 :     in
223 : blume 384 "2" $ int n & symbol sy
224 : blume 340 end
225 : blume 306
226 : blume 384 local
227 :     val SN = 2
228 :     val SBN = 3
229 :     in
230 :     fun sn n = let
231 :     fun raw_sn (DG.SNODE n) =
232 :     "a" $ si (#smlinfo n) & list sn (#localimports n) &
233 :     list fsbn (#globalimports n)
234 :     in
235 :     share SNs raw_sn n
236 :     end
237 : blume 306
238 : blume 384 and sbn x = let
239 :     val op $ = PU.$ SBN
240 :     in
241 :     case x of
242 :     DG.SB_BNODE n => "a" $ bn n
243 :     | DG.SB_SNODE n => "b" $ sn n
244 :     end
245 : blume 370
246 : blume 384 and fsbn (f, n) = filter f & sbn n
247 :     end
248 : blume 307
249 : blume 384 fun impexp (s, (n, _)) = symbol s & fsbn n
250 : blume 306
251 : blume 384 fun w_exports e = list impexp (SymbolMap.listItemsi e)
252 : blume 306
253 : blume 384 fun privileges p = list string (StringSet.listItems p)
254 : blume 306
255 : blume 384 fun group () = let
256 :     fun sg (GG.GROUP { grouppath, ... }) = abspath grouppath
257 : blume 311 in
258 : blume 340 (* Pickle the sublibs first because we need to already
259 : blume 330 * have them back when we unpickle BNODEs. *)
260 : blume 384 list sg sublibs & w_exports exports & privileges required
261 : blume 311 end
262 : blume 308
263 : blume 384 val pickle = PU.pickle initMap (group ())
264 : blume 311 val sz = size pickle
265 :     val offset_adjustment = sz + 4
266 : blume 308
267 : blume 361 fun mkStableGroup mksname = let
268 : blume 311 val m = ref SmlInfoMap.empty
269 :     fun sn (DG.SNODE (n as { smlinfo, ... })) =
270 :     case SmlInfoMap.find (!m, smlinfo) of
271 :     SOME n => n
272 :     | NONE => let
273 : blume 371 val li = map sn (#localimports n)
274 :     val gi = map fsbn (#globalimports n)
275 : blume 311 val sourcepath = SmlInfo.sourcepath smlinfo
276 : blume 340 (* FIXME: see the comment near the other
277 : blume 354 * occurence of SrcPath.spec... *)
278 :     val spec = SrcPath.specOf sourcepath
279 : blume 311 val offset =
280 :     getOffset smlinfo + offset_adjustment
281 :     val share = SmlInfo.share smlinfo
282 :     val locs = SmlInfo.errorLocation gp smlinfo
283 :     val error = EM.errorNoSource grpSrcInfo locs
284 :     val i = BinInfo.new { group = grouppath,
285 : blume 361 mkStablename = mksname,
286 : blume 311 spec = spec,
287 :     offset = offset,
288 :     share = share,
289 :     error = error }
290 :     val n = DG.BNODE { bininfo = i,
291 :     localimports = li,
292 :     globalimports = gi }
293 :     in
294 : blume 363 transfer_state (smlinfo, i);
295 : blume 311 m := SmlInfoMap.insert (!m, smlinfo, n);
296 :     n
297 :     end
298 : blume 308
299 : blume 311 and sbn (DG.SB_SNODE n) = sn n
300 :     | sbn (DG.SB_BNODE n) = n
301 : blume 308
302 : blume 311 and fsbn (f, n) = (f, sbn n)
303 : blume 308
304 : blume 311 fun impexp ((f, n), e) = ((f, DG.SB_BNODE (sbn n)), e)
305 : blume 308
306 : blume 311 val exports = SymbolMap.map impexp (#exports grec)
307 :     val simap = genStableInfoMap (exports, grouppath)
308 :     in
309 :     GG.GROUP { exports = exports,
310 : blume 348 kind = GG.STABLELIB simap,
311 : blume 311 required = required,
312 :     grouppath = grouppath,
313 : blume 348 sublibs = sublibs }
314 : blume 311 end
315 : blume 308
316 : blume 311 fun writeInt32 (s, i) = let
317 :     val a = Word8Array.array (4, 0w0)
318 :     val _ = Pack32Big.update (a, 0, LargeWord.fromInt i)
319 :     in
320 :     BinIO.output (s, Word8Array.extract (a, 0, NONE))
321 :     end
322 :     val memberlist = rev (!members)
323 :    
324 :     val gpath = #grouppath grec
325 : blume 361 fun mksname () = FilenamePolicy.mkStableName policy gpath
326 : blume 345 fun work outs =
327 : blume 354 (Say.vsay ["[stabilizing ", SrcPath.descr gpath, "]\n"];
328 : blume 311 writeInt32 (outs, sz);
329 :     BinIO.output (outs, Byte.stringToBytes pickle);
330 :     app (cpb outs) memberlist;
331 : blume 361 mkStableGroup mksname)
332 : blume 311 in
333 : blume 361 SOME (SafeIO.perform { openIt = AutoDir.openBinOut o mksname,
334 : blume 345 closeIt = BinIO.closeOut,
335 :     work = work,
336 : blume 354 cleanup = fn () =>
337 : blume 361 (OS.FileSys.remove (mksname ())
338 :     handle _ => ()) })
339 : blume 345 handle exn => NONE
340 : blume 311 end
341 :     in
342 : blume 348 case #kind grec of
343 :     GG.STABLELIB _ => SOME g
344 :     | GG.NOLIB => EM.impossible "stabilize: no library"
345 :     | GG.LIB wrapped =>
346 : blume 314 if not (recomp gp g) then
347 : blume 311 (anyerrors := true; NONE)
348 :     else let
349 : blume 380 fun notStable (GG.GROUP { kind, ... }) =
350 : blume 353 case kind of GG.STABLELIB _ => false | _ => true
351 : blume 308 in
352 : blume 340 case List.filter notStable (#sublibs grec) of
353 : blume 348 [] => doit wrapped
354 : blume 311 | l => let
355 :     val grammar = case l of [_] => " is" | _ => "s are"
356 :     fun ppb pps = let
357 :     fun loop [] = ()
358 : blume 380 | loop (GG.GROUP { grouppath, ... } :: t) =
359 : blume 311 (PP.add_string pps
360 : blume 354 (SrcPath.descr grouppath);
361 : blume 311 PP.add_newline pps;
362 :     loop t)
363 :     in
364 :     PP.add_newline pps;
365 :     PP.add_string pps
366 :     (concat ["because the following sub-group",
367 :     grammar, " not stable:"]);
368 :     PP.add_newline pps;
369 :     loop l
370 :     end
371 :     val errcons = #errcons gp
372 : blume 354 val gdescr = SrcPath.descr (#grouppath grec)
373 : blume 311 in
374 :     EM.errorNoFile (errcons, anyerrors) SM.nullRegion
375 :     EM.COMPLAIN
376 : blume 354 (gdescr ^ " cannot be stabilized")
377 : blume 311 ppb;
378 :     NONE
379 :     end
380 : blume 308 end
381 : blume 311 end
382 : blume 310
383 :     fun loadStable (gp, getGroup, anyerrors) group = let
384 : blume 306
385 : blume 355 val es2bs = GenericVC.CoerceEnv.es2bs
386 :     fun bn2env n =
387 :     Statenv2DAEnv.cvtMemo (fn () => es2bs (bn2statenv gp n))
388 : blume 308
389 : blume 311 val errcons = #errcons gp
390 :     val grpSrcInfo = (errcons, anyerrors)
391 : blume 354 val gdescr = SrcPath.descr group
392 : blume 311 fun error l = EM.errorNoFile (errcons, anyerrors) SM.nullRegion
393 : blume 367 EM.COMPLAIN (concat ("(stable) " :: gdescr :: ": " :: l))
394 :     EM.nullErrorBody
395 : blume 309
396 : blume 384 exception Format = UU.Format
397 : blume 306
398 : blume 318 val pcmode = #pcmode (#param gp)
399 : blume 310 val policy = #fnpolicy (#param gp)
400 : blume 323 val primconf = #primconf (#param gp)
401 : blume 361 fun mksname () = FilenamePolicy.mkStableName policy group
402 : blume 310
403 : blume 345 fun work s = let
404 : blume 310
405 : blume 345 fun getGroup' p =
406 :     case getGroup p of
407 :     SOME g => g
408 : blume 354 | NONE => (error ["unable to find ", SrcPath.descr p];
409 : blume 345 raise Format)
410 : blume 306
411 : blume 345 (* for getting sharing right... *)
412 :     val m = ref IntBinaryMap.empty
413 :     val next = ref 0
414 : blume 304
415 : blume 357 val pset = ref PidSet.empty
416 :    
417 : blume 345 fun bytesIn n = let
418 :     val bv = BinIO.inputN (s, n)
419 :     in
420 :     if n = Word8Vector.length bv then bv
421 :     else raise Format
422 :     end
423 : blume 304
424 : blume 345 val sz = LargeWord.toIntX (Pack32Big.subVec (bytesIn 4, 0))
425 : blume 384 val pickle = Byte.bytesToString (bytesIn sz)
426 : blume 345 val offset_adjustment = sz + 4
427 :    
428 : blume 384 val session = UU.mkSession (UU.stringReader pickle)
429 : blume 304
430 : blume 384 fun list m r = UU.r_list session m r
431 :     fun option m r = UU.r_option session m r
432 :     val int = UU.r_int session
433 :     fun share m r = UU.share session m r
434 :     val string = UU.r_string session
435 :     val symbol = UU.r_symbol session
436 :     val bool = UU.r_bool session
437 : blume 304
438 : blume 384 val stringListM = UU.mkMap ()
439 :     val symbolListM = UU.mkMap ()
440 :     val stringListM = UU.mkMap ()
441 :     val ssoM = UU.mkMap ()
442 :     val boolOptionM = UU.mkMap ()
443 :     val sgListM = UU.mkMap ()
444 :     val snM = UU.mkMap ()
445 :     val snListM = UU.mkMap ()
446 :     val bnM = UU.mkMap ()
447 :     val sbnM = UU.mkMap ()
448 :     val fsbnListM = UU.mkMap ()
449 :     val impexpListM = UU.mkMap ()
450 : blume 304
451 : blume 384 val stringlist = list stringListM string
452 : blume 304
453 : blume 384 fun abspath () =
454 :     SrcPath.unpickle pcmode (stringlist (), group)
455 : blume 367 handle SrcPath.Format => raise Format
456 :     | SrcPath.BadAnchor a =>
457 :     (error ["configuration anchor \"", a, "\" undefined"];
458 :     raise Format)
459 : blume 304
460 : blume 384 val symbollist = list symbolListM symbol
461 : blume 367
462 : blume 384 fun symbolset () =
463 :     SymbolSet.addList (SymbolSet.empty, symbollist ())
464 : blume 305
465 : blume 384 val filter = option ssoM symbolset
466 : blume 305
467 : blume 384 fun primitive () =
468 :     valOf (Primitive.fromIdent primconf
469 :     (String.sub (string (), 0)))
470 :     handle _ => raise Format
471 : blume 305
472 : blume 384 val sh = option boolOptionM bool
473 : blume 305
474 : blume 384 fun si () = let
475 :     val spec = string ()
476 :     val locs = string ()
477 :     val offset = int () + offset_adjustment
478 :     val share = sh ()
479 : blume 345 val error = EM.errorNoSource grpSrcInfo locs
480 :     in
481 :     BinInfo.new { group = group,
482 : blume 361 mkStablename = mksname,
483 : blume 345 error = error,
484 :     spec = spec,
485 :     offset = offset,
486 :     share = share }
487 :     end
488 : blume 306
489 : blume 384 fun sg () = getGroup' (abspath ())
490 : blume 345
491 : blume 384 val sublibs = list sgListM sg ()
492 : blume 307
493 : blume 384 fun bn () = let
494 :     fun bn' #"1" = DG.PNODE (primitive ())
495 :     | bn' #"2" = let
496 :     val n = int ()
497 :     val sy = symbol ()
498 : blume 380 val GG.GROUP { exports = slexp, ... } =
499 : blume 340 List.nth (sublibs, n) handle _ => raise Format
500 : blume 330 in
501 : blume 340 case SymbolMap.find (slexp, sy) of
502 : blume 330 SOME ((_, DG.SB_BNODE (n as DG.BNODE _)), _) => n
503 :     | _ => raise Format
504 :     end
505 : blume 384 | bn' _ = raise Format
506 :     in
507 :     share bnM bn'
508 :     end
509 : blume 306
510 : blume 330 (* this is the place where what used to be an
511 :     * SNODE changes to a BNODE! *)
512 : blume 384 fun sn () = let
513 :     fun sn' #"a" =
514 :     DG.BNODE { bininfo = si (),
515 :     localimports = snlist (),
516 :     globalimports = fsbnlist () }
517 :     | sn' _ = raise Format
518 :     in
519 :     share snM sn'
520 :     end
521 : blume 306
522 : blume 384 and snlist () = list snListM sn ()
523 : blume 306
524 : blume 330 (* this one changes from farsbnode to plain farbnode *)
525 : blume 384 and sbn () = let
526 :     fun sbn' #"a" = bn ()
527 :     | sbn' #"b" = sn ()
528 :     | sbn' _ = raise Format
529 :     in
530 :     share sbnM sbn'
531 :     end
532 : blume 306
533 : blume 384 and fsbn () = (filter (), sbn ())
534 : blume 306
535 : blume 384 and fsbnlist () = list fsbnListM fsbn ()
536 :    
537 :     fun impexp () = let
538 :     val sy = symbol ()
539 :     val (f, n) = fsbn () (* really reads farbnodes! *)
540 : blume 330 val e = bn2env n
541 :     (* put a filter in front to avoid having the FCTENV being
542 :     * queried needlessly (this avoids spurious module loadings) *)
543 :     val e' = DAEnv.FILTER (SymbolSet.singleton sy, e)
544 :     in
545 :     (sy, ((f, DG.SB_BNODE n), e')) (* coerce to farsbnodes *)
546 :     end
547 :    
548 : blume 384 val impexplist = list impexpListM impexp
549 :    
550 : blume 330 fun r_exports () =
551 : blume 384 foldl SymbolMap.insert' SymbolMap.empty (impexplist ())
552 : blume 330
553 : blume 384 val stringlist = list stringListM string
554 : blume 330
555 : blume 384 fun privileges () =
556 :     StringSet.addList (StringSet.empty, stringlist ())
557 :    
558 : blume 306 val exports = r_exports ()
559 : blume 384 val required = privileges ()
560 : blume 308 val simap = genStableInfoMap (exports, group)
561 : blume 306 in
562 :     GG.GROUP { exports = exports,
563 : blume 348 kind = GG.STABLELIB simap,
564 : blume 306 required = required,
565 : blume 308 grouppath = group,
566 : blume 348 sublibs = sublibs }
567 : blume 306 end
568 : blume 304 in
569 : blume 361 SOME (SafeIO.perform { openIt = BinIO.openIn o mksname,
570 : blume 345 closeIt = BinIO.closeIn,
571 :     work = work,
572 :     cleanup = fn () => () })
573 :     handle Format => NONE
574 : blume 346 | IO.Io _ => NONE
575 : blume 345 end
576 : blume 304 end
577 : blume 309
578 :     end (* local *)

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