Coverage Report

Created: 2025-09-27 07:08

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