Class: MilkTea::DebugMap
- Inherits:
-
Object
- Object
- MilkTea::DebugMap
- Defined in:
- lib/milk_tea/tooling/debug_map.rb
Defined Under Namespace
Constant Summary collapse
- VERSION =
1
Instance Attribute Summary collapse
-
#binary_path ⇒ Object
readonly
Returns the value of attribute binary_path.
-
#functions ⇒ Object
readonly
Returns the value of attribute functions.
-
#program_source_path ⇒ Object
readonly
Returns the value of attribute program_source_path.
Class Method Summary collapse
- .absolute_path_string?(path) ⇒ Boolean
- .collect_locals(statements, locals = []) ⇒ Object
- .first_statement_line(statements) ⇒ Object
- .first_statement_source_path(statements) ⇒ Object
- .from_ir(ir_program, binary_path:) ⇒ Object
- .load(path) ⇒ Object
- .load_entries(entries) ⇒ Object
- .load_for_binary(binary_path) ⇒ Object
- .nested_statements(statement) ⇒ Object
- .path_for_payload(path, base_dir) ⇒ Object
- .path_from_payload(path, base_dir) ⇒ Object
- .sidecar_path_for(binary_path) ⇒ Object
Instance Method Summary collapse
- #function_for_c_name(c_name) ⇒ Object
-
#initialize(binary_path:, program_source_path:, functions:) ⇒ DebugMap
constructor
A new instance of DebugMap.
- #source_variable_for(function_c_name, source_name) ⇒ Object
- #to_h(base_dir: nil) ⇒ Object
- #variable_for(function_c_name, c_name) ⇒ Object
- #write(path) ⇒ Object
Constructor Details
#initialize(binary_path:, program_source_path:, functions:) ⇒ DebugMap
Returns a new instance of DebugMap.
39 40 41 42 43 44 45 46 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 39 def initialize(binary_path:, program_source_path:, functions:) @binary_path = binary_path ? File.(binary_path) : nil @program_source_path = program_source_path ? File.(program_source_path) : nil @functions = functions @functions_by_c_name = functions.each_with_object({}) do |function, memo| memo[function.linkage_name] ||= function end end |
Instance Attribute Details
#binary_path ⇒ Object (readonly)
Returns the value of attribute binary_path.
37 38 39 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 37 def binary_path @binary_path end |
#functions ⇒ Object (readonly)
Returns the value of attribute functions.
37 38 39 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 37 def functions @functions end |
#program_source_path ⇒ Object (readonly)
Returns the value of attribute program_source_path.
37 38 39 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 37 def program_source_path @program_source_path end |
Class Method Details
.absolute_path_string?(path) ⇒ Boolean
163 164 165 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 163 def self.absolute_path_string?(path) path.start_with?("/") || path.start_with?("\\\\") || path.match?(/\A[A-Za-z]:[\\\/]/) end |
.collect_locals(statements, locals = []) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 174 def collect_locals(statements, locals = []) Array(statements).each do |statement| case statement when IR::LocalDecl locals << Entry.new(name: statement.name.to_s, linkage_name: statement.linkage_name.to_s, line: statement.line) when IR::BlockStmt, IR::WhileStmt collect_locals(statement.body, locals) when IR::ForStmt collect_locals([statement.init], locals) collect_locals(statement.body, locals) collect_locals([statement.post], locals) when IR::IfStmt collect_locals(statement.then_body, locals) collect_locals(statement.else_body, locals) when IR::SwitchStmt statement.cases.each do |switch_case| collect_locals(switch_case.body, locals) end end end locals end |
.first_statement_line(statements) ⇒ Object
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 214 def first_statement_line(statements) Array(statements).each do |statement| if statement.respond_to?(:line) && statement.line return statement.line end nested = nested_statements(statement) nested.each do |branch| line = first_statement_line(branch) return line if line end end nil end |
.first_statement_source_path(statements) ⇒ Object
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 198 def first_statement_source_path(statements) Array(statements).each do |statement| if statement.respond_to?(:path) && statement.path return statement.path end nested = nested_statements(statement) nested.each do |branch| source_path = first_statement_source_path(branch) return source_path if source_path end end nil end |
.from_ir(ir_program, binary_path:) ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 83 def self.from_ir(ir_program, binary_path:) functions = ir_program.functions.map do |function| source_path = first_statement_source_path(function.body) || ir_program.path Function.new( name: function.name.to_s, linkage_name: function.linkage_name.to_s, path: source_path ? File.(source_path) : nil, line: first_statement_line(function.body), params: function.params.map { |param| Entry.new(name: param.name.to_s, linkage_name: param.linkage_name.to_s, line: nil) }, locals: collect_locals(function.body) ) end new(binary_path:, program_source_path: ir_program.path, functions:) end |
.load(path) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 52 def self.load(path) resolved_path = File.(path) base_dir = File.dirname(resolved_path) payload = JSON.parse(File.read(resolved_path)) functions = Array(payload["functions"]).map do |function| Function.new( name: function.fetch("name"), linkage_name: function.fetch("cName"), path: path_from_payload(function["sourcePath"], base_dir), line: function["line"], params: load_entries(function["params"]), locals: load_entries(function["locals"]) ) end new( binary_path: path_from_payload(payload["binaryPath"], base_dir), program_source_path: path_from_payload(payload["programSourcePath"], base_dir), functions: ) end |
.load_entries(entries) ⇒ Object
168 169 170 171 172 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 168 def load_entries(entries) Array(entries).map do |entry| Entry.new(name: entry.fetch("name"), linkage_name: entry.fetch("cName"), line: entry["line"]) end end |
.load_for_binary(binary_path) ⇒ Object
74 75 76 77 78 79 80 81 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 74 def self.load_for_binary(binary_path) path = sidecar_path_for(binary_path) return nil unless File.file?(path) load(path) rescue JSON::ParserError nil end |
.nested_statements(statement) ⇒ Object
230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 230 def nested_statements(statement) case statement when IR::BlockStmt, IR::WhileStmt [statement.body] when IR::ForStmt [[statement.init], statement.body, [statement.post]] when IR::IfStmt [statement.then_body, statement.else_body] when IR::SwitchStmt statement.cases.map(&:body) else [] end end |
.path_for_payload(path, base_dir) ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 142 def self.path_for_payload(path, base_dir) return nil unless path return path.tr("\\", "/") unless base_dir = File.(path) begin Pathname.new().relative_path_from(Pathname.new(base_dir)).to_s.tr("\\", "/") rescue ArgumentError .tr("\\", "/") end end |
.path_from_payload(path, base_dir) ⇒ Object
154 155 156 157 158 159 160 161 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 154 def self.path_from_payload(path, base_dir) return nil unless path return path if path.empty? return path if absolute_path_string?(path) return path unless base_dir File.(path, base_dir) end |
.sidecar_path_for(binary_path) ⇒ Object
48 49 50 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 48 def self.sidecar_path_for(binary_path) "#{File.(binary_path)}.mtdbg.json" end |
Instance Method Details
#function_for_c_name(c_name) ⇒ Object
106 107 108 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 106 def function_for_c_name(c_name) @functions_by_c_name[c_name.to_s] end |
#source_variable_for(function_c_name, source_name) ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 118 def source_variable_for(function_c_name, source_name) function = function_for_c_name(function_c_name) return nil unless function matches = (function.params + function.locals).select { |entry| entry.name == source_name.to_s } return nil if matches.empty? return matches.first if matches.map(&:linkage_name).uniq.one? nil end |
#to_h(base_dir: nil) ⇒ Object
130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 130 def to_h(base_dir: nil) payload = { "version" => VERSION, "functions" => functions.map { |function| function.to_h(base_dir:) }, } serialized_binary_path = self.class.path_for_payload(binary_path, base_dir) payload["binaryPath"] = serialized_binary_path if serialized_binary_path serialized_program_source_path = self.class.path_for_payload(program_source_path, base_dir) payload["programSourcePath"] = serialized_program_source_path if serialized_program_source_path payload end |
#variable_for(function_c_name, c_name) ⇒ Object
110 111 112 113 114 115 116 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 110 def variable_for(function_c_name, c_name) function = function_for_c_name(function_c_name) return nil unless function function.params.find { |entry| entry.linkage_name == c_name.to_s } || function.locals.find { |entry| entry.linkage_name == c_name.to_s } end |
#write(path) ⇒ Object
99 100 101 102 103 104 |
# File 'lib/milk_tea/tooling/debug_map.rb', line 99 def write(path) resolved_path = File.(path) base_dir = File.dirname(resolved_path) FileUtils.mkdir_p(base_dir) File.write(resolved_path, JSON.pretty_generate(to_h(base_dir:)) + "\n") end |