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