Coverage Report

Created: 2026-09-13 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
11.9k
{
91
11.9k
    char *r = jsonnet_realloc(vm, nullptr, v.length() + 1);
92
11.9k
    std::memcpy(r, v.c_str(), v.length() + 1);
93
11.9k
    return r;
94
11.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
12.8k
        : gcGrowthTrigger(2.0),
229
12.8k
          maxStack(500),
230
12.8k
          gcMinObjects(1000),
231
12.8k
          maxTrace(20),
232
12.8k
          importCallback(default_import_callback),
233
12.8k
          importCallbackContext(this),
234
12.8k
          stringOutput(false),
235
12.8k
          trailingNewline(true),
236
12.8k
          fmtDebugDesugaring(false)
237
12.8k
    {
238
12.8k
        jpaths.emplace_back("/usr/share/jsonnet-" + std::string(jsonnet_version()) + "/");
239
12.8k
        jpaths.emplace_back("/usr/local/share/jsonnet-" + std::string(jsonnet_version()) + "/");
240
12.8k
    }
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
38.4k
#define TRY try {
320
#define CATCH(func)                                                                           \
321
38.4k
    }                                                                                         \
322
38.4k
    catch (const std::bad_alloc &)                                                            \
323
38.4k
    {                                                                                         \
324
0
        memory_panic();                                                                       \
325
0
    }                                                                                         \
326
38.4k
    catch (const std::exception &e)                                                           \
327
38.4k
    {                                                                                         \
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
25.6k
{
335
25.6k
    return LIB_JSONNET_VERSION;
336
25.6k
}
337
338
JsonnetVm *jsonnet_make(void)
339
12.8k
{
340
12.8k
    TRY
341
12.8k
        return new JsonnetVm();
342
12.8k
    CATCH("jsonnet_make")
343
0
    return nullptr;
344
12.8k
}
345
346
void jsonnet_destroy(JsonnetVm *vm)
347
12.8k
{
348
12.8k
    TRY
349
12.8k
        delete vm;
350
12.8k
    CATCH("jsonnet_destroy")
351
12.8k
}
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
12.8k
{
380
12.8k
    vm->importCallback = cb;
381
12.8k
    vm->importCallbackContext = ctx;
382
12.8k
}
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
12.8k
{
542
12.8k
    try {
543
12.8k
        Allocator alloc;
544
12.8k
        AST *expr;
545
12.8k
        Tokens tokens = jsonnet_lex(filename, snippet);
546
547
12.8k
        expr = jsonnet_parse(&alloc, tokens);
548
549
12.8k
        jsonnet_desugar(&alloc, expr, &vm->tla);
550
551
12.8k
        unsigned max_stack = vm->maxStack;
552
553
        // For the stdlib desugaring.
554
12.8k
        max_stack++;
555
556
        // For the TLA desugaring.
557
12.8k
        max_stack++;
558
559
12.8k
        jsonnet_static_analysis(expr);
560
12.8k
        switch (kind) {
561
0
            case REGULAR: {
562
0
                std::string json_str = jsonnet_vm_execute(&alloc,
563
0
                                                          expr,
564
0
                                                          vm->ext,
565
0
                                                          max_stack,
566
0
                                                          vm->gcMinObjects,
567
0
                                                          vm->gcGrowthTrigger,
568
0
                                                          vm->nativeCallbacks,
569
0
                                                          vm->importCallback,
570
0
                                                          vm->importCallbackContext,
571
0
                                                          vm->stringOutput);
572
0
                if (vm->trailingNewline) {
573
0
                    json_str += "\n";
574
0
                }
575
0
                *error = false;
576
0
                return from_string(vm, json_str);
577
0
            } break;
578
579
2.86k
            case MULTI: {
580
2.86k
                std::map<std::string, std::string> files =
581
2.86k
                    jsonnet_vm_execute_multi(&alloc,
582
2.86k
                                             expr,
583
2.86k
                                             vm->ext,
584
2.86k
                                             max_stack,
585
2.86k
                                             vm->gcMinObjects,
586
2.86k
                                             vm->gcGrowthTrigger,
587
2.86k
                                             vm->nativeCallbacks,
588
2.86k
                                             vm->importCallback,
589
2.86k
                                             vm->importCallbackContext,
590
2.86k
                                             vm->stringOutput);
591
2.86k
                size_t sz = 1;  // final sentinel
592
3.31k
                for (const auto &pair : files) {
593
3.31k
                    sz += pair.first.length() + 1;   // include sentinel
594
3.31k
                    sz += pair.second.length() + 2;  // Add a '\n' as well as sentinel
595
3.31k
                }
596
2.86k
                char *buf = (char *)::malloc(sz);
597
2.86k
                if (buf == nullptr)
598
0
                    memory_panic();
599
2.86k
                std::ptrdiff_t i = 0;
600
3.31k
                for (const auto &pair : files) {
601
3.31k
                    memcpy(&buf[i], pair.first.c_str(), pair.first.length() + 1);
602
3.31k
                    i += pair.first.length() + 1;
603
3.31k
                    memcpy(&buf[i], pair.second.c_str(), pair.second.length());
604
3.31k
                    i += pair.second.length();
605
3.31k
                    if (vm->trailingNewline) {
606
3.31k
                        buf[i] = '\n';
607
3.31k
                        i++;
608
3.31k
                    }
609
3.31k
                    buf[i] = '\0';
610
3.31k
                    i++;
611
3.31k
                }
612
2.86k
                buf[i] = '\0';  // final sentinel
613
2.86k
                *error = false;
614
2.86k
                return buf;
615
0
            } break;
616
617
3.03k
            case STREAM: {
618
3.03k
                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.03k
                std::vector<std::string> documents =
623
3.03k
                    jsonnet_vm_execute_stream(&alloc,
624
3.03k
                                              expr,
625
3.03k
                                              vm->ext,
626
3.03k
                                              max_stack,
627
3.03k
                                              vm->gcMinObjects,
628
3.03k
                                              vm->gcGrowthTrigger,
629
3.03k
                                              vm->nativeCallbacks,
630
3.03k
                                              vm->importCallback,
631
3.03k
                                              vm->importCallbackContext,
632
3.03k
                                              vm->stringOutput);
633
3.03k
                size_t sz = 1;  // final sentinel
634
711k
                for (const auto &doc : documents) {
635
711k
                    sz += doc.length() + 2;  // Add a '\n' as well as sentinel
636
711k
                }
637
3.03k
                char *buf = (char *)::malloc(sz);
638
3.03k
                if (buf == nullptr)
639
0
                    memory_panic();
640
3.03k
                std::ptrdiff_t i = 0;
641
711k
                for (const auto &doc : documents) {
642
711k
                    memcpy(&buf[i], doc.c_str(), doc.length());
643
711k
                    i += doc.length();
644
711k
                    buf[i] = '\n';
645
711k
                    i++;
646
711k
                    buf[i] = '\0';
647
711k
                    i++;
648
711k
                }
649
3.03k
                buf[i] = '\0';  // final sentinel
650
3.03k
                *error = false;
651
3.03k
                return buf;
652
3.03k
            } break;
653
654
0
            default:
655
0
                fputs("INTERNAL ERROR: bad value of 'kind', probably memory corruption.\n", stderr);
656
0
                abort();
657
12.8k
        }
658
659
12.8k
    } catch (StaticError &e) {
660
6.91k
        std::stringstream ss;
661
6.91k
        ss << "STATIC ERROR: " << e << std::endl;
662
6.91k
        *error = true;
663
6.91k
        return from_string(vm, ss.str());
664
665
6.91k
    } catch (RuntimeError &e) {
666
5.03k
        std::stringstream ss;
667
5.03k
        ss << "RUNTIME ERROR: " << e.msg << std::endl;
668
5.03k
        const long max_above = vm->maxTrace / 2;
669
5.03k
        const long max_below = vm->maxTrace - max_above;
670
5.03k
        const long sz = e.stackTrace.size();
671
452k
        for (long i = 0; i < sz; ++i) {
672
447k
            const auto &f = e.stackTrace[i];
673
447k
            if (vm->maxTrace > 0 && i >= max_above && i < sz - max_below) {
674
413k
                if (i == max_above)
675
1.13k
                    ss << "\t..." << std::endl;
676
413k
            } else {
677
34.0k
                ss << "\t" << f.location << "\t" << f.name << std::endl;
678
34.0k
            }
679
447k
        }
680
5.03k
        *error = true;
681
5.03k
        return from_string(vm, ss.str());
682
5.03k
    }
683
684
0
    return nullptr;  // Quiet, compiler.
685
12.8k
}
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
0
{
730
0
    TRY
731
0
        return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, REGULAR);
732
0
    CATCH("jsonnet_evaluate_snippet")
733
0
    return nullptr;  // Never happens.
734
0
}
735
736
char *jsonnet_evaluate_snippet_multi(JsonnetVm *vm, const char *filename, const char *snippet,
737
                                     int *error)
738
6.25k
{
739
6.25k
    TRY
740
6.25k
        return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, MULTI);
741
6.25k
    CATCH("jsonnet_evaluate_snippet_multi")
742
0
    return nullptr;  // Never happens.
743
6.25k
}
744
745
char *jsonnet_evaluate_snippet_stream(JsonnetVm *vm, const char *filename, const char *snippet,
746
                                      int *error)
747
6.55k
{
748
6.55k
    TRY
749
6.55k
        return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, STREAM);
750
6.55k
    CATCH("jsonnet_evaluate_snippet_stream")
751
0
    return nullptr;  // Never happens.
752
6.55k
}
753
754
char *jsonnet_realloc(JsonnetVm *vm, char *str, size_t sz)
755
24.7k
{
756
24.7k
    (void)vm;
757
24.7k
    if (str == nullptr) {
758
11.9k
        if (sz == 0)
759
0
            return nullptr;
760
11.9k
        auto *r = static_cast<char *>(::malloc(sz));
761
11.9k
        if (r == nullptr)
762
0
            memory_panic();
763
11.9k
        return r;
764
12.8k
    } else {
765
12.8k
        if (sz == 0) {
766
12.8k
            ::free(str);
767
12.8k
            return nullptr;
768
12.8k
        } 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
12.8k
    }
775
24.7k
}