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