Coverage Report

Created: 2026-08-13 06:16

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