Skip to content

Commit 32637a1

Browse files
committed
Add support for reading GOTO functions from JSON
Adds a parser for JSON-encoded `goto_functionst` and support to `symtab2gb` to optionally read such from a file and store them in `goto_functions` of a `goto_modelt`.
1 parent 5777e16 commit 32637a1

16 files changed

+1521
-4
lines changed

doc/man/symtab2gb.1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ https://github.com/model-checking/kani).
1919
\fB\-\-out\fR \fIoutfile\fR
2020
Specify the filename of the resulting binary (default: a.out)
2121
.TP
22+
\fB\-\-goto\-functions\fR \fIfile\fR
23+
Specify a file containing JSON-encoded goto functions
24+
.TP
2225
\fB\-\-verbosity\fR #
2326
verbosity level
2427
.SH ENVIRONMENT

src/json-symtab-language/Makefile

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
SRC = json_symtab_language.cpp \
2-
json_symbol_table.cpp \
3-
json_symbol.cpp
1+
SRC = json_goto_function.cpp \
2+
json_goto_functions.cpp \
3+
json_symtab_language.cpp \
4+
json_symbol_table.cpp \
5+
json_symbol.cpp \
6+
# Empty last line
47

58
INCLUDES= -I ..
69

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
/*******************************************************************\
2+
3+
Module: JSON goto_function deserialization
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// JSON goto_function deserialization
11+
12+
#include "json_goto_function.h"
13+
14+
#include <util/exception_utils.h>
15+
#include <util/expr.h>
16+
#include <util/json_irep.h>
17+
#include <util/string2int.h>
18+
19+
#include "json_symbol.h"
20+
21+
/// Return code at "code" key in a JSON object, if any.
22+
/// \param json: The json object to pick the value from.
23+
/// \return An expression, unless an exception was thrown before.
24+
static goto_instruction_codet try_get_code(const json_objectt &json)
25+
{
26+
auto code_it = json.find("code");
27+
if(code_it == json.end())
28+
throw deserialization_exceptiont{
29+
"instruction_from_json: key 'code' not found"};
30+
31+
json_irept json2irep{true};
32+
irept code_irep = json2irep.convert_from_json(code_it->second);
33+
return static_cast<goto_instruction_codet &>(code_irep);
34+
}
35+
36+
/// Return code at "code" key in a JSON object, if any.
37+
/// \param json: The json object to pick the value from.
38+
/// \param code_id: The expected code type
39+
/// \return An expression, unless an exception was thrown before.
40+
static goto_instruction_codet
41+
try_get_code(const json_objectt &json, const irep_idt &code_id)
42+
{
43+
goto_instruction_codet code = try_get_code(json);
44+
if(code.get_statement() != code_id)
45+
{
46+
throw deserialization_exceptiont{
47+
"instruction_from_json: expected '" + id2string(code_id) + "', got '" +
48+
id2string(code.get_statement()) + "'"};
49+
}
50+
return code;
51+
}
52+
53+
/// Return expression at "guard" key in a JSON object, if any.
54+
/// \param json: The json object to pick the value from.
55+
/// \return An expression, unless an exception was thrown before.
56+
static exprt try_get_guard(const json_objectt &json)
57+
{
58+
auto guard_it = json.find("guard");
59+
if(guard_it == json.end())
60+
throw deserialization_exceptiont{
61+
"instruction_from_json: key 'guard' not found"};
62+
63+
json_irept json2irep{true};
64+
irept guard_irep = json2irep.convert_from_json(guard_it->second);
65+
return static_cast<exprt &>(guard_irep);
66+
}
67+
68+
/// Deserialize a goto_programt::instructiont from JSON
69+
/// \param json: The JSON object representing an instruction
70+
/// \return A goto_programt::instructiont
71+
static goto_programt::instructiont
72+
instruction_from_json(const json_objectt &json)
73+
{
74+
auto id_it = json.find("instructionId");
75+
if(id_it == json.end())
76+
{
77+
throw deserialization_exceptiont{
78+
"instruction_from_json: key 'instructionId' not found"};
79+
}
80+
auto instruction_type = try_get_string(id_it->second, "instructionId");
81+
82+
source_locationt source_location = source_locationt::nil();
83+
auto location_it = json.find("sourceLocation");
84+
if(location_it != json.end())
85+
source_location = try_get_source_location(location_it->second);
86+
87+
if(instruction_type == "GOTO")
88+
{
89+
return goto_programt::make_incomplete_goto(
90+
try_get_guard(json), source_location);
91+
}
92+
else if(instruction_type == "ASSUME")
93+
{
94+
return goto_programt::make_assumption(try_get_guard(json), source_location);
95+
}
96+
else if(instruction_type == "ASSERT")
97+
{
98+
return goto_programt::make_assertion(try_get_guard(json), source_location);
99+
}
100+
else if(instruction_type == "OTHER")
101+
{
102+
return goto_programt::make_other(try_get_code(json), source_location);
103+
}
104+
else if(instruction_type == "SKIP")
105+
{
106+
return goto_programt::make_skip(source_location);
107+
}
108+
else if(instruction_type == "START_THREAD")
109+
{
110+
return goto_programt::instructiont{
111+
static_cast<const goto_instruction_codet &>(get_nil_irep()),
112+
source_location,
113+
START_THREAD,
114+
nil_exprt{},
115+
{}};
116+
}
117+
else if(instruction_type == "END_THREAD")
118+
{
119+
return goto_programt::instructiont{
120+
static_cast<const goto_instruction_codet &>(get_nil_irep()),
121+
source_location,
122+
END_THREAD,
123+
nil_exprt{},
124+
{}};
125+
}
126+
else if(instruction_type == "LOCATION")
127+
{
128+
return goto_programt::make_location(source_location);
129+
}
130+
else if(instruction_type == "END_FUNCTION")
131+
{
132+
return goto_programt::make_end_function(source_location);
133+
}
134+
else if(instruction_type == "ATOMIC_BEGIN")
135+
{
136+
return goto_programt::make_atomic_begin(source_location);
137+
}
138+
else if(instruction_type == "ATOMIC_END")
139+
{
140+
return goto_programt::make_atomic_end(source_location);
141+
}
142+
else if(instruction_type == "SET_RETURN_VALUE")
143+
{
144+
return goto_programt::make_set_return_value(
145+
to_code_return(try_get_code(json, ID_return)).return_value(),
146+
source_location);
147+
}
148+
else if(instruction_type == "ASSIGN")
149+
{
150+
return goto_programt::make_assignment(
151+
to_code_assign(try_get_code(json, ID_assign)), source_location);
152+
}
153+
else if(instruction_type == "DECL")
154+
{
155+
return goto_programt::make_decl(
156+
to_code_decl(try_get_code(json, ID_decl)), source_location);
157+
}
158+
else if(instruction_type == "DEAD")
159+
{
160+
return goto_programt::instructiont{
161+
to_code_dead(try_get_code(json, ID_dead)),
162+
source_location,
163+
DEAD,
164+
nil_exprt{},
165+
{}};
166+
}
167+
else if(instruction_type == "FUNCTION_CALL")
168+
{
169+
return goto_programt::make_function_call(
170+
to_code_function_call(try_get_code(json, ID_function_call)),
171+
source_location);
172+
}
173+
else if(instruction_type == "THROW")
174+
{
175+
return goto_programt::make_throw(source_location);
176+
}
177+
else if(instruction_type == "CATCH")
178+
{
179+
return goto_programt::make_catch(source_location);
180+
}
181+
else
182+
{
183+
throw deserialization_exceptiont{
184+
"instruction_from_json: got unexpected instructionId '" +
185+
instruction_type + "'"};
186+
}
187+
}
188+
189+
/// Deserialize a goto_programt from JSON
190+
/// \param json: The JSON object representing a goto_program
191+
/// \return A goto_programt
192+
static goto_programt goto_program_from_json(const jsont &json)
193+
{
194+
if(!json.is_array())
195+
throw deserialization_exceptiont{"goto_program_from_json takes an array"};
196+
197+
goto_programt program;
198+
199+
// First pass: create all instructions
200+
std::map<unsigned, goto_programt::targett> target_map;
201+
for(const auto &instruction_json : to_json_array(json))
202+
{
203+
if(!instruction_json.is_object())
204+
throw deserialization_exceptiont{"instruction_from_json takes an object"};
205+
206+
const json_objectt &json_object = to_json_object(instruction_json);
207+
auto location_number_it = json_object.find("locationNumber");
208+
std::optional<unsigned> loc_unsigned;
209+
if(
210+
location_number_it != json_object.end() &&
211+
location_number_it->second.is_number())
212+
{
213+
loc_unsigned = string2optional_unsigned(location_number_it->second.value);
214+
}
215+
if(!loc_unsigned.has_value())
216+
{
217+
throw deserialization_exceptiont{
218+
"goto_program_from_json: key 'locationNumber' not found or does not "
219+
"map to an unsigned number"};
220+
}
221+
222+
program.add(instruction_from_json(json_object));
223+
auto new_key =
224+
target_map.insert({*loc_unsigned, std::prev(program.instructions.end())})
225+
.second;
226+
if(!new_key)
227+
{
228+
throw deserialization_exceptiont{
229+
"goto_program_from_json: duplicate locationNumber " +
230+
location_number_it->second.value};
231+
}
232+
}
233+
234+
// Second pass: resolve targets
235+
goto_programt::targett instruction_it = program.instructions.begin();
236+
for(const auto &instruction_json : to_json_array(json))
237+
{
238+
for(const auto &kv : to_json_object(instruction_json))
239+
{
240+
if(
241+
kv.first == "code" || kv.first == "guard" ||
242+
kv.first == "instruction" || kv.first == "instructionId" ||
243+
kv.first == "locationNumber" || kv.first == "sourceLocation")
244+
{
245+
continue;
246+
}
247+
else if(kv.first == "labels")
248+
{
249+
if(!kv.second.is_array())
250+
throw deserialization_exceptiont{"labels must be an array"};
251+
252+
for(const auto &label : to_json_array(kv.second))
253+
{
254+
if(!label.is_string())
255+
throw deserialization_exceptiont{"label must be a string"};
256+
instruction_it->labels.push_back(label.value);
257+
}
258+
}
259+
else if(kv.first == "targets")
260+
{
261+
if(!kv.second.is_array())
262+
throw deserialization_exceptiont{"targets must be an array"};
263+
264+
for(const auto &target : to_json_array(kv.second))
265+
{
266+
std::optional<unsigned> target_unsigned =
267+
string2optional_unsigned(target.value);
268+
if(!target.is_number() || !target_unsigned.has_value())
269+
throw deserialization_exceptiont{
270+
"target must be an unsigned number"};
271+
auto target_it = target_map.find(*target_unsigned);
272+
if(target_it == target_map.end())
273+
throw deserialization_exceptiont{"target not in function"};
274+
instruction_it->targets.push_back(target_it->second);
275+
}
276+
277+
if(
278+
!instruction_it->is_incomplete_goto() ||
279+
instruction_it->targets.empty())
280+
{
281+
throw deserialization_exceptiont{
282+
"goto_program_from_json: invalid targets entry"};
283+
}
284+
goto_programt::targett target = instruction_it->targets.back();
285+
instruction_it->targets.pop_back();
286+
instruction_it->complete_goto(target);
287+
}
288+
else
289+
{
290+
throw deserialization_exceptiont{
291+
"goto_program_from_json: unexpected key '" + kv.first + "'"};
292+
}
293+
}
294+
295+
++instruction_it;
296+
}
297+
298+
return program;
299+
}
300+
301+
goto_functiont goto_function_from_json(const json_objectt &json)
302+
{
303+
goto_functiont result;
304+
305+
for(const auto &kv : json)
306+
{
307+
if(kv.first == "instructions")
308+
{
309+
result.body = goto_program_from_json(kv.second);
310+
}
311+
else if(kv.first == "parameterIdentifiers")
312+
{
313+
if(!kv.second.is_array())
314+
{
315+
throw deserialization_exceptiont{
316+
"parameterIdentifiers must be an array"};
317+
}
318+
319+
for(const auto &param : to_json_array(kv.second))
320+
{
321+
if(!param.is_string())
322+
{
323+
throw deserialization_exceptiont{
324+
"parameter identifier must be a string"};
325+
}
326+
result.parameter_identifiers.push_back(param.value);
327+
}
328+
}
329+
else if(kv.first == "isHidden")
330+
{
331+
if(try_get_bool(kv.second, "isHidden"))
332+
result.make_hidden();
333+
}
334+
else if(kv.first == "name")
335+
{
336+
// ignored, already processed by goto_functions_from_json
337+
}
338+
else if(kv.first == "isBodyAvailable" || kv.first == "isInternal")
339+
{
340+
// ignored, computed at runtime
341+
}
342+
else
343+
{
344+
throw deserialization_exceptiont{
345+
"goto_function_from_json: unexpected key '" + kv.first + "'"};
346+
}
347+
}
348+
349+
return result;
350+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*******************************************************************\
2+
3+
Module: JSON goto_function deserialization
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// JSON goto_function deserialization
11+
12+
#ifndef CPROVER_JSON_SYMTAB_LANGUAGE_JSON_GOTO_FUNCTION_H
13+
#define CPROVER_JSON_SYMTAB_LANGUAGE_JSON_GOTO_FUNCTION_H
14+
15+
#include <goto-programs/goto_function.h>
16+
17+
class json_objectt;
18+
19+
/// Deserialize a goto_functiont from JSON
20+
/// \param json: The JSON object representing a goto_function
21+
/// \return A goto_functiont
22+
goto_functiont goto_function_from_json(const json_objectt &json);
23+
24+
#endif // CPROVER_JSON_SYMTAB_LANGUAGE_JSON_GOTO_FUNCTION_H

0 commit comments

Comments
 (0)