/src/jsonnet/core/libjsonnet.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Copyright 2015 Google Inc. All rights reserved. |
3 | | |
4 | | Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | you may not use this file except in compliance with the License. |
6 | | You may obtain a copy of the License at |
7 | | |
8 | | http://www.apache.org/licenses/LICENSE-2.0 |
9 | | |
10 | | Unless required by applicable law or agreed to in writing, software |
11 | | distributed under the License is distributed on an "AS IS" BASIS, |
12 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | See the License for the specific language governing permissions and |
14 | | limitations under the License. |
15 | | */ |
16 | | |
17 | | #include <cerrno> |
18 | | #include <cstdlib> |
19 | | #include <cstring> |
20 | | |
21 | | #include <exception> |
22 | | #include <fstream> |
23 | | #include <iostream> |
24 | | #include <sstream> |
25 | | #include <string> |
26 | | |
27 | | extern "C" { |
28 | | #include "libjsonnet.h" |
29 | | #include "libjsonnet_fmt.h" |
30 | | } |
31 | | |
32 | | #include "desugarer.h" |
33 | | #include "formatter.h" |
34 | | #include "json.h" |
35 | | #include "parser.h" |
36 | | #include "static_analysis.h" |
37 | | #include "vm.h" |
38 | | |
39 | | namespace { |
40 | | using ::jsonnet::internal::Allocator; |
41 | | using ::jsonnet::internal::AST; |
42 | | using ::jsonnet::internal::FmtOpts; |
43 | | using ::jsonnet::internal::Fodder; |
44 | | using ::jsonnet::internal::jsonnet_lex; |
45 | | using ::jsonnet::internal::RuntimeError; |
46 | | using ::jsonnet::internal::StaticError; |
47 | | using ::jsonnet::internal::Tokens; |
48 | | using ::jsonnet::internal::VmExt; |
49 | | using ::jsonnet::internal::VmNativeCallback; |
50 | | using ::jsonnet::internal::VmNativeCallbackMap; |
51 | | } // namespace |
52 | | |
53 | | static void memory_panic(void) |
54 | 0 | { |
55 | 0 | fputs("FATAL ERROR: a memory allocation error occurred.\n", stderr); |
56 | 0 | abort(); |
57 | 0 | } |
58 | | |
59 | | static char *from_string(JsonnetVm *vm, const std::string &v) |
60 | 534 | { |
61 | 534 | char *r = jsonnet_realloc(vm, nullptr, v.length() + 1); |
62 | 534 | std::memcpy(r, v.c_str(), v.length() + 1); |
63 | 534 | return r; |
64 | 534 | } |
65 | | |
66 | | static char *from_string_nonull(JsonnetVm *vm, const std::string &v, size_t *buflen) |
67 | 0 | { |
68 | 0 | char *r = jsonnet_realloc(vm, nullptr, v.length()); |
69 | 0 | std::memcpy(r, v.data(), v.length()); |
70 | 0 | *buflen = v.length(); |
71 | 0 | return r; |
72 | 0 | } |
73 | | |
74 | | static int default_import_callback(void *ctx, const char *dir, const char *file, |
75 | | char **found_here_cptr, char **buf, size_t *buflen); |
76 | | |
77 | | const char *jsonnet_json_extract_string(JsonnetVm *vm, const struct JsonnetJsonValue *v) |
78 | 0 | { |
79 | 0 | (void)vm; |
80 | 0 | if (v->kind != JsonnetJsonValue::STRING) |
81 | 0 | return nullptr; |
82 | 0 | return v->string.c_str(); |
83 | 0 | } |
84 | | |
85 | | int jsonnet_json_extract_number(struct JsonnetVm *vm, const struct JsonnetJsonValue *v, double *out) |
86 | 0 | { |
87 | 0 | (void)vm; |
88 | 0 | if (v->kind != JsonnetJsonValue::NUMBER) |
89 | 0 | return 0; |
90 | 0 | *out = v->number; |
91 | 0 | return 1; |
92 | 0 | } |
93 | | |
94 | | int jsonnet_json_extract_bool(struct JsonnetVm *vm, const struct JsonnetJsonValue *v) |
95 | 0 | { |
96 | 0 | (void)vm; |
97 | 0 | if (v->kind != JsonnetJsonValue::BOOL) |
98 | 0 | return 2; |
99 | 0 | return v->number != 0; |
100 | 0 | } |
101 | | |
102 | | int jsonnet_json_extract_null(struct JsonnetVm *vm, const struct JsonnetJsonValue *v) |
103 | 0 | { |
104 | 0 | (void)vm; |
105 | 0 | return v->kind == JsonnetJsonValue::NULL_KIND; |
106 | 0 | } |
107 | | |
108 | | JsonnetJsonValue *jsonnet_json_make_string(JsonnetVm *vm, const char *v) |
109 | 0 | { |
110 | 0 | (void)vm; |
111 | 0 | JsonnetJsonValue *r = new JsonnetJsonValue(); |
112 | 0 | r->kind = JsonnetJsonValue::STRING; |
113 | 0 | r->string = v; |
114 | 0 | return r; |
115 | 0 | } |
116 | | |
117 | | JsonnetJsonValue *jsonnet_json_make_number(struct JsonnetVm *vm, double v) |
118 | 0 | { |
119 | 0 | (void)vm; |
120 | 0 | JsonnetJsonValue *r = new JsonnetJsonValue(); |
121 | 0 | r->kind = JsonnetJsonValue::NUMBER; |
122 | 0 | r->number = v; |
123 | 0 | return r; |
124 | 0 | } |
125 | | |
126 | | JsonnetJsonValue *jsonnet_json_make_bool(struct JsonnetVm *vm, int v) |
127 | 0 | { |
128 | 0 | (void)vm; |
129 | 0 | JsonnetJsonValue *r = new JsonnetJsonValue(); |
130 | 0 | r->kind = JsonnetJsonValue::BOOL; |
131 | 0 | r->number = v != 0; |
132 | 0 | return r; |
133 | 0 | } |
134 | | |
135 | | JsonnetJsonValue *jsonnet_json_make_null(struct JsonnetVm *vm) |
136 | 0 | { |
137 | 0 | (void)vm; |
138 | 0 | JsonnetJsonValue *r = new JsonnetJsonValue(); |
139 | 0 | r->kind = JsonnetJsonValue::NULL_KIND; |
140 | 0 | return r; |
141 | 0 | } |
142 | | |
143 | | JsonnetJsonValue *jsonnet_json_make_array(JsonnetVm *vm) |
144 | 0 | { |
145 | 0 | (void)vm; |
146 | 0 | JsonnetJsonValue *r = new JsonnetJsonValue(); |
147 | 0 | r->kind = JsonnetJsonValue::ARRAY; |
148 | 0 | return r; |
149 | 0 | } |
150 | | |
151 | | void jsonnet_json_array_append(JsonnetVm *vm, JsonnetJsonValue *arr, JsonnetJsonValue *v) |
152 | 0 | { |
153 | 0 | (void)vm; |
154 | 0 | assert(arr->kind == JsonnetJsonValue::ARRAY); |
155 | 0 | arr->elements.emplace_back(v); |
156 | 0 | } |
157 | | |
158 | | JsonnetJsonValue *jsonnet_json_make_object(JsonnetVm *vm) |
159 | 0 | { |
160 | 0 | (void)vm; |
161 | 0 | JsonnetJsonValue *r = new JsonnetJsonValue(); |
162 | 0 | r->kind = JsonnetJsonValue::OBJECT; |
163 | 0 | return r; |
164 | 0 | } |
165 | | |
166 | | void jsonnet_json_object_append(JsonnetVm *vm, JsonnetJsonValue *obj, const char *f, |
167 | | JsonnetJsonValue *v) |
168 | 0 | { |
169 | 0 | (void)vm; |
170 | 0 | assert(obj->kind == JsonnetJsonValue::OBJECT); |
171 | 0 | obj->fields[std::string(f)] = std::unique_ptr<JsonnetJsonValue>(v); |
172 | 0 | } |
173 | | |
174 | | void jsonnet_json_destroy(JsonnetVm *vm, JsonnetJsonValue *v) |
175 | 0 | { |
176 | 0 | (void)vm; |
177 | 0 | delete v; |
178 | 0 | } |
179 | | |
180 | | struct JsonnetVm { |
181 | | double gcGrowthTrigger; |
182 | | unsigned maxStack; |
183 | | unsigned gcMinObjects; |
184 | | unsigned maxTrace; |
185 | | std::map<std::string, VmExt> ext; |
186 | | std::map<std::string, VmExt> tla; |
187 | | JsonnetImportCallback *importCallback; |
188 | | VmNativeCallbackMap nativeCallbacks; |
189 | | void *importCallbackContext; |
190 | | bool stringOutput; |
191 | | std::vector<std::string> jpaths; |
192 | | |
193 | | FmtOpts fmtOpts; |
194 | | bool fmtDebugDesugaring; |
195 | | |
196 | | JsonnetVm(void) |
197 | | : gcGrowthTrigger(2.0), |
198 | | maxStack(500), |
199 | | gcMinObjects(1000), |
200 | | maxTrace(20), |
201 | | importCallback(default_import_callback), |
202 | | importCallbackContext(this), |
203 | | stringOutput(false), |
204 | | fmtDebugDesugaring(false) |
205 | 626 | { |
206 | 626 | jpaths.emplace_back("/usr/share/jsonnet-" + std::string(jsonnet_version()) + "/"); |
207 | 626 | jpaths.emplace_back("/usr/local/share/jsonnet-" + std::string(jsonnet_version()) + "/"); |
208 | 626 | } |
209 | | }; |
210 | | |
211 | | enum ImportStatus { IMPORT_STATUS_OK, IMPORT_STATUS_FILE_NOT_FOUND, IMPORT_STATUS_IO_ERROR }; |
212 | | |
213 | | static enum ImportStatus try_path(const std::string &dir, const std::string &rel, |
214 | | std::string &content, std::string &found_here, |
215 | | std::string &err_msg) |
216 | 0 | { |
217 | 0 | std::string abs_path; |
218 | 0 | if (rel.length() == 0) { |
219 | 0 | err_msg = "the empty string is not a valid filename"; |
220 | 0 | return IMPORT_STATUS_IO_ERROR; |
221 | 0 | } |
222 | | // It is possible that rel is actually absolute. |
223 | 0 | if (rel[0] == '/') { |
224 | 0 | abs_path = rel; |
225 | 0 | } else { |
226 | 0 | abs_path = dir + rel; |
227 | 0 | } |
228 | |
|
229 | 0 | if (abs_path[abs_path.length() - 1] == '/') { |
230 | 0 | err_msg = "attempted to import a directory"; |
231 | 0 | return IMPORT_STATUS_IO_ERROR; |
232 | 0 | } |
233 | | |
234 | 0 | std::ifstream f; |
235 | 0 | f.open(abs_path.c_str()); |
236 | 0 | if (!f.good()) |
237 | 0 | return IMPORT_STATUS_FILE_NOT_FOUND; |
238 | 0 | try { |
239 | 0 | content.assign(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()); |
240 | 0 | } catch (const std::ios_base::failure &io_err) { |
241 | 0 | err_msg = io_err.what(); |
242 | 0 | return IMPORT_STATUS_IO_ERROR; |
243 | 0 | } |
244 | 0 | if (!f.good()) { |
245 | 0 | err_msg = strerror(errno); |
246 | 0 | return IMPORT_STATUS_IO_ERROR; |
247 | 0 | } |
248 | | |
249 | 0 | found_here = abs_path; |
250 | |
|
251 | 0 | return IMPORT_STATUS_OK; |
252 | 0 | } |
253 | | |
254 | | static int default_import_callback(void *ctx, const char *dir, const char *file, |
255 | | char **found_here_cptr, char **buf, size_t *buflen) |
256 | 0 | { |
257 | 0 | auto *vm = static_cast<JsonnetVm *>(ctx); |
258 | |
|
259 | 0 | std::string input, found_here, err_msg; |
260 | |
|
261 | 0 | ImportStatus status = try_path(dir, file, input, found_here, err_msg); |
262 | |
|
263 | 0 | std::vector<std::string> jpaths(vm->jpaths); |
264 | | |
265 | | // If not found, try library search path. |
266 | 0 | while (status == IMPORT_STATUS_FILE_NOT_FOUND) { |
267 | 0 | if (jpaths.size() == 0) { |
268 | 0 | const char *err = "no match locally or in the Jsonnet library paths."; |
269 | 0 | *buf = from_string_nonull(vm, err, buflen); |
270 | 0 | return 1; // failure |
271 | 0 | } |
272 | 0 | status = try_path(jpaths.back(), file, input, found_here, err_msg); |
273 | 0 | jpaths.pop_back(); |
274 | 0 | } |
275 | | |
276 | 0 | if (status == IMPORT_STATUS_IO_ERROR) { |
277 | 0 | *buf = from_string_nonull(vm, err_msg, buflen); |
278 | 0 | return 1; // failure |
279 | 0 | } |
280 | | |
281 | 0 | assert(status == IMPORT_STATUS_OK); |
282 | 0 | *found_here_cptr = from_string(vm, found_here); |
283 | 0 | *buf = from_string_nonull(vm, input, buflen); |
284 | 0 | return 0; // success |
285 | 0 | } |
286 | | |
287 | 1.87k | #define TRY try { |
288 | | #define CATCH(func) \ |
289 | 1.87k | } \ |
290 | 1.87k | catch (const std::bad_alloc &) \ |
291 | 1.87k | { \ |
292 | 0 | memory_panic(); \ |
293 | 0 | } \ |
294 | 1.87k | catch (const std::exception &e) \ |
295 | 1.87k | { \ |
296 | 0 | std::cerr << "Something went wrong during " func ", please report this: " << e.what() \ |
297 | 0 | << std::endl; \ |
298 | 0 | abort(); \ |
299 | 0 | } |
300 | | |
301 | | const char *jsonnet_version(void) |
302 | 1.25k | { |
303 | 1.25k | return LIB_JSONNET_VERSION; |
304 | 1.25k | } |
305 | | |
306 | | JsonnetVm *jsonnet_make(void) |
307 | 626 | { |
308 | 626 | TRY |
309 | 626 | return new JsonnetVm(); |
310 | 626 | CATCH("jsonnet_make") |
311 | 0 | return nullptr; |
312 | 626 | } |
313 | | |
314 | | void jsonnet_destroy(JsonnetVm *vm) |
315 | 626 | { |
316 | 626 | TRY |
317 | 626 | delete vm; |
318 | 626 | CATCH("jsonnet_destroy") |
319 | 626 | } |
320 | | |
321 | | void jsonnet_max_stack(JsonnetVm *vm, unsigned v) |
322 | 0 | { |
323 | 0 | vm->maxStack = v; |
324 | 0 | } |
325 | | |
326 | | void jsonnet_gc_min_objects(JsonnetVm *vm, unsigned v) |
327 | 0 | { |
328 | 0 | vm->gcMinObjects = v; |
329 | 0 | } |
330 | | |
331 | | void jsonnet_gc_growth_trigger(JsonnetVm *vm, double v) |
332 | 0 | { |
333 | 0 | vm->gcGrowthTrigger = v; |
334 | 0 | } |
335 | | |
336 | | void jsonnet_string_output(struct JsonnetVm *vm, int v) |
337 | 0 | { |
338 | 0 | vm->stringOutput = bool(v); |
339 | 0 | } |
340 | | |
341 | | void jsonnet_import_callback(struct JsonnetVm *vm, JsonnetImportCallback *cb, void *ctx) |
342 | 626 | { |
343 | 626 | vm->importCallback = cb; |
344 | 626 | vm->importCallbackContext = ctx; |
345 | 626 | } |
346 | | |
347 | | void jsonnet_native_callback(struct JsonnetVm *vm, const char *name, JsonnetNativeCallback *cb, |
348 | | void *ctx, const char *const *params) |
349 | 0 | { |
350 | 0 | std::vector<std::string> params2; |
351 | 0 | for (; *params != nullptr; params++) |
352 | 0 | params2.push_back(*params); |
353 | 0 | vm->nativeCallbacks[name] = VmNativeCallback{cb, ctx, params2}; |
354 | 0 | } |
355 | | |
356 | | void jsonnet_ext_var(JsonnetVm *vm, const char *key, const char *val) |
357 | 0 | { |
358 | 0 | vm->ext[key] = VmExt(val, false); |
359 | 0 | } |
360 | | |
361 | | void jsonnet_ext_code(JsonnetVm *vm, const char *key, const char *val) |
362 | 0 | { |
363 | 0 | vm->ext[key] = VmExt(val, true); |
364 | 0 | } |
365 | | |
366 | | void jsonnet_tla_var(JsonnetVm *vm, const char *key, const char *val) |
367 | 0 | { |
368 | 0 | vm->tla[key] = VmExt(val, false); |
369 | 0 | } |
370 | | |
371 | | void jsonnet_tla_code(JsonnetVm *vm, const char *key, const char *val) |
372 | 0 | { |
373 | 0 | vm->tla[key] = VmExt(val, true); |
374 | 0 | } |
375 | | |
376 | | void jsonnet_fmt_debug_desugaring(JsonnetVm *vm, int v) |
377 | 0 | { |
378 | 0 | vm->fmtDebugDesugaring = v; |
379 | 0 | } |
380 | | |
381 | | void jsonnet_fmt_indent(JsonnetVm *vm, int v) |
382 | 0 | { |
383 | 0 | vm->fmtOpts.indent = v; |
384 | 0 | } |
385 | | |
386 | | void jsonnet_fmt_max_blank_lines(JsonnetVm *vm, int v) |
387 | 0 | { |
388 | 0 | vm->fmtOpts.maxBlankLines = v; |
389 | 0 | } |
390 | | |
391 | | void jsonnet_fmt_string(JsonnetVm *vm, int v) |
392 | 0 | { |
393 | 0 | if (v != 'd' && v != 's' && v != 'l') |
394 | 0 | v = 'l'; |
395 | 0 | vm->fmtOpts.stringStyle = v; |
396 | 0 | } |
397 | | |
398 | | void jsonnet_fmt_comment(JsonnetVm *vm, int v) |
399 | 0 | { |
400 | 0 | if (v != 'h' && v != 's' && v != 'l') |
401 | 0 | v = 'l'; |
402 | 0 | vm->fmtOpts.commentStyle = v; |
403 | 0 | } |
404 | | |
405 | | void jsonnet_fmt_pad_arrays(JsonnetVm *vm, int v) |
406 | 0 | { |
407 | 0 | vm->fmtOpts.padArrays = v; |
408 | 0 | } |
409 | | |
410 | | void jsonnet_fmt_pad_objects(JsonnetVm *vm, int v) |
411 | 0 | { |
412 | 0 | vm->fmtOpts.padObjects = v; |
413 | 0 | } |
414 | | |
415 | | void jsonnet_fmt_pretty_field_names(JsonnetVm *vm, int v) |
416 | 0 | { |
417 | 0 | vm->fmtOpts.prettyFieldNames = v; |
418 | 0 | } |
419 | | |
420 | | void jsonnet_fmt_sort_imports(JsonnetVm *vm, int v) |
421 | 0 | { |
422 | 0 | vm->fmtOpts.sortImports = v; |
423 | 0 | } |
424 | | |
425 | | void jsonnet_max_trace(JsonnetVm *vm, unsigned v) |
426 | 0 | { |
427 | 0 | vm->maxTrace = v; |
428 | 0 | } |
429 | | |
430 | | void jsonnet_jpath_add(JsonnetVm *vm, const char *path_) |
431 | 0 | { |
432 | 0 | if (std::strlen(path_) == 0) |
433 | 0 | return; |
434 | 0 | std::string path = path_; |
435 | 0 | if (path[path.length() - 1] != '/') |
436 | 0 | path += '/'; |
437 | 0 | vm->jpaths.emplace_back(path); |
438 | 0 | } |
439 | | |
440 | | static char *jsonnet_fmt_snippet_aux(JsonnetVm *vm, const char *filename, const char *snippet, |
441 | | int *error) |
442 | 0 | { |
443 | 0 | try { |
444 | 0 | Allocator alloc; |
445 | 0 | std::string json_str; |
446 | 0 | AST *expr; |
447 | 0 | std::map<std::string, std::string> files; |
448 | 0 | Tokens tokens = jsonnet_lex(filename, snippet); |
449 | |
|
450 | 0 | expr = jsonnet_parse(&alloc, tokens); |
451 | 0 | Fodder final_fodder = tokens.front().fodder; |
452 | |
|
453 | 0 | if (vm->fmtDebugDesugaring) |
454 | 0 | jsonnet_desugar(&alloc, expr, &vm->tla); |
455 | |
|
456 | 0 | json_str = jsonnet_fmt(expr, final_fodder, vm->fmtOpts); |
457 | |
|
458 | 0 | *error = false; |
459 | 0 | return from_string(vm, json_str); |
460 | |
|
461 | 0 | } catch (StaticError &e) { |
462 | 0 | std::stringstream ss; |
463 | 0 | ss << "STATIC ERROR: " << e << std::endl; |
464 | 0 | *error = true; |
465 | 0 | return from_string(vm, ss.str()); |
466 | 0 | } |
467 | 0 | } |
468 | | |
469 | | char *jsonnet_fmt_file(JsonnetVm *vm, const char *filename, int *error) |
470 | 0 | { |
471 | 0 | TRY |
472 | 0 | std::ifstream f; |
473 | 0 | f.open(filename); |
474 | 0 | if (!f.good()) { |
475 | 0 | std::stringstream ss; |
476 | 0 | ss << "Opening input file: " << filename << ": " << strerror(errno); |
477 | 0 | *error = true; |
478 | 0 | return from_string(vm, ss.str()); |
479 | 0 | } |
480 | 0 | std::string input; |
481 | 0 | input.assign(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()); |
482 | |
|
483 | 0 | return jsonnet_fmt_snippet_aux(vm, filename, input.c_str(), error); |
484 | 0 | CATCH("jsonnet_fmt_file") |
485 | 0 | return nullptr; // Never happens. |
486 | 0 | } |
487 | | |
488 | | char *jsonnet_fmt_snippet(JsonnetVm *vm, const char *filename, const char *snippet, int *error) |
489 | 0 | { |
490 | 0 | TRY |
491 | 0 | return jsonnet_fmt_snippet_aux(vm, filename, snippet, error); |
492 | 0 | CATCH("jsonnet_fmt_snippet") |
493 | 0 | return nullptr; // Never happens. |
494 | 0 | } |
495 | | |
496 | | namespace { |
497 | | enum EvalKind { REGULAR, MULTI, STREAM }; |
498 | | } // namespace |
499 | | |
500 | | static char *jsonnet_evaluate_snippet_aux(JsonnetVm *vm, const char *filename, const char *snippet, |
501 | | int *error, EvalKind kind) |
502 | 626 | { |
503 | 626 | try { |
504 | 626 | Allocator alloc; |
505 | 626 | AST *expr; |
506 | 626 | Tokens tokens = jsonnet_lex(filename, snippet); |
507 | | |
508 | 626 | expr = jsonnet_parse(&alloc, tokens); |
509 | | |
510 | 626 | jsonnet_desugar(&alloc, expr, &vm->tla); |
511 | | |
512 | 626 | unsigned max_stack = vm->maxStack; |
513 | | |
514 | | // For the stdlib desugaring. |
515 | 626 | max_stack++; |
516 | | |
517 | | // For the TLA desugaring. |
518 | 626 | max_stack++; |
519 | | |
520 | 626 | jsonnet_static_analysis(expr); |
521 | 626 | switch (kind) { |
522 | 0 | case REGULAR: { |
523 | 0 | std::string json_str = jsonnet_vm_execute(&alloc, |
524 | 0 | expr, |
525 | 0 | vm->ext, |
526 | 0 | max_stack, |
527 | 0 | vm->gcMinObjects, |
528 | 0 | vm->gcGrowthTrigger, |
529 | 0 | vm->nativeCallbacks, |
530 | 0 | vm->importCallback, |
531 | 0 | vm->importCallbackContext, |
532 | 0 | vm->stringOutput); |
533 | 0 | json_str += "\n"; |
534 | 0 | *error = false; |
535 | 0 | return from_string(vm, json_str); |
536 | 0 | } break; |
537 | | |
538 | 284 | case MULTI: { |
539 | 284 | std::map<std::string, std::string> files = |
540 | 284 | jsonnet_vm_execute_multi(&alloc, |
541 | 284 | expr, |
542 | 284 | vm->ext, |
543 | 284 | max_stack, |
544 | 284 | vm->gcMinObjects, |
545 | 284 | vm->gcGrowthTrigger, |
546 | 284 | vm->nativeCallbacks, |
547 | 284 | vm->importCallback, |
548 | 284 | vm->importCallbackContext, |
549 | 284 | vm->stringOutput); |
550 | 284 | size_t sz = 1; // final sentinel |
551 | 644 | for (const auto &pair : files) { |
552 | 644 | sz += pair.first.length() + 1; // include sentinel |
553 | 644 | sz += pair.second.length() + 2; // Add a '\n' as well as sentinel |
554 | 644 | } |
555 | 284 | char *buf = (char *)::malloc(sz); |
556 | 284 | if (buf == nullptr) |
557 | 0 | memory_panic(); |
558 | 284 | std::ptrdiff_t i = 0; |
559 | 644 | for (const auto &pair : files) { |
560 | 644 | memcpy(&buf[i], pair.first.c_str(), pair.first.length() + 1); |
561 | 644 | i += pair.first.length() + 1; |
562 | 644 | memcpy(&buf[i], pair.second.c_str(), pair.second.length()); |
563 | 644 | i += pair.second.length(); |
564 | 644 | buf[i] = '\n'; |
565 | 644 | i++; |
566 | 644 | buf[i] = '\0'; |
567 | 644 | i++; |
568 | 644 | } |
569 | 284 | buf[i] = '\0'; // final sentinel |
570 | 284 | *error = false; |
571 | 284 | return buf; |
572 | 0 | } break; |
573 | | |
574 | 0 | case STREAM: { |
575 | 0 | std::vector<std::string> documents = |
576 | 0 | jsonnet_vm_execute_stream(&alloc, |
577 | 0 | expr, |
578 | 0 | vm->ext, |
579 | 0 | max_stack, |
580 | 0 | vm->gcMinObjects, |
581 | 0 | vm->gcGrowthTrigger, |
582 | 0 | vm->nativeCallbacks, |
583 | 0 | vm->importCallback, |
584 | 0 | vm->importCallbackContext, |
585 | 0 | vm->stringOutput); |
586 | 0 | size_t sz = 1; // final sentinel |
587 | 0 | for (const auto &doc : documents) { |
588 | 0 | sz += doc.length() + 2; // Add a '\n' as well as sentinel |
589 | 0 | } |
590 | 0 | char *buf = (char *)::malloc(sz); |
591 | 0 | if (buf == nullptr) |
592 | 0 | memory_panic(); |
593 | 0 | std::ptrdiff_t i = 0; |
594 | 0 | for (const auto &doc : documents) { |
595 | 0 | memcpy(&buf[i], doc.c_str(), doc.length()); |
596 | 0 | i += doc.length(); |
597 | 0 | buf[i] = '\n'; |
598 | 0 | i++; |
599 | 0 | buf[i] = '\0'; |
600 | 0 | i++; |
601 | 0 | } |
602 | 0 | buf[i] = '\0'; // final sentinel |
603 | 0 | *error = false; |
604 | 0 | return buf; |
605 | 0 | } break; |
606 | | |
607 | 0 | default: |
608 | 0 | fputs("INTERNAL ERROR: bad value of 'kind', probably memory corruption.\n", stderr); |
609 | 0 | abort(); |
610 | 626 | } |
611 | | |
612 | 626 | } catch (StaticError &e) { |
613 | 342 | std::stringstream ss; |
614 | 342 | ss << "STATIC ERROR: " << e << std::endl; |
615 | 342 | *error = true; |
616 | 342 | return from_string(vm, ss.str()); |
617 | | |
618 | 342 | } catch (RuntimeError &e) { |
619 | 192 | std::stringstream ss; |
620 | 192 | ss << "RUNTIME ERROR: " << e.msg << std::endl; |
621 | 192 | const long max_above = vm->maxTrace / 2; |
622 | 192 | const long max_below = vm->maxTrace - max_above; |
623 | 192 | const long sz = e.stackTrace.size(); |
624 | 16.8k | for (long i = 0; i < sz; ++i) { |
625 | 16.6k | const auto &f = e.stackTrace[i]; |
626 | 16.6k | if (vm->maxTrace > 0 && i >= max_above && i < sz - max_below) { |
627 | 15.4k | if (i == max_above) |
628 | 40 | ss << "\t..." << std::endl; |
629 | 15.4k | } else { |
630 | 1.19k | ss << "\t" << f.location << "\t" << f.name << std::endl; |
631 | 1.19k | } |
632 | 16.6k | } |
633 | 192 | *error = true; |
634 | 192 | return from_string(vm, ss.str()); |
635 | 192 | } |
636 | | |
637 | 0 | return nullptr; // Quiet, compiler. |
638 | 626 | } |
639 | | |
640 | | static char *jsonnet_evaluate_file_aux(JsonnetVm *vm, const char *filename, int *error, |
641 | | EvalKind kind) |
642 | 0 | { |
643 | 0 | std::ifstream f; |
644 | 0 | f.open(filename); |
645 | 0 | if (!f.good()) { |
646 | 0 | std::stringstream ss; |
647 | 0 | ss << "Opening input file: " << filename << ": " << strerror(errno); |
648 | 0 | *error = true; |
649 | 0 | return from_string(vm, ss.str()); |
650 | 0 | } |
651 | 0 | std::string input; |
652 | 0 | input.assign(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()); |
653 | |
|
654 | 0 | return jsonnet_evaluate_snippet_aux(vm, filename, input.c_str(), error, kind); |
655 | 0 | } |
656 | | |
657 | | char *jsonnet_evaluate_file(JsonnetVm *vm, const char *filename, int *error) |
658 | 0 | { |
659 | 0 | TRY |
660 | 0 | return jsonnet_evaluate_file_aux(vm, filename, error, REGULAR); |
661 | 0 | CATCH("jsonnet_evaluate_file") |
662 | 0 | return nullptr; // Never happens. |
663 | 0 | } |
664 | | |
665 | | char *jsonnet_evaluate_file_multi(JsonnetVm *vm, const char *filename, int *error) |
666 | 0 | { |
667 | 0 | TRY |
668 | 0 | return jsonnet_evaluate_file_aux(vm, filename, error, MULTI); |
669 | 0 | CATCH("jsonnet_evaluate_file_multi") |
670 | 0 | return nullptr; // Never happens. |
671 | 0 | } |
672 | | |
673 | | char *jsonnet_evaluate_file_stream(JsonnetVm *vm, const char *filename, int *error) |
674 | 0 | { |
675 | 0 | TRY |
676 | 0 | return jsonnet_evaluate_file_aux(vm, filename, error, STREAM); |
677 | 0 | CATCH("jsonnet_evaluate_file_stream") |
678 | 0 | return nullptr; // Never happens. |
679 | 0 | } |
680 | | |
681 | | char *jsonnet_evaluate_snippet(JsonnetVm *vm, const char *filename, const char *snippet, int *error) |
682 | 0 | { |
683 | 0 | TRY |
684 | 0 | return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, REGULAR); |
685 | 0 | CATCH("jsonnet_evaluate_snippet") |
686 | 0 | return nullptr; // Never happens. |
687 | 0 | } |
688 | | |
689 | | char *jsonnet_evaluate_snippet_multi(JsonnetVm *vm, const char *filename, const char *snippet, |
690 | | int *error) |
691 | 626 | { |
692 | 626 | TRY |
693 | 626 | return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, MULTI); |
694 | 626 | CATCH("jsonnet_evaluate_snippet_multi") |
695 | 0 | return nullptr; // Never happens. |
696 | 626 | } |
697 | | |
698 | | char *jsonnet_evaluate_snippet_stream(JsonnetVm *vm, const char *filename, const char *snippet, |
699 | | int *error) |
700 | 0 | { |
701 | 0 | TRY |
702 | 0 | return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, STREAM); |
703 | 0 | CATCH("jsonnet_evaluate_snippet_stream") |
704 | 0 | return nullptr; // Never happens. |
705 | 0 | } |
706 | | |
707 | | char *jsonnet_realloc(JsonnetVm *vm, char *str, size_t sz) |
708 | 1.16k | { |
709 | 1.16k | (void)vm; |
710 | 1.16k | if (str == nullptr) { |
711 | 534 | if (sz == 0) |
712 | 0 | return nullptr; |
713 | 534 | auto *r = static_cast<char *>(::malloc(sz)); |
714 | 534 | if (r == nullptr) |
715 | 0 | memory_panic(); |
716 | 534 | return r; |
717 | 626 | } else { |
718 | 626 | if (sz == 0) { |
719 | 626 | ::free(str); |
720 | 626 | return nullptr; |
721 | 626 | } else { |
722 | 0 | auto *r = static_cast<char *>(::realloc(str, sz)); |
723 | 0 | if (r == nullptr) |
724 | 0 | memory_panic(); |
725 | 0 | return r; |
726 | 0 | } |
727 | 626 | } |
728 | 1.16k | } |