Coverage Report

Created: 2025-08-26 06:57

/src/jsonnet/core/libjsonnet.cpp
Line
Count
Source (jump to first uncovered line)
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
22.5k
{
91
22.5k
    char *r = jsonnet_realloc(vm, nullptr, v.length() + 1);
92
22.5k
    std::memcpy(r, v.c_str(), v.length() + 1);
93
22.5k
    return r;
94
22.5k
}
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
23.6k
        : gcGrowthTrigger(2.0),
228
23.6k
          maxStack(500),
229
23.6k
          gcMinObjects(1000),
230
23.6k
          maxTrace(20),
231
23.6k
          importCallback(default_import_callback),
232
23.6k
          importCallbackContext(this),
233
23.6k
          stringOutput(false),
234
23.6k
          fmtDebugDesugaring(false)
235
23.6k
    {
236
23.6k
        jpaths.emplace_back("/usr/share/jsonnet-" + std::string(jsonnet_version()) + "/");
237
23.6k
        jpaths.emplace_back("/usr/local/share/jsonnet-" + std::string(jsonnet_version()) + "/");
238
23.6k
    }
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
70.8k
#define TRY try {
318
#define CATCH(func)                                                                           \
319
70.8k
    }                                                                                         \
320
70.8k
    catch (const std::bad_alloc &)                                                            \
321
70.8k
    {                                                                                         \
322
0
        memory_panic();                                                                       \
323
0
    }                                                                                         \
324
70.8k
    catch (const std::exception &e)                                                           \
325
70.8k
    {                                                                                         \
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
47.2k
{
333
47.2k
    return LIB_JSONNET_VERSION;
334
47.2k
}
335
336
JsonnetVm *jsonnet_make(void)
337
23.6k
{
338
23.6k
    TRY
339
23.6k
        return new JsonnetVm();
340
23.6k
    CATCH("jsonnet_make")
341
0
    return nullptr;
342
23.6k
}
343
344
void jsonnet_destroy(JsonnetVm *vm)
345
23.6k
{
346
23.6k
    TRY
347
23.6k
        delete vm;
348
23.6k
    CATCH("jsonnet_destroy")
349
23.6k
}
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
23.6k
{
373
23.6k
    vm->importCallback = cb;
374
23.6k
    vm->importCallbackContext = ctx;
375
23.6k
}
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
23.6k
{
535
23.6k
    try {
536
23.6k
        Allocator alloc;
537
23.6k
        AST *expr;
538
23.6k
        Tokens tokens = jsonnet_lex(filename, snippet);
539
540
23.6k
        expr = jsonnet_parse(&alloc, tokens);
541
542
23.6k
        jsonnet_desugar(&alloc, expr, &vm->tla);
543
544
23.6k
        unsigned max_stack = vm->maxStack;
545
546
        // For the stdlib desugaring.
547
23.6k
        max_stack++;
548
549
        // For the TLA desugaring.
550
23.6k
        max_stack++;
551
552
23.6k
        jsonnet_static_analysis(expr);
553
23.6k
        switch (kind) {
554
3.18k
            case REGULAR: {
555
3.18k
                std::string json_str = jsonnet_vm_execute(&alloc,
556
3.18k
                                                          expr,
557
3.18k
                                                          vm->ext,
558
3.18k
                                                          max_stack,
559
3.18k
                                                          vm->gcMinObjects,
560
3.18k
                                                          vm->gcGrowthTrigger,
561
3.18k
                                                          vm->nativeCallbacks,
562
3.18k
                                                          vm->importCallback,
563
3.18k
                                                          vm->importCallbackContext,
564
3.18k
                                                          vm->stringOutput);
565
3.18k
                json_str += "\n";
566
3.18k
                *error = false;
567
3.18k
                return from_string(vm, json_str);
568
0
            } break;
569
570
3.48k
            case MULTI: {
571
3.48k
                std::map<std::string, std::string> files =
572
3.48k
                    jsonnet_vm_execute_multi(&alloc,
573
3.48k
                                             expr,
574
3.48k
                                             vm->ext,
575
3.48k
                                             max_stack,
576
3.48k
                                             vm->gcMinObjects,
577
3.48k
                                             vm->gcGrowthTrigger,
578
3.48k
                                             vm->nativeCallbacks,
579
3.48k
                                             vm->importCallback,
580
3.48k
                                             vm->importCallbackContext,
581
3.48k
                                             vm->stringOutput);
582
3.48k
                size_t sz = 1;  // final sentinel
583
5.06k
                for (const auto &pair : files) {
584
5.06k
                    sz += pair.first.length() + 1;   // include sentinel
585
5.06k
                    sz += pair.second.length() + 2;  // Add a '\n' as well as sentinel
586
5.06k
                }
587
3.48k
                char *buf = (char *)::malloc(sz);
588
3.48k
                if (buf == nullptr)
589
0
                    memory_panic();
590
3.48k
                std::ptrdiff_t i = 0;
591
5.06k
                for (const auto &pair : files) {
592
5.06k
                    memcpy(&buf[i], pair.first.c_str(), pair.first.length() + 1);
593
5.06k
                    i += pair.first.length() + 1;
594
5.06k
                    memcpy(&buf[i], pair.second.c_str(), pair.second.length());
595
5.06k
                    i += pair.second.length();
596
5.06k
                    buf[i] = '\n';
597
5.06k
                    i++;
598
5.06k
                    buf[i] = '\0';
599
5.06k
                    i++;
600
5.06k
                }
601
3.48k
                buf[i] = '\0';  // final sentinel
602
3.48k
                *error = false;
603
3.48k
                return buf;
604
0
            } break;
605
606
3.13k
            case STREAM: {
607
3.13k
                std::vector<std::string> documents =
608
3.13k
                    jsonnet_vm_execute_stream(&alloc,
609
3.13k
                                              expr,
610
3.13k
                                              vm->ext,
611
3.13k
                                              max_stack,
612
3.13k
                                              vm->gcMinObjects,
613
3.13k
                                              vm->gcGrowthTrigger,
614
3.13k
                                              vm->nativeCallbacks,
615
3.13k
                                              vm->importCallback,
616
3.13k
                                              vm->importCallbackContext,
617
3.13k
                                              vm->stringOutput);
618
3.13k
                size_t sz = 1;  // final sentinel
619
1.13M
                for (const auto &doc : documents) {
620
1.13M
                    sz += doc.length() + 2;  // Add a '\n' as well as sentinel
621
1.13M
                }
622
3.13k
                char *buf = (char *)::malloc(sz);
623
3.13k
                if (buf == nullptr)
624
0
                    memory_panic();
625
3.13k
                std::ptrdiff_t i = 0;
626
1.13M
                for (const auto &doc : documents) {
627
1.13M
                    memcpy(&buf[i], doc.c_str(), doc.length());
628
1.13M
                    i += doc.length();
629
1.13M
                    buf[i] = '\n';
630
1.13M
                    i++;
631
1.13M
                    buf[i] = '\0';
632
1.13M
                    i++;
633
1.13M
                }
634
3.13k
                buf[i] = '\0';  // final sentinel
635
3.13k
                *error = false;
636
3.13k
                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
23.6k
        }
643
644
23.6k
    } catch (StaticError &e) {
645
13.7k
        std::stringstream ss;
646
13.7k
        ss << "STATIC ERROR: " << e << std::endl;
647
13.7k
        *error = true;
648
13.7k
        return from_string(vm, ss.str());
649
650
13.7k
    } catch (RuntimeError &e) {
651
7.32k
        std::stringstream ss;
652
7.32k
        ss << "RUNTIME ERROR: " << e.msg << std::endl;
653
7.32k
        const long max_above = vm->maxTrace / 2;
654
7.32k
        const long max_below = vm->maxTrace - max_above;
655
7.32k
        const long sz = e.stackTrace.size();
656
740k
        for (long i = 0; i < sz; ++i) {
657
732k
            const auto &f = e.stackTrace[i];
658
732k
            if (vm->maxTrace > 0 && i >= max_above && i < sz - max_below) {
659
678k
                if (i == max_above)
660
1.83k
                    ss << "\t..." << std::endl;
661
678k
            } else {
662
54.2k
                ss << "\t" << f.location << "\t" << f.name << std::endl;
663
54.2k
            }
664
732k
        }
665
7.32k
        *error = true;
666
7.32k
        return from_string(vm, ss.str());
667
7.32k
    }
668
669
0
    return nullptr;  // Quiet, compiler.
670
23.6k
}
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.81k
{
715
7.81k
    TRY
716
7.81k
        return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, REGULAR);
717
7.81k
    CATCH("jsonnet_evaluate_snippet")
718
0
    return nullptr;  // Never happens.
719
7.81k
}
720
721
char *jsonnet_evaluate_snippet_multi(JsonnetVm *vm, const char *filename, const char *snippet,
722
                                     int *error)
723
8.20k
{
724
8.20k
    TRY
725
8.20k
        return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, MULTI);
726
8.20k
    CATCH("jsonnet_evaluate_snippet_multi")
727
0
    return nullptr;  // Never happens.
728
8.20k
}
729
730
char *jsonnet_evaluate_snippet_stream(JsonnetVm *vm, const char *filename, const char *snippet,
731
                                      int *error)
732
7.57k
{
733
7.57k
    TRY
734
7.57k
        return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, STREAM);
735
7.57k
    CATCH("jsonnet_evaluate_snippet_stream")
736
0
    return nullptr;  // Never happens.
737
7.57k
}
738
739
char *jsonnet_realloc(JsonnetVm *vm, char *str, size_t sz)
740
46.1k
{
741
46.1k
    (void)vm;
742
46.1k
    if (str == nullptr) {
743
22.5k
        if (sz == 0)
744
0
            return nullptr;
745
22.5k
        auto *r = static_cast<char *>(::malloc(sz));
746
22.5k
        if (r == nullptr)
747
0
            memory_panic();
748
22.5k
        return r;
749
23.6k
    } else {
750
23.6k
        if (sz == 0) {
751
23.6k
            ::free(str);
752
23.6k
            return nullptr;
753
23.6k
        } 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
23.6k
    }
760
46.1k
}