/
runtime_annotation.rb
55 lines (48 loc) · 1.72 KB
/
runtime_annotation.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
module Laser
module Analysis
# This is a simple inherited attribute that specifies whether a given node
# will be executed when at load-time or at run-time. In short, method bodies
# and not-run blocks at the top-level are not run, and everything else is.
#
# The possible values of #runtime are :load, :run, or :unknown.
class RuntimeAnnotation < BasicAnnotation
add_property :runtime
def annotate!(root)
@current_runtime = :load
super
end
def default_visit(node)
node.runtime = @current_runtime
visit_children(node)
end
def visit_with_runtime(*nodes, new_runtime)
old_runtime, @current_runtime = @current_runtime, new_runtime
nodes.each { |node| visit node }
ensure
@current_runtime = old_runtime
end
add :def do |node, name, params, body|
default_visit(node)
default_visit(name)
visit_with_runtime(params, body, :run)
end
add :defs do |node, singleton, separator, name, params, body|
default_visit(node)
default_visit(singleton)
default_visit(name)
visit_with_runtime(params, body, :run)
end
add :method_add_block do |node, call, block|
# TODO(adgar): Check if call is resolved, and if so, check if the block is in fact
# executed immediately or stored. If we know the answer to that, we can specify :run
# or :load for the block instead of just :unknown.
default_visit(node)
default_visit(call)
if @current_runtime == :load
then visit_with_runtime(block, :unknown)
else default_visit block
end
end
end
end
end