Coverage Report

Created: 2026-06-08 06:30

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
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
}