UNPKG

22 kBtext/coffeescriptView Raw
1kit = require "./kit"
2{Scope} = require "./scope"
3builder = require "./builder"
4assert = require "assert"
5config = require "./config"
6{VarUsage} = builder
7
8module.exports = root = {}
9
10# generates smart constructor for node
11root.regNodeType = regNodeType = (n, ctr) ->
12 root["#{n[0].toUpperCase()}#{n.substr(1)}"] = ctr
13 Scope.prototype[n] = (args...) ->
14 args.unshift null
15 nctr = Function::bind.apply(ctr,args)
16 nctr.name = ctr.name
17 res = (new nctr()).init(@)
18 res
19
20id = 0
21
22# common parent for effect-flow graph nodes
23class Node
24 constructor: ->
25 @unwindBy = {}
26 @id = id++
27 @vdeps = new VarUsage()
28 # copies some options and state from another node
29 morphInit: (other) ->
30 @setOrig(other.orig)
31 @vdeps = other.vdeps
32 @
33 # initialize node (called from its smart constructor)
34 init: (ctx) ->
35 @opts = kit.merge({},ctx.policy.opts)
36 @ctx = ctx
37 @vdeps.vars = ctx.vars
38 @vdeps.refs = ctx.refs
39 @
40 # sets JS AST statement representing original block
41 # translated into node, it is used as is if the node
42 # is pure
43 setOrig: (orig) ->
44 @orig = orig unless @orig?
45 @
46 # sets parents of the node
47 setParent: (@parent) -> @
48 # returns resulting list of JS AST statements
49 getFullBlock: (eff) ->
50 eff = eff and @opts.coerce is "none"
51 @getBuilder().toBlock(eff)
52 # returns resulting list of JS AST statements
53 getFullExpr: (eff) ->
54 eff = eff and @opts.coerce is "none"
55 @getBuilder().toExpr(eff)
56 # first translation pass
57 fwdPass: (use) ->
58 unless @_fwdPassDone
59 @_fwdPassDone = true
60 @vdeps.setBefore(use)
61 @_fwdPass(use)
62 kit.extend(use,@vdeps.uses)
63 @parent.vdeps.addInner(@vdeps) if @parent?
64 @
65 _fwdPass: (use) ->
66 # second translation pass, goes in reverse order
67 backPass: (use) ->
68 if @eff
69 @propagateEff()
70 kit.extend(@vdeps.after,use)
71 @_backPass(use)
72 kit.extend(use,@vdeps.uses)
73 @
74 _backPass: ->
75 # signals the node is effectful and update the information
76 # to all dependent nodes
77 propagateEff: ->
78 return @ if @_propagateEffDone
79 @_propagateEff()
80 _propagateEff: ->
81 @_propagateEffDone = true
82 @eff = true
83 @ctx.eff = true
84 @
85 # converts node into JS AST builder
86 getBuilder: ->
87 b = (do =>
88 return builder.empty() if @isEmpty()
89 unless @eff
90 return builder.pure(kit.blockStmts(@orig)) if @orig
91 @_getBuilder())
92 b.setOpts(@opts).mergeEnv(@vdeps)
93 # returns node having same effect but not returning any result
94 # (or same)
95 ignoreResult: ->
96 @ignoreResult = true
97 @
98
99root.Node = Node
100
101# common parent for effectful nodes
102class EffectfulNode extends Node
103 constructor: -> super()
104 eff: true
105
106# PureBlock should definitely not have effects,
107# all returns/throw/break/continue
108# parts MUST be in corresponding sub-nodes
109class PureNode extends Node
110 constructor: (@block) ->
111 super()
112 @block ?= []
113 eff: false
114 _getBuilder: -> builder.pure(@block)
115 pureBlock: -> @block
116
117# node representing pure JS expression
118class PureExprNode extends PureNode
119 constructor: (@pureExpr) ->
120 super [kit.ret(@pureExpr)]
121 setExpr: (@pureExpr) ->
122 @block = [kit.ret(@pureExpr)]
123 @
124 _getBuilder: -> builder.exprPure(@pureExpr)
125 ignoreResult: ->
126 @ctx.pureNode([kit.exprStmt(@pureExpr)]).morphInit(@)
127
128regNodeType "pureNode", PureNode
129regNodeType "pureExprNode", PureExprNode
130
131Scope::exprNode = (e) ->
132 (if @policy.opts.bind
133 new EffValNode(e)
134 else
135 new PureExprNode(e)).init(@)
136
137Scope::pureExprNode = (e) ->
138 new PureExprNode(e).init(@)
139
140# nothing (EmptyStatement)
141class EmptyNode extends PureNode
142 constructor: ->
143 super([])
144 @orig = {type:"EmptyStatement"}
145 setOrig: -> @
146 getBuilder: -> builder.empty().setOpts(@opts).mergeEnv(@vdeps)
147
148regNodeType "emptyNode", EmptyNode
149
150# either it is pure or not may be calculated only in runtime
151class DynNode extends Node
152 constructor: ->
153 super()
154 _propagateEff: ->
155 super()
156 if @parent?
157 @parent.propagateEff()
158 @
159
160# node representing `M.reify` call
161class ReifyNode extends Node
162 constructor: ->
163 super()
164 eff: false
165 setBody: (@body) ->
166 @body.setParent(@)
167 @
168 setIsThunk: (@isThunk) -> @
169 setVar: (@var) -> @
170 _backPass: (use) ->
171 @body.backPass(use)
172 _fwdPass: (use) ->
173 @body.fwdPass(use)
174 _propagateEff: -> @
175 getBuilder: ->
176 e = if @isThunk
177 @body.getBuilder().toExpr(true)
178 else
179 kit.fun([], @body.getBuilder().toBlock(true))
180 expr = kit.call(kit.packId("reify"),[e])
181 if @var
182 return builder.pure([kit.varDecl(@var, expr)]).mergeEnv(@vdeps)
183 return builder.exprPure(expr).mergeEnv(@vdeps)
184
185regNodeType "reifyNode", ReifyNode
186
187# represents ordered sequence of other nodes
188class BlockNode extends DynNode
189 constructor: ->
190 super()
191 @block = []
192 @placeholders = []
193 # adds children to the end of the sequence
194 append: (c...) ->
195 for i,x in c when i? and not i.isEmpty()
196 i.setParent(@)
197 if i.eff
198 @eff = true
199 @block.push i
200 @
201 _getBuilder: ->
202 cur = builder.empty()
203 {block} = @
204 for i,x in block
205 cur = cur.append(i.getBuilder())
206 cur
207 addPlaceholder: (e, p) ->
208 x = @block.indexOf e
209 assert.ok(x >= 0)
210 (@placeholders[x] ?= []).push p
211 _fwdPass: (use) ->
212 @placeholders = []
213 ouse = kit.extend({},use)
214 i.fwdPass(use) for i,x in @block
215 if @placeholders.length
216 c = @
217 for i, x in @placeholders when i by -1
218 b = c.block
219 n = b.splice(0,x)
220 nb = switch n.length
221 when 0 then @ctx.emptyNode()
222 when 1 then n[0]
223 else c = @ctx.blockNode().append(n...).fwdPass(ouse)
224 s = {}
225 j.setBlock(nb,b,s) for j in i
226 delete @placeholders
227 _backPass: (use) ->
228 eff = @eff
229 unless eff
230 for f in @block
231 if f.eff
232 eff = true
233 break
234 for i in @block by -1
235 i.backPass(use)
236 if eff
237 @_propagateEff()
238 @
239 last: -> @block[@block.length - 1]
240
241regNodeType "blockNode", BlockNode
242
243Node::getTail = -> @
244
245Scope::nodes = (arr...) ->
246 if arr.length is 1
247 return arr[0]
248 res = []
249 walk = (n) ->
250 if n instanceof BlockNode
251 for i in n.block
252 walk(i)
253 else
254 res.push n
255 walk(i) for i in arr
256 return @emptyNode() unless res.length
257 return res[0] if res.length is 1
258 return @blockNode().append(arr...)
259
260Node::append = (args...) ->
261 @ctx.nodes(@,args...)
262EmptyNode::append = (f,nxt...) ->
263 return f.append(nxt...) if nxt.length
264 return f
265PureNode::append = (f,nxt...) ->
266 if f.pureBlock?
267 @block.push(f.pureBlock()...)
268 return @append(nxt...) if nxt.length
269 return @
270 super(f,nxt...)
271
272Node::fullDeps = ->
273Node::setPosition = (@exprPos) -> @
274
275Node::fullDeps = (pos, deps) ->
276 if @exprPos
277 pos.push @exprPos
278 deps.push @
279
280PureNode::fullDeps = ->
281 if @exprPos
282 [p,px] = @exprPos
283 p[px] = kit.lit(null)
284
285PureExprNode::fullDeps = (pos,deps) ->
286 if @exprPos
287 if @vdeps.mod
288 pos.push @exprPos
289 deps.push(@)
290 else
291 [p,px] = @exprPos
292 p[px] = @pureExpr
293
294class EffValNode extends EffectfulNode
295 constructor: (@val) -> super()
296 ignoreResult: -> @noRes = true; @
297 _getBuilder: -> builder.exprEffCoerce(@val)
298
299regNodeType "effValNode", EffValNode
300
301lookupPlaceholderTarget = ->
302 i = @
303 loop
304 p = i.parent
305 break unless p?
306 if p.addPlaceholder?
307 p.addPlaceholder(i, @)
308 break
309 i = p
310 @
311
312# represents placeholder expression `M.$`
313class PlaceholderNode extends PureExprNode
314 constructor: (@arg) ->
315 super()
316 setExpr: (expr) ->
317 return super(expr) unless @arg
318 super(kit.mapply([expr],kit.fun([], [kit.ret @arg])))
319 setBlock: (n, b, s) ->
320 vn = s.vn
321 @depBlock = n
322 unless s.vn?
323 @prim = true
324 s.vn = vn = @ctx.uniqId("n")
325 b.unshift(@ctx.reifyNode().setBody(n).setVar(vn).setParent(@))
326 @ctx.refs[i] = true for i of n.vdeps.mods
327 @setExpr(vn)
328 _fwdPass: (use) ->
329 lookupPlaceholderTarget.call(@)
330
331Node::fillPlaceholder = (p) ->
332 @parent.fillPlaceholder(p, @)
333
334BlockNode::fillPlaceholder = (p) ->
335 {block} = @
336 x = block.indexOf(n)
337 assert.ok(x >= 0)
338
339regNodeType "placeholderNode", PlaceholderNode
340
341Node::setExprPos = (@exprPos) -> @
342
343# represents node with variables bound from another nodes
344class BindNode extends DynNode
345 constructor: (body,deps) ->
346 super()
347 @setDeps(deps)
348 @setBody(body)
349 bindNode: true
350 setBody: (body) ->
351 body.setParent(@) if body?
352 @body = body
353 @
354 # dependencies, their resulting values to be substituted at
355 # defined positions within `body`
356 setDeps: (deps) ->
357 for i in deps
358 i.setParent(@)
359 @deps = deps
360 @
361 _fullDeps: (pos, deps) ->
362 @body.fullDeps(pos,deps)
363 for i,x in @deps
364 i.fullDeps(pos,deps)
365 return
366 # returns positions along with dependencies
367 fullDeps: (pos,deps) ->
368 if @body.eff
369 super(pos,deps)
370 else @_fullDeps(pos,deps)
371 # returns builder for more than 1 effectful dependency
372 # overloaded for sequence builder
373 assemblePar: (deps, cur) -> builder.par(deps,cur)
374 _getBuilder: ->
375 pos = []
376 eargs = []
377 @_fullDeps(pos,eargs)
378 deps = []
379 cur = @body.getBuilder()
380 shallow = true
381 commit = =>
382 switch deps.length
383 when 0 then return cur
384 when 1 then cur = deps[0].append(cur)
385 else cur = @assemblePar(deps, cur)
386 deps = []
387 cur
388 for [p,px],x in pos by -1
389 e = eargs[x]
390 unless e.eff
391 if not e.vdeps.mod # or shallow
392 eb = e.getBuilder().toExprBuilder()
393 cur.pushEnv(eb.env)
394 p[px] = eb.expr
395 continue
396 if shallow
397 eb = e.getBuilder().toExprBuilder()
398 p[px] = eb.expr
399 nxt = commit()
400 cur = builder.pure([]).pushEnv(eb.env).setOpts(@opts).append(nxt)
401 continue
402 b = e.getBuilder().setBindVar(p[px] = @ctx.uniqId("b"))
403 if e.vdeps.mod
404 cur = b.append(commit())
405 else
406 deps.unshift(b)
407 shallow = false
408 commit()
409 ignoreResult: ->
410 super()
411 @setBody(@body.ignoreResult())
412 @
413 _backPass: (use) ->
414 @body.backPass(use)
415 for i in @deps by -1
416 i.backPass(use)
417 for i in @deps
418 eff = eff or i.eff
419 eff or= @eff or @body.eff
420 if eff
421 @propagateEff()
422 @
423 _fwdPass: (use) ->
424 {block} = @
425 for i in @deps
426 i.fwdPass(use)
427 @body.fwdPass(use)
428
429regNodeType "bindNode", BindNode
430
431simpleExpressions =
432 Literal: true
433 Identifier: true
434
435isSimpleExpr = (e) ->
436 simpleExpressions[e.type]
437
438# simple expression result bound to new generated variable
439class SharedNode extends DynNode
440 constructor: (def,body) ->
441 super()
442 @setDef(def)
443 @setBody(body)
444 @refs = []
445 setDef: (@def) ->
446 if @def
447 @def.setParent(@)
448 @eff = true if @def.eff
449 @
450 setBody: (@body) ->
451 if @body
452 @body.setParent(@)
453 @eff = true if @body.eff
454 @
455 init: (ctx) ->
456 if @def.pureExpr and isSimpleExpr(@def.pureExpr)
457 @_ref = @def.pureExpr
458 @_simplExpr = true
459 else
460 @_ref = @name = ctx.uniqId "s"
461 super(ctx)
462 _getBuilder: ->
463 return @body.getBuilder() if @_simplExpr
464 if @def.pureExpr?
465 builder.pure([kit.varDecl(@name,@def.pureExpr)])
466 .mergeEnv(@def.vdeps)
467 .append(@body.getBuilder())
468 else
469 builder.par([@def.getBuilder().setBindVar(@name)],@body.getBuilder())
470 ignoreResult: ->
471 super()
472 @setBody(@body.ignoreResult())
473 @
474 _backPass: (use) ->
475 @body.backPass(use)
476 @def.backPass(use)
477 eff = @eff or @body.eff or @def.eff
478 if eff
479 @propagateEff()
480 @
481 _fwdPass: (use) ->
482 {block} = @
483 @def.fwdPass(use)
484 @body.fwdPass(use)
485
486regNodeType "sharedNode", SharedNode
487
488# common parent for break, continue, return statements
489# and yield expression
490class JumpNode extends DynNode
491 constructor: (@dst,@kind) ->
492 super()
493 @vdeps.skipMods()
494 setDst: (@dst) -> @
495 _fwdPass: (use) ->
496 @val.fwdPass(use) if @val
497 i = @
498 loop
499 i = i.parent
500 break if not i? or i is @dst
501 i.unwindBy[@id] = @
502 return
503
504# represents break, continue or return statement
505class BreakNode extends JumpNode
506 constructor: (dst, val, name) ->
507 super(dst, name ? "brk")
508 @setVal(val)
509 setVal: (@val) ->
510 @val.setParent(@) if @val
511 @
512 _getBuilder: ->
513 if @ret
514 return @val.getBuilder() if @val
515 return builder.empty()
516 lab = @dst.getLabel()
517 if @val
518 vexpr = @val.getBuilder().coerceObj().toExpr() if @val?
519 expr = if @val.eff
520 kit.mbind([vexpr],lab)
521 else
522 kit.call(lab,[vexpr])
523 else
524 thread = @dst.thread
525 if thread.length > 1
526 thread = [kit.arr(thread)]
527 expr = kit.call(lab,thread)
528 return builder.exprEff(expr).noCapture().setBrk(true)
529 _backPass: (use) ->
530 eff = @eff
531 if @val
532 @val.backPass(use)
533 eff or= @val.eff
534 if eff
535 @propagateEff()
536 @
537
538# represents yield or M.yield expressions
539class YieldNode extends BreakNode
540 constructor: (dst,val) ->
541 super(dst,val)
542 @eff = true
543 ignoreResult: -> @
544 setBlock: (@block) -> @
545 _getBuilder: ->
546 b = @block.getBuilder().append(super())
547 .setOpts(@opts).mergeEnv(@vdeps).toExprBuilder()
548 return builder.exprEff(kit.call(kit.mem(b.expr,kit.id("mopt")),[])).morph(b)
549 _fwdPass: (use) ->
550 @dst.opts.keepScope = true
551 lookupPlaceholderTarget.call(@)
552 _backPass: (use) ->
553 @propagateEff()
554 @block.backPass(use)
555
556regNodeType "breakNode", BreakNode
557regNodeType "yieldNode", YieldNode
558
559# represents various branching nodes, such as `if` statements or loops etc
560class BranchedNode extends DynNode
561 constructor: (@fun, @template) ->
562 super()
563 @deps = []
564 setDeps: (deps...) ->
565 cur = @deps
566 cur.length = 0
567 if deps?
568 for i in deps
569 i.setParent(@)
570 cur.push i
571 @
572 _getItemBuilder: (i) ->
573 i.getBuilder()
574 _getBuilder: ->
575 {eff} = @
576 threadOut = {}
577 nenv = {}
578 for i in @deps
579 i.vdeps.threadOutMap(threadOut)
580 blocks = []
581 needCoerce = false
582 for i in @deps
583 cb = @_getItemBuilder(i)
584 cb = cb.addThread(threadOut)
585 bn = cb.capture().toBlockBuilder(eff)
586 needCoerce or= bn._needCoerce is true
587 for j, v of bn.env
588 w = nenv[j] ?= {}
589 w.use = true if v.use
590 w.set = true if v.set
591 w.mod = true if v.mod
592 w.thread = true if threadOut[j]
593 blocks.push(kit.flatBlock(bn.block))
594 block = builder.block(eff, [@fun(blocks...)])
595 .pushEnv(nenv)
596 .noCapture()
597 .needCoerce(needCoerce ? false)
598 return block
599 _propagateEff: ->
600 super()
601 {deps} = @
602 for i,x in deps
603 deps[x] = i.propagateEff()
604 @
605 _backPass: (use) ->
606 unless @eff
607 for i in @deps when i.eff
608 @eff = true
609 break
610 if @eff
611 @_propagateEff()
612 for i in @deps
613 i.backPass(kit.extend({},use))
614 for i in @deps
615 kit.extend(use,i.vdeps.uses)
616 @
617 _fwdPass: (use) ->
618 for i in @deps
619 i.fwdPass(kit.extend({},use))
620 return
621
622regNodeType "branchedNode", BranchedNode
623
624# common parent for simple combinators with one inner sub node (body)
625class WrapNode extends DynNode
626 constructor: (body) ->
627 super()
628 @setBody(body)
629 setBody: (@body) ->
630 @body.setParent(@) if @body?
631 @
632 _backPass: (use) ->
633 if @eff or @body.eff
634 @_propagateEff()
635 @body.backPass(use)
636 @
637 _fwdPass: (use) ->
638 @body.fwdPass(use)
639
640# returns a function with threaded variables representing the node
641getBuilderFun = (node,ctx) ->
642 thread = node.vdeps.threadOutMap()
643 b = node.getBuilder()
644 b.addThread(thread) if thread?
645 b = b.capture().toBlockBuilder()
646 thread[i] = true for i of b.env when i.thread
647 vars = (i for i of thread).sort().map(kit.id)
648 fun = kit.spreadFun(vars,kit.fun(vars, b.block))
649 return [fun,vars,b,thread]
650
651# represents M.repeat
652class RepeatNode extends WrapNode
653 constructor: (body) -> super(body)
654 _getBuilder: ->
655 [fun,vars,b] = getBuilderFun(@body,@ctx)
656 if vars.length > 1
657 vars = [kit.arr(vars)]
658 expr = kit.call(kit.packId("repeat"),[fun,vars...])
659 builder.exprEff(expr).morph(b).mergeEnv(@vdeps).noCapture()
660 _fwdPass: (use) ->
661 @body.fwdPass({})
662 _backPass: (use) ->
663 use[i] = true for i of @body.vdeps.uses
664 super(use)
665
666regNodeType "repeatNode", RepeatNode
667
668# M.block or M.scope for encoding break, continue, return statements
669class ControlNode extends WrapNode
670 constructor: (@kind) ->
671 @exits = []
672 @kind ?= "block"
673 @labCount = 0
674 @thread = []
675 super()
676 setLabel: (lab) ->
677 lab ?= "l"
678 @label = @ctx.uniqId(lab)
679 @
680 getLabel: ->
681 return @label if @label?
682 @label = @ctx.uniqId("l")
683 breakNode: (v) ->
684 res = @ctx.breakNode(@,v)
685 @exits.push res
686 res
687 yieldNode: (v) ->
688 res = @ctx.yieldNode(@,v)
689 @exits.push res
690 res
691 trimExits: (loopBody) ->
692 if not @opts.keepScope
693 if @exits.length is 1
694 i = j = @exits[0]
695 loop
696 k = i
697 i = i.parent
698 if i is @ or i is loopBody
699 j.ret = true
700 @exits.length = 0
701 break
702 if i instanceof BlockNode
703 x = i.block.indexOf(k)
704 return @ if x < 0
705 i.block.splice(x+1,i.block.length)
706 continue
707 return unless i instanceof BindNode
708 return
709 _getBuilder: ->
710 @trimExits()
711 return @body.getBuilder() unless @exits.length and @eff
712 unless @kind is "scope"
713 thread = @body.vdeps.threadOutMap()
714 @thread = (i for i of thread).sort().map(kit.id)
715 else
716 @thread = []
717 b = @body.getBuilder()
718 b = b.capture().toBlockBuilder()
719 bodyBlock = b.block
720 builder.exprEff(
721 kit.call(kit.packId(@kind),[kit.fun([@getLabel()], bodyBlock)])
722 ).morph(b).mergeEnv(@vdeps).noCapture()
723 _propagateEff: ->
724 super()
725 for i in @exits
726 i.propagateEff()
727 @
728 _backPass: (use) ->
729 @body.backPass(use)
730 unless @kind is "scope"
731 #cuses = @body.vdeps.mods
732 for i in @exits
733 i.vdeps.after[j] = true for j,v of @vdeps.after when v
734 return
735
736regNodeType "controlNode", ControlNode
737
738# helper for different loop generation strategies
739class LoopNode extends ControlNode
740 constructor: -> super()
741 simpleLoop: ->
742 return unless @pre
743 return if @opts.loop is "seq"
744 @cont.trimExits()
745 return if @cont.exits.length
746 return if @update? and @update.eff
747 return if @test.eff or @test.vdeps.upd
748 return if @inner.vdeps.upd
749 return for i,v of @inner.unwindBy when v.dst isnt @ and v.dst isnt @cont
750 @trimExits(@inner.parent)
751 return if @exits.length
752 [update, updVars, b, thread] = getBuilderFun(@update, @ctx)
753 tb = @test.getBuilder().toBlockBuilder().capture()
754 test = kit.spreadFun(updVars, kit.fun(updVars, tb.block))
755 bb = @inner.getBuilder()
756 bb = bb.capture().toBlockBuilder()
757 body = kit.spreadFun(updVars, kit.fun(updVars, bb.block))
758 v.mod = false for i, v of b.env when v.mod
759 updVars = [kit.arr(updVars)] if updVars.length > 1
760 return builder.exprEff(
761 kit.call(kit.packId("forPar"),[test,body,update,updVars...])
762 ).morph(b).mergeEnv(@vdeps).noCapture()
763 _getBuilder: ->
764 res = @simpleLoop()
765 return res if res?
766 super()
767
768regNodeType "loopNode", LoopNode
769
770# try...catch...finally block representation
771class TryCatchNode extends DynNode
772 constructor: ->
773 super()
774 @throws = []
775 setBody: (b) ->
776 @body = b
777 b.setParent(@) if b?
778 @
779 setHandler: (@hvar, h) ->
780 @handler = h
781 h.setParent(@) if h?
782 @
783 setFin: (f) ->
784 @fin = f
785 f.setParent(@) if f?
786 @
787 _getBuilder: ->
788 return @body.getBuilder() unless @handler? or @fin?
789 bb = @body.getBuilder().coerceTry().toExprBuilder(true)
790 e = bb.expr
791 if @handler?
792 hb = @handler.getBuilder().toBlockBuilder(true)
793 e = kit.catch(bb.expr,@hvar,hb.block)
794 if @fin?
795 fb = @fin.getBuilder().toBlockBuilder(true)
796 e = kit.finally(e,fb.block)
797 res = builder.exprEff(e).pushEnv(bb.env)
798 res.pushEnv(hb.env) if hb?
799 res.pushEnv(fb.env) if fb?
800 res
801 _fwdPass: (use) ->
802 @body.fwdPass(use)
803 if @handler
804 @handler.fwdPass(use)
805 hdeps = @handler.vdeps.uses
806 else
807 hdeps = {}
808 if @fin
809 @fin.fwdPass(use)
810 fdeps = @fin.vdeps.uses
811 else
812 fdeps = {}
813 for i of @body.vdeps.mods when fdeps[i] or hdeps[i]
814 @ctx.refs[i] = true
815 return
816 _propagateEff: ->
817 super()
818 i.propagateEff() for i in @throws
819 @
820 _backPass: (use) ->
821 #return @ if @_propagateEffDone
822 unless @eff
823 @eff = @body.eff or @handler? and @handler.eff or @fin and @fin.eff
824 if @eff
825 @_propagateEff()
826 @fin.backPass(use) if @fin?
827 @handler.backPass(use) if @handler?
828 @body.backPass(use)
829
830regNodeType "tryCatchNode", TryCatchNode
831
832# throw statement representation
833class ThrowNode extends DynNode
834 constructor: (val) ->
835 super()
836 @setVal(val)
837 setVal: (val) ->
838 @val = val.setParent(@)
839 @
840 _backPass: (use) ->
841 super()
842 unless @eff
843 @eff = @val.eff
844 if @eff
845 @_propagateEff()
846 @val.backPass(use)
847 @
848 _fwdPass: (use) ->
849 p = @parent
850 l = @
851 loop
852 if p.throws? and p.body is l
853 @dst = p
854 break
855 l = p
856 p = p.parent
857 @dst.throws.push @
858 @val.fwdPass(use)
859 @
860 _getBuilder: ->
861 assert.ok(@eff)
862 v = @ctx.uniqId("e")
863 if @val.pureExpr
864 return builder.exprEff(kit.call(kit.packId("raise"), [@val.pureExpr]))
865 @val.getBuilder().setBindVar(v).setOpts(@opts)
866 .append(builder.exprEff(kit.call(kit.packId("raise"), [v]))
867 .setOpts(@opts))
868
869regNodeType "throwNode", ThrowNode
870
871Node::tryCatch = (hv,h,f) ->
872 @ctx.tryCatchNode().setBody(@)
873 .setHandler(hv,h)
874 .setFin(f)
875
876Scope::stmtNode = (s) ->
877 return EmptyNode() unless s?
878 sv = @stmt s
879 return sv if sv?
880 return PureNode([s])
881
882Node::isEmpty = -> false
883BlockNode::isEmpty = -> not @block.length
884EmptyNode::isEmpty = -> true