-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcheck.rb
145 lines (125 loc) · 3.37 KB
/
check.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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
require_relative "common"
module Checker
class FuncallChecker
def initialize
@exit_status = 0
end
def self.run(tree)
new.check(tree)
end
def collect_fn_sigs(top_stmts)
top_stmts
.map { |fn| [fn[1], fn[2]] }
.to_h
end
def _check(fn_sigs, nodes)
if nodes[0] == "funcall" || nodes[0] == "call"
fn_name = nodes[1]
if fn_sigs.key?(fn_name)
num_args_exp = fn_sigs[fn_name].size
num_args_act = nodes.size - 2
if num_args_act != num_args_exp
$stderr.puts(
format(
"ERROR: wrong number of arguments: function %s (given %d, expected %d)",
fn_name,
num_args_act,
num_args_exp
)
)
$stderr.puts " expected arguments: " + fn_sigs[fn_name].join(", ")
@exit_status = 1
end
else
$stderr.puts "ERROR: undefined function: " + fn_name
@exit_status = 1
end
end
nodes.each do |node|
if node.is_a?(Array)
_check(fn_sigs, node)
end
end
end
def check(tree)
top_stmts = tree[1..-1]
fn_sigs = collect_fn_sigs(top_stmts)
# builtin functions
fn_sigs["getchar"] = []
fn_sigs["write" ] = ["char", "fd"]
fn_sigs["get_sp" ] = []
fn_sigs["_panic" ] = []
fn_sigs["_debug" ] = []
fn_sigs["set_vram" ] = ["vram_addr", "value"]
fn_sigs["get_vram" ] = ["vram_addr"]
_check(fn_sigs, tree)
if @exit_status != 0
exit @exit_status
end
end
end
def self.find_func_def(tree, name)
tree[1..]
.find { |top_stmt|
head, _name, _ = top_stmt
head == "func" && _name == name
}
end
def self.check_total_string_size(tree)
require "pp"
# ["func", "GS_STRINGS", [], [["return", 20]]]
func_def = find_func_def(tree, "GS_STRINGS")
return if func_def.nil?
_, _, _, stmts = func_def
_, retval = stmts[0]
def_size = retval
# [ "func", "init_strings", [],
# [ ["var", "offset_", ["+", "g_main_", ["funcall", "GO_STRINGS"]]],
# ["set", ["deref", ["+", "offset_", 0]], 72],
# ["set", ["deref", ["+", "offset_", 1]], 101],
# ...
# ]]
_, _, _, stmts = find_func_def(tree, "init_strings")
actual_size = stmts.size
if actual_size >= def_size
raise "total string size is too large: #{actual_size} >= #{def_size}"
end
end
def self.check_gvar_width(file)
gs_total = 0
gs_total += 1 # alloc cursor
declared_size = nil
File.read(file).each_line { |line|
case line
when /^def GS_.+ return (\d+);/
gs_total += $1.to_i
when /var \[(\d+)\]g;/
declared_size = $1.to_i
end
}
if declared_size
if declared_size != gs_total
raise "ERROR: #{file}: total (#{gs_total}) declared (#{declared_size})"
end
else
# OK: グローバル変数を使っていない
end
end
end
cmd = ARGV.shift
case cmd
when "fn-sig"
require "json"
file = ARGV[0]
tree = JSON.parse(File.read(file))
Checker::FuncallChecker.run(tree)
when "gvar-width"
file = ARGV[0]
Checker.check_gvar_width(file)
when "string-size"
file = ARGV[0]
tree = JSON.parse(File.read(file))
Checker.check_total_string_size(tree)
else
raise "invalid command"
end