/src/jsonnet/core/libjsonnet.cpp
Line | Count | Source |
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 | 20.9k | { |
91 | 20.9k | char *r = jsonnet_realloc(vm, nullptr, v.length() + 1); |
92 | 20.9k | std::memcpy(r, v.c_str(), v.length() + 1); |
93 | 20.9k | return r; |
94 | 20.9k | } |
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 | | bool trailingNewline; |
222 | | std::vector<std::string> jpaths; |
223 | | |
224 | | FmtOpts fmtOpts; |
225 | | bool fmtDebugDesugaring; |
226 | | |
227 | | JsonnetVm(void) |
228 | 22.1k | : gcGrowthTrigger(2.0), |
229 | 22.1k | maxStack(500), |
230 | 22.1k | gcMinObjects(1000), |
231 | 22.1k | maxTrace(20), |
232 | 22.1k | importCallback(default_import_callback), |
233 | 22.1k | importCallbackContext(this), |
234 | 22.1k | stringOutput(false), |
235 | 22.1k | trailingNewline(true), |
236 | 22.1k | fmtDebugDesugaring(false) |
237 | 22.1k | { |
238 | 22.1k | jpaths.emplace_back("/usr/share/jsonnet-" + std::string(jsonnet_version()) + "/"); |
239 | 22.1k | jpaths.emplace_back("/usr/local/share/jsonnet-" + std::string(jsonnet_version()) + "/"); |
240 | 22.1k | } |
241 | | }; |
242 | | |
243 | | enum ImportStatus { IMPORT_STATUS_OK, IMPORT_STATUS_FILE_NOT_FOUND, IMPORT_STATUS_IO_ERROR }; |
244 | | |
245 | | static enum ImportStatus try_path(const std::string &dir, const std::string &rel, |
246 | | std::string &content, std::string &found_here, |
247 | | std::string &err_msg) |
248 | 0 | { |
249 | 0 | std::string abs_path; |
250 | 0 | if (rel.length() == 0) { |
251 | 0 | err_msg = "the empty string is not a valid filename"; |
252 | 0 | return IMPORT_STATUS_IO_ERROR; |
253 | 0 | } |
254 | | // It is possible that rel is actually absolute. |
255 | 0 | if (rel[0] == '/') { |
256 | 0 | abs_path = rel; |
257 | 0 | } else { |
258 | 0 | abs_path = dir + rel; |
259 | 0 | } |
260 | |
|
261 | 0 | if (abs_path[abs_path.length() - 1] == '/') { |
262 | 0 | err_msg = "attempted to import a directory"; |
263 | 0 | return IMPORT_STATUS_IO_ERROR; |
264 | 0 | } |
265 | | |
266 | 0 | std::ifstream f; |
267 | 0 | f.open(abs_path.c_str()); |
268 | 0 | if (!f.good()) |
269 | 0 | return IMPORT_STATUS_FILE_NOT_FOUND; |
270 | 0 | try { |
271 | 0 | content.assign(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()); |
272 | 0 | } catch (const std::ios_base::failure &io_err) { |
273 | 0 | err_msg = io_err.what(); |
274 | 0 | return IMPORT_STATUS_IO_ERROR; |
275 | 0 | } |
276 | 0 | if (!f.good()) { |
277 | 0 | err_msg = strerror(errno); |
278 | 0 | return IMPORT_STATUS_IO_ERROR; |
279 | 0 | } |
280 | | |
281 | 0 | found_here = abs_path; |
282 | |
|
283 | 0 | return IMPORT_STATUS_OK; |
284 | 0 | } |
285 | | |
286 | | static int default_import_callback(void *ctx, const char *dir, const char *file, |
287 | | char **found_here_cptr, char **buf, size_t *buflen) |
288 | 0 | { |
289 | 0 | auto *vm = static_cast<JsonnetVm *>(ctx); |
290 | |
|
291 | 0 | std::string input, found_here, err_msg; |
292 | |
|
293 | 0 | ImportStatus status = try_path(dir, file, input, found_here, err_msg); |
294 | |
|
295 | 0 | std::vector<std::string> jpaths(vm->jpaths); |
296 | | |
297 | | // If not found, try library search path. |
298 | 0 | while (status == IMPORT_STATUS_FILE_NOT_FOUND) { |
299 | 0 | if (jpaths.size() == 0) { |
300 | 0 | const char *err = "no match locally or in the Jsonnet library paths."; |
301 | 0 | *buf = from_string_nonull(vm, err, buflen); |
302 | 0 | return 1; // failure |
303 | 0 | } |
304 | 0 | status = try_path(jpaths.back(), file, input, found_here, err_msg); |
305 | 0 | jpaths.pop_back(); |
306 | 0 | } |
307 | | |
308 | 0 | if (status == IMPORT_STATUS_IO_ERROR) { |
309 | 0 | *buf = from_string_nonull(vm, err_msg, buflen); |
310 | 0 | return 1; // failure |
311 | 0 | } |
312 | | |
313 | 0 | assert(status == IMPORT_STATUS_OK); |
314 | 0 | *found_here_cptr = from_string(vm, found_here); |
315 | 0 | *buf = from_string_nonull(vm, input, buflen); |
316 | 0 | return 0; // success |
317 | 0 | } |
318 | | |
319 | 66.3k | #define TRY try { |
320 | | #define CATCH(func) \ |
321 | 66.3k | } \ |
322 | 66.3k | catch (const std::bad_alloc &) \ |
323 | 66.3k | { \ |
324 | 0 | memory_panic(); \ |
325 | 0 | } \ |
326 | 66.3k | catch (const std::exception &e) \ |
327 | 66.3k | { \ |
328 | 0 | std::cerr << "Something went wrong during " func ", please report this: " << e.what() \ |
329 | 0 | << std::endl; \ |
330 | 0 | abort(); \ |
331 | 0 | } |
332 | | |
333 | | const char *jsonnet_version(void) |
334 | 44.2k | { |
335 | 44.2k | return LIB_JSONNET_VERSION; |
336 | 44.2k | } |
337 | | |
338 | | JsonnetVm *jsonnet_make(void) |
339 | 22.1k | { |
340 | 22.1k | TRY |
341 | 22.1k | return new JsonnetVm(); |
342 | 22.1k | CATCH("jsonnet_make") |
343 | 0 | return nullptr; |
344 | 22.1k | } |
345 | | |
346 | | void jsonnet_destroy(JsonnetVm *vm) |
347 | 22.1k | { |
348 | 22.1k | TRY |
349 | 22.1k | delete vm; |
350 | 22.1k | CATCH("jsonnet_destroy") |
351 | 22.1k | } |
352 | | |
353 | | void jsonnet_max_stack(JsonnetVm *vm, unsigned v) |
354 | 0 | { |
355 | 0 | vm->maxStack = v; |
356 | 0 | } |
357 | | |
358 | | void jsonnet_gc_min_objects(JsonnetVm *vm, unsigned v) |
359 | 0 | { |
360 | 0 | vm->gcMinObjects = v; |
361 | 0 | } |
362 | | |
363 | | void jsonnet_gc_growth_trigger(JsonnetVm *vm, double v) |
364 | 0 | { |
365 | 0 | vm->gcGrowthTrigger = v; |
366 | 0 | } |
367 | | |
368 | | void jsonnet_string_output(struct JsonnetVm *vm, int v) |
369 | 0 | { |
370 | 0 | vm->stringOutput = bool(v); |
371 | 0 | } |
372 | | |
373 | | void jsonnet_set_trailing_newline(struct JsonnetVm *vm, int enable) |
374 | 0 | { |
375 | 0 | vm->trailingNewline = bool(enable); |
376 | 0 | } |
377 | | |
378 | | void jsonnet_import_callback(struct JsonnetVm *vm, JsonnetImportCallback *cb, void *ctx) |
379 | 22.1k | { |
380 | 22.1k | vm->importCallback = cb; |
381 | 22.1k | vm->importCallbackContext = ctx; |
382 | 22.1k | } |
383 | | |
384 | | void jsonnet_native_callback(struct JsonnetVm *vm, const char *name, JsonnetNativeCallback *cb, |
385 | | void *ctx, const char *const *params) |
386 | 0 | { |
387 | 0 | std::vector<std::string> params2; |
388 | 0 | for (; *params != nullptr; params++) |
389 | 0 | params2.push_back(*params); |
390 | 0 | vm->nativeCallbacks[name] = VmNativeCallback{cb, ctx, params2}; |
391 | 0 | } |
392 | | |
393 | | void jsonnet_ext_var(JsonnetVm *vm, const char *key, const char *val) |
394 | 0 | { |
395 | 0 | vm->ext[key] = VmExt(val, false); |
396 | 0 | } |
397 | | |
398 | | void jsonnet_ext_code(JsonnetVm *vm, const char *key, const char *val) |
399 | 0 | { |
400 | 0 | vm->ext[key] = VmExt(val, true); |
401 | 0 | } |
402 | | |
403 | | void jsonnet_tla_var(JsonnetVm *vm, const char *key, const char *val) |
404 | 0 | { |
405 | 0 | vm->tla[key] = VmExt(val, false); |
406 | 0 | } |
407 | | |
408 | | void jsonnet_tla_code(JsonnetVm *vm, const char *key, const char *val) |
409 | 0 | { |
410 | 0 | vm->tla[key] = VmExt(val, true); |
411 | 0 | } |
412 | | |
413 | | void jsonnet_fmt_debug_desugaring(JsonnetVm *vm, int v) |
414 | 0 | { |
415 | 0 | vm->fmtDebugDesugaring = v; |
416 | 0 | } |
417 | | |
418 | | void jsonnet_fmt_indent(JsonnetVm *vm, int v) |
419 | 0 | { |
420 | 0 | vm->fmtOpts.indent = v; |
421 | 0 | } |
422 | | |
423 | | void jsonnet_fmt_max_blank_lines(JsonnetVm *vm, int v) |
424 | 0 | { |
425 | 0 | vm->fmtOpts.maxBlankLines = v; |
426 | 0 | } |
427 | | |
428 | | void jsonnet_fmt_string(JsonnetVm *vm, int v) |
429 | 0 | { |
430 | 0 | if (v != 'd' && v != 's' && v != 'l') |
431 | 0 | v = 'l'; |
432 | 0 | vm->fmtOpts.stringStyle = v; |
433 | 0 | } |
434 | | |
435 | | void jsonnet_fmt_comment(JsonnetVm *vm, int v) |
436 | 0 | { |
437 | 0 | if (v != 'h' && v != 's' && v != 'l') |
438 | 0 | v = 'l'; |
439 | 0 | vm->fmtOpts.commentStyle = v; |
440 | 0 | } |
441 | | |
442 | | void jsonnet_fmt_pad_arrays(JsonnetVm *vm, int v) |
443 | 0 | { |
444 | 0 | vm->fmtOpts.padArrays = v; |
445 | 0 | } |
446 | | |
447 | | void jsonnet_fmt_pad_objects(JsonnetVm *vm, int v) |
448 | 0 | { |
449 | 0 | vm->fmtOpts.padObjects = v; |
450 | 0 | } |
451 | | |
452 | | void jsonnet_fmt_pretty_field_names(JsonnetVm *vm, int v) |
453 | 0 | { |
454 | 0 | vm->fmtOpts.prettyFieldNames = v; |
455 | 0 | } |
456 | | |
457 | | void jsonnet_fmt_sort_imports(JsonnetVm *vm, int v) |
458 | 0 | { |
459 | 0 | vm->fmtOpts.sortImports = v; |
460 | 0 | } |
461 | | |
462 | | void jsonnet_max_trace(JsonnetVm *vm, unsigned v) |
463 | 0 | { |
464 | 0 | vm->maxTrace = v; |
465 | 0 | } |
466 | | |
467 | | void jsonnet_jpath_add(JsonnetVm *vm, const char *path_) |
468 | 0 | { |
469 | 0 | if (std::strlen(path_) == 0) |
470 | 0 | return; |
471 | 0 | std::string path = path_; |
472 | 0 | if (path[path.length() - 1] != '/') |
473 | 0 | path += '/'; |
474 | 0 | vm->jpaths.emplace_back(path); |
475 | 0 | } |
476 | | |
477 | | static char *jsonnet_fmt_snippet_aux(JsonnetVm *vm, const char *filename, const char *snippet, |
478 | | int *error) |
479 | 0 | { |
480 | 0 | try { |
481 | 0 | Allocator alloc; |
482 | 0 | std::string json_str; |
483 | 0 | AST *expr; |
484 | 0 | std::map<std::string, std::string> files; |
485 | 0 | Tokens tokens = jsonnet_lex(filename, snippet); |
486 | |
|
487 | 0 | expr = jsonnet_parse(&alloc, tokens); |
488 | 0 | Fodder final_fodder = tokens.front().fodder; |
489 | |
|
490 | 0 | if (vm->fmtDebugDesugaring) { |
491 | 0 | jsonnet_desugar(&alloc, expr, &vm->tla); |
492 | 0 | ReEscapeStrings(alloc).expr(expr); |
493 | 0 | } |
494 | |
|
495 | 0 | json_str = jsonnet_fmt(expr, final_fodder, vm->fmtOpts); |
496 | |
|
497 | 0 | *error = false; |
498 | 0 | return from_string(vm, json_str); |
499 | |
|
500 | 0 | } catch (StaticError &e) { |
501 | 0 | std::stringstream ss; |
502 | 0 | ss << "STATIC ERROR: " << e << std::endl; |
503 | 0 | *error = true; |
504 | 0 | return from_string(vm, ss.str()); |
505 | 0 | } |
506 | 0 | } |
507 | | |
508 | | char *jsonnet_fmt_file(JsonnetVm *vm, const char *filename, int *error) |
509 | 0 | { |
510 | 0 | TRY |
511 | 0 | std::ifstream f; |
512 | 0 | f.open(filename); |
513 | 0 | if (!f.good()) { |
514 | 0 | std::stringstream ss; |
515 | 0 | ss << "Opening input file: " << filename << ": " << strerror(errno); |
516 | 0 | *error = true; |
517 | 0 | return from_string(vm, ss.str()); |
518 | 0 | } |
519 | 0 | std::string input; |
520 | 0 | input.assign(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()); |
521 | |
|
522 | 0 | return jsonnet_fmt_snippet_aux(vm, filename, input.c_str(), error); |
523 | 0 | CATCH("jsonnet_fmt_file") |
524 | 0 | return nullptr; // Never happens. |
525 | 0 | } |
526 | | |
527 | | char *jsonnet_fmt_snippet(JsonnetVm *vm, const char *filename, const char *snippet, int *error) |
528 | 0 | { |
529 | 0 | TRY |
530 | 0 | return jsonnet_fmt_snippet_aux(vm, filename, snippet, error); |
531 | 0 | CATCH("jsonnet_fmt_snippet") |
532 | 0 | return nullptr; // Never happens. |
533 | 0 | } |
534 | | |
535 | | namespace { |
536 | | enum EvalKind { REGULAR, MULTI, STREAM }; |
537 | | } // namespace |
538 | | |
539 | | static char *jsonnet_evaluate_snippet_aux(JsonnetVm *vm, const char *filename, const char *snippet, |
540 | | int *error, EvalKind kind) |
541 | 22.1k | { |
542 | 22.1k | try { |
543 | 22.1k | Allocator alloc; |
544 | 22.1k | AST *expr; |
545 | 22.1k | Tokens tokens = jsonnet_lex(filename, snippet); |
546 | | |
547 | 22.1k | expr = jsonnet_parse(&alloc, tokens); |
548 | | |
549 | 22.1k | jsonnet_desugar(&alloc, expr, &vm->tla); |
550 | | |
551 | 22.1k | unsigned max_stack = vm->maxStack; |
552 | | |
553 | | // For the stdlib desugaring. |
554 | 22.1k | max_stack++; |
555 | | |
556 | | // For the TLA desugaring. |
557 | 22.1k | max_stack++; |
558 | | |
559 | 22.1k | jsonnet_static_analysis(expr); |
560 | 22.1k | switch (kind) { |
561 | 2.88k | case REGULAR: { |
562 | 2.88k | std::string json_str = jsonnet_vm_execute(&alloc, |
563 | 2.88k | expr, |
564 | 2.88k | vm->ext, |
565 | 2.88k | max_stack, |
566 | 2.88k | vm->gcMinObjects, |
567 | 2.88k | vm->gcGrowthTrigger, |
568 | 2.88k | vm->nativeCallbacks, |
569 | 2.88k | vm->importCallback, |
570 | 2.88k | vm->importCallbackContext, |
571 | 2.88k | vm->stringOutput); |
572 | 2.88k | if (vm->trailingNewline) { |
573 | 1.17k | json_str += "\n"; |
574 | 1.17k | } |
575 | 2.88k | *error = false; |
576 | 2.88k | return from_string(vm, json_str); |
577 | 0 | } break; |
578 | | |
579 | 4.02k | case MULTI: { |
580 | 4.02k | std::map<std::string, std::string> files = |
581 | 4.02k | jsonnet_vm_execute_multi(&alloc, |
582 | 4.02k | expr, |
583 | 4.02k | vm->ext, |
584 | 4.02k | max_stack, |
585 | 4.02k | vm->gcMinObjects, |
586 | 4.02k | vm->gcGrowthTrigger, |
587 | 4.02k | vm->nativeCallbacks, |
588 | 4.02k | vm->importCallback, |
589 | 4.02k | vm->importCallbackContext, |
590 | 4.02k | vm->stringOutput); |
591 | 4.02k | size_t sz = 1; // final sentinel |
592 | 6.01k | for (const auto &pair : files) { |
593 | 6.01k | sz += pair.first.length() + 1; // include sentinel |
594 | 6.01k | sz += pair.second.length() + 2; // Add a '\n' as well as sentinel |
595 | 6.01k | } |
596 | 4.02k | char *buf = (char *)::malloc(sz); |
597 | 4.02k | if (buf == nullptr) |
598 | 0 | memory_panic(); |
599 | 4.02k | std::ptrdiff_t i = 0; |
600 | 6.01k | for (const auto &pair : files) { |
601 | 6.01k | memcpy(&buf[i], pair.first.c_str(), pair.first.length() + 1); |
602 | 6.01k | i += pair.first.length() + 1; |
603 | 6.01k | memcpy(&buf[i], pair.second.c_str(), pair.second.length()); |
604 | 6.01k | i += pair.second.length(); |
605 | 6.01k | if (vm->trailingNewline) { |
606 | 6.01k | buf[i] = '\n'; |
607 | 6.01k | i++; |
608 | 6.01k | } |
609 | 6.01k | buf[i] = '\0'; |
610 | 6.01k | i++; |
611 | 6.01k | } |
612 | 4.02k | buf[i] = '\0'; // final sentinel |
613 | 4.02k | *error = false; |
614 | 4.02k | return buf; |
615 | 0 | } break; |
616 | | |
617 | 3.36k | case STREAM: { |
618 | 3.36k | if (!vm->trailingNewline) { |
619 | 0 | *error = true; |
620 | 0 | return from_string(vm, "INTERNAL: trailing-newline is required for streamed output"); |
621 | 0 | } |
622 | 3.36k | std::vector<std::string> documents = |
623 | 3.36k | jsonnet_vm_execute_stream(&alloc, |
624 | 3.36k | expr, |
625 | 3.36k | vm->ext, |
626 | 3.36k | max_stack, |
627 | 3.36k | vm->gcMinObjects, |
628 | 3.36k | vm->gcGrowthTrigger, |
629 | 3.36k | vm->nativeCallbacks, |
630 | 3.36k | vm->importCallback, |
631 | 3.36k | vm->importCallbackContext, |
632 | 3.36k | vm->stringOutput); |
633 | 3.36k | size_t sz = 1; // final sentinel |
634 | 577k | for (const auto &doc : documents) { |
635 | 577k | sz += doc.length() + 2; // Add a '\n' as well as sentinel |
636 | 577k | } |
637 | 3.36k | char *buf = (char *)::malloc(sz); |
638 | 3.36k | if (buf == nullptr) |
639 | 0 | memory_panic(); |
640 | 3.36k | std::ptrdiff_t i = 0; |
641 | 577k | for (const auto &doc : documents) { |
642 | 577k | memcpy(&buf[i], doc.c_str(), doc.length()); |
643 | 577k | i += doc.length(); |
644 | 577k | buf[i] = '\n'; |
645 | 577k | i++; |
646 | 577k | buf[i] = '\0'; |
647 | 577k | i++; |
648 | 577k | } |
649 | 3.36k | buf[i] = '\0'; // final sentinel |
650 | 3.36k | *error = false; |
651 | 3.36k | return buf; |
652 | 3.36k | } break; |
653 | | |
654 | 0 | default: |
655 | 0 | fputs("INTERNAL ERROR: bad value of 'kind', probably memory corruption.\n", stderr); |
656 | 0 | abort(); |
657 | 22.1k | } |
658 | | |
659 | 22.1k | } catch (StaticError &e) { |
660 | 11.8k | std::stringstream ss; |
661 | 11.8k | ss << "STATIC ERROR: " << e << std::endl; |
662 | 11.8k | *error = true; |
663 | 11.8k | return from_string(vm, ss.str()); |
664 | | |
665 | 11.8k | } catch (RuntimeError &e) { |
666 | 7.95k | std::stringstream ss; |
667 | 7.95k | ss << "RUNTIME ERROR: " << e.msg << std::endl; |
668 | 7.95k | const long max_above = vm->maxTrace / 2; |
669 | 7.95k | const long max_below = vm->maxTrace - max_above; |
670 | 7.95k | const long sz = e.stackTrace.size(); |
671 | 810k | for (long i = 0; i < sz; ++i) { |
672 | 802k | const auto &f = e.stackTrace[i]; |
673 | 802k | if (vm->maxTrace > 0 && i >= max_above && i < sz - max_below) { |
674 | 742k | if (i == max_above) |
675 | 2.04k | ss << "\t..." << std::endl; |
676 | 742k | } else { |
677 | 60.6k | ss << "\t" << f.location << "\t" << f.name << std::endl; |
678 | 60.6k | } |
679 | 802k | } |
680 | 7.95k | *error = true; |
681 | 7.95k | return from_string(vm, ss.str()); |
682 | 7.95k | } |
683 | | |
684 | 0 | return nullptr; // Quiet, compiler. |
685 | 22.1k | } |
686 | | |
687 | | static char *jsonnet_evaluate_file_aux(JsonnetVm *vm, const char *filename, int *error, |
688 | | EvalKind kind) |
689 | 0 | { |
690 | 0 | std::ifstream f; |
691 | 0 | f.open(filename); |
692 | 0 | if (!f.good()) { |
693 | 0 | std::stringstream ss; |
694 | 0 | ss << "Opening input file: " << filename << ": " << strerror(errno); |
695 | 0 | *error = true; |
696 | 0 | return from_string(vm, ss.str()); |
697 | 0 | } |
698 | 0 | std::string input; |
699 | 0 | input.assign(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()); |
700 | |
|
701 | 0 | return jsonnet_evaluate_snippet_aux(vm, filename, input.c_str(), error, kind); |
702 | 0 | } |
703 | | |
704 | | char *jsonnet_evaluate_file(JsonnetVm *vm, const char *filename, int *error) |
705 | 0 | { |
706 | 0 | TRY |
707 | 0 | return jsonnet_evaluate_file_aux(vm, filename, error, REGULAR); |
708 | 0 | CATCH("jsonnet_evaluate_file") |
709 | 0 | return nullptr; // Never happens. |
710 | 0 | } |
711 | | |
712 | | char *jsonnet_evaluate_file_multi(JsonnetVm *vm, const char *filename, int *error) |
713 | 0 | { |
714 | 0 | TRY |
715 | 0 | return jsonnet_evaluate_file_aux(vm, filename, error, MULTI); |
716 | 0 | CATCH("jsonnet_evaluate_file_multi") |
717 | 0 | return nullptr; // Never happens. |
718 | 0 | } |
719 | | |
720 | | char *jsonnet_evaluate_file_stream(JsonnetVm *vm, const char *filename, int *error) |
721 | 0 | { |
722 | 0 | TRY |
723 | 0 | return jsonnet_evaluate_file_aux(vm, filename, error, STREAM); |
724 | 0 | CATCH("jsonnet_evaluate_file_stream") |
725 | 0 | return nullptr; // Never happens. |
726 | 0 | } |
727 | | |
728 | | char *jsonnet_evaluate_snippet(JsonnetVm *vm, const char *filename, const char *snippet, int *error) |
729 | 6.45k | { |
730 | 6.45k | TRY |
731 | 6.45k | return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, REGULAR); |
732 | 6.45k | CATCH("jsonnet_evaluate_snippet") |
733 | 0 | return nullptr; // Never happens. |
734 | 6.45k | } |
735 | | |
736 | | char *jsonnet_evaluate_snippet_multi(JsonnetVm *vm, const char *filename, const char *snippet, |
737 | | int *error) |
738 | 8.31k | { |
739 | 8.31k | TRY |
740 | 8.31k | return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, MULTI); |
741 | 8.31k | CATCH("jsonnet_evaluate_snippet_multi") |
742 | 0 | return nullptr; // Never happens. |
743 | 8.31k | } |
744 | | |
745 | | char *jsonnet_evaluate_snippet_stream(JsonnetVm *vm, const char *filename, const char *snippet, |
746 | | int *error) |
747 | 7.33k | { |
748 | 7.33k | TRY |
749 | 7.33k | return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, STREAM); |
750 | 7.33k | CATCH("jsonnet_evaluate_snippet_stream") |
751 | 0 | return nullptr; // Never happens. |
752 | 7.33k | } |
753 | | |
754 | | char *jsonnet_realloc(JsonnetVm *vm, char *str, size_t sz) |
755 | 43.0k | { |
756 | 43.0k | (void)vm; |
757 | 43.0k | if (str == nullptr) { |
758 | 20.9k | if (sz == 0) |
759 | 0 | return nullptr; |
760 | 20.9k | auto *r = static_cast<char *>(::malloc(sz)); |
761 | 20.9k | if (r == nullptr) |
762 | 0 | memory_panic(); |
763 | 20.9k | return r; |
764 | 22.1k | } else { |
765 | 22.1k | if (sz == 0) { |
766 | 22.1k | ::free(str); |
767 | 22.1k | return nullptr; |
768 | 22.1k | } else { |
769 | 0 | auto *r = static_cast<char *>(::realloc(str, sz)); |
770 | 0 | if (r == nullptr) |
771 | 0 | memory_panic(); |
772 | 0 | return r; |
773 | 0 | } |
774 | 22.1k | } |
775 | 43.0k | } |