Coverage Report

Created: 2026-09-13 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jsonnet/core/vm.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 <cassert>
18
#include <cmath>
19
#include <cstdlib>
20
21
#include <memory>
22
#include <set>
23
#include <string>
24
#include <string_view>
25
26
#include "ast.h"
27
#include "desugarer.h"
28
#include "json.h"
29
#include <nlohmann/json.hpp>
30
#include "md5.h"
31
#include "parser.h"
32
#ifdef USE_SYSTEM_RAPIDYAML
33
#include <ryml.hpp>
34
#include <ryml_std.hpp>
35
#else
36
#include "rapidyaml-0.10.0.hpp"
37
#endif
38
#include "state.h"
39
#include "static_analysis.h"
40
#include "static_error.h"
41
#include "string_utils.h"
42
#include "vm.h"
43
#include "path_utils.h"
44
#include "unicode.h"
45
46
namespace jsonnet::internal {
47
48
/** Macro that dumps an error and aborts the program regardless of
49
 *  whether NDEBUG is defined. This should be used to mark codepaths
50
 *  as unreachable.
51
 */
52
#define JSONNET_UNREACHABLE()                                      \
53
0
  do {                                                             \
54
0
    std::cerr << __FILE__ << ":" << __LINE__                       \
55
0
              << ": INTERNAL ERROR: reached unreachable code path" \
56
0
              << std::endl;                                        \
57
0
    abort();                                                       \
58
0
  } while (0)
59
60
using json = nlohmann::json;
61
62
namespace {
63
64
// Custom exception type thrown by RapidYAML error handling callback.
65
// Should be caught and converted to a runtime error.
66
struct RapidYamlError {
67
    // Construct any way that std::string can be constructed.
68
    template <typename... Args>
69
0
    explicit RapidYamlError(Args&&... args): msg_(std::forward<Args>(args)...) {}
70
    std::string msg_;
71
};
72
73
/** Stack frames.
74
 *
75
 * Of these, FRAME_CALL is the most special, as it is the only frame the stack
76
 * trace (for errors) displays.
77
 */
78
enum FrameKind {
79
    FRAME_APPLY_TARGET,          // e in e(...)
80
    FRAME_BINARY_LEFT,           // a in a + b
81
    FRAME_BINARY_RIGHT,          // b in a + b
82
    FRAME_BINARY_OP,             // a + b, with a and b already calculated
83
    FRAME_BUILTIN_FILTER,        // When executing std.filter, used to hold intermediate state.
84
    FRAME_BUILTIN_FORCE_THUNKS,  // When forcing builtin args, holds intermediate state.
85
    FRAME_CALL,                  // Used any time we have switched location in user code.
86
    FRAME_ERROR,                 // e in error e
87
    FRAME_IF,                    // e in if e then a else b
88
    FRAME_IN_SUPER_ELEMENT,      // e in 'e in super'
89
    FRAME_INDEX_TARGET,          // e in e[x]
90
    FRAME_INDEX_INDEX,           // e in x[e]
91
    FRAME_INVARIANTS,            // Caches the thunks that need to be executed one at a time.
92
    FRAME_LOCAL,                 // Stores thunk bindings as we execute e in local ...; e
93
    FRAME_OBJECT,  // Stores intermediate state as we execute es in { [e]: ..., [e]: ... }
94
    FRAME_OBJECT_COMP_ARRAY,    // e in {f:a for x in e]
95
    FRAME_OBJECT_COMP_ELEMENT,  // Stores intermediate state when building object
96
    FRAME_STRING_CONCAT,        // Stores intermediate state while co-ercing objects
97
    FRAME_SUPER_INDEX,          // e in super[e]
98
    FRAME_UNARY,                // e in -e
99
    FRAME_BUILTIN_JOIN_STRINGS, // When executing std.join over strings, used to hold intermediate state.
100
    FRAME_BUILTIN_JOIN_ARRAYS,  // When executing std.join over arrays, used to hold intermediate state.
101
    FRAME_BUILTIN_DECODE_UTF8,  // When executing std.decodeUTF8, used to hold intermediate state.
102
};
103
104
/** A frame on the stack.
105
 *
106
 * Every time a subterm is evaluated, we first push a new stack frame to
107
 * store the continuation.
108
 *
109
 * The stack frame is a bit like a tagged union, except not as memory
110
 * efficient.  The set of member variables that are actually used depends on
111
 * the value of the member variable kind.
112
 *
113
 * If the stack frame is of kind FRAME_CALL, then it counts towards the
114
 * maximum number of stack frames allowed.  Other stack frames are not
115
 * counted.  This is because FRAME_CALL exists where there is a branch in
116
 * the code, e.g. the forcing of a thunk, evaluation of a field, calling a
117
 * function, etc.
118
 *
119
 * The stack is used to mark objects during garbage
120
 * collection, so HeapObjects not referred to from the stack may be
121
 * prematurely collected.
122
 */
123
struct Frame {
124
    /** Tag (tagged union). */
125
    FrameKind kind;
126
127
    /** The code we were executing before. */
128
    const AST *ast;
129
130
    /** The location of the code we were executing before.
131
     *
132
     * location == ast->location when ast != nullptr
133
     */
134
    LocationRange location;
135
136
    /** Reuse this stack frame for the purpose of tail call optimization. */
137
    bool tailCall;
138
139
    /** Used for a variety of purposes. */
140
    Value val;
141
142
    /** Used for a variety of purposes. */
143
    Value val2;
144
145
    /** Used for a variety of purposes. */
146
    DesugaredObject::Fields::const_iterator fit;
147
148
    /** Used for a variety of purposes. */
149
    std::map<const Identifier *, HeapSimpleObject::Field> objectFields;
150
151
    /** Used for a variety of purposes. */
152
    unsigned elementId;
153
154
    /** Used for a variety of purposes. */
155
    std::map<const Identifier *, HeapThunk *> elements;
156
157
    /** Used for a variety of purposes. */
158
    std::vector<HeapThunk *> thunks;
159
160
    /** Used for accumulating a joined string. */
161
    UString str;
162
    bool first;
163
164
    /** Used for accumulating bytes */
165
    std::string bytes;
166
167
    /** The context is used in error messages to attempt to find a reasonable name for the
168
     * object, function, or thunk value being executed.  If it is a thunk, it is filled
169
     * with the value when the frame terminates.
170
     */
171
    HeapEntity *context;
172
173
    /** The lexically nearest object we are in, or nullptr.  Note
174
     * that this is not the same as context, because we could be inside a function,
175
     * inside an object and then context would be the function, but self would still point
176
     * to the object.
177
     */
178
    HeapObject *self;
179
180
    /** The "super" level of self.  Sometimes, we look upwards in the
181
     * inheritance tree, e.g. via an explicit use of super, or because a given field
182
     * has been inherited.  When evaluating a field from one of these super objects,
183
     * we need to bind self to the concrete object (so self must point
184
     * there) but uses of super should be resolved relative to the object whose
185
     * field we are evaluating.  Thus, we keep a second field for that.  This is
186
     * usually 0, unless we are evaluating a super object's field.
187
     */
188
    unsigned offset;
189
190
    /** A set of variables introduced at this point. */
191
    BindingFrame bindings;
192
193
    Frame(const FrameKind &kind, const AST *ast)
194
83.3M
        : kind(kind),
195
83.3M
          ast(ast),
196
83.3M
          location(ast->location),
197
83.3M
          tailCall(false),
198
83.3M
          elementId(0),
199
83.3M
          first(false),
200
83.3M
          context(NULL),
201
83.3M
          self(NULL),
202
83.3M
          offset(0)
203
83.3M
    {
204
83.3M
        val.t = Value::NULL_TYPE;
205
83.3M
        val2.t = Value::NULL_TYPE;
206
83.3M
    }
207
208
    Frame(const FrameKind &kind, const LocationRange &location)
209
80.7M
        : kind(kind),
210
80.7M
          ast(nullptr),
211
80.7M
          location(location),
212
80.7M
          tailCall(false),
213
80.7M
          elementId(0),
214
80.7M
          first(false),
215
80.7M
          context(NULL),
216
80.7M
          self(NULL),
217
80.7M
          offset(0)
218
80.7M
    {
219
80.7M
        val.t = Value::NULL_TYPE;
220
80.7M
        val2.t = Value::NULL_TYPE;
221
80.7M
    }
222
223
    /** Mark everything visible from this frame. */
224
    void mark(Heap &heap) const
225
35.8M
    {
226
35.8M
        heap.markFrom(val);
227
35.8M
        heap.markFrom(val2);
228
35.8M
        if (context)
229
25.0M
            heap.markFrom(context);
230
35.8M
        if (self)
231
24.7M
            heap.markFrom(self);
232
35.8M
        for (const auto &bind : bindings)
233
34.2M
            heap.markFrom(bind.second);
234
35.8M
        for (const auto &el : elements)
235
0
            heap.markFrom(el.second);
236
35.8M
        for (const auto &th : thunks)
237
3.33M
            heap.markFrom(th);
238
35.8M
    }
239
240
    bool isCall(void) const
241
983M
    {
242
983M
        return kind == FRAME_CALL;
243
983M
    }
244
};
245
246
/** The stack holds all the stack frames and manages the stack frame limit. */
247
class Stack {
248
    /** How many call frames are on the stack. */
249
    unsigned calls;
250
251
    /** How many call frames should be allowed before aborting the program. */
252
    unsigned limit;
253
254
    /** The stack frames. */
255
    std::vector<Frame> stack;
256
257
   public:
258
5.89k
    Stack(unsigned limit) : calls(0), limit(limit) {}
259
260
5.89k
    ~Stack(void) {}
261
262
    unsigned size(void)
263
239M
    {
264
239M
        return stack.size();
265
239M
    }
266
267
    /** Search for the closest variable in scope that matches the given name. */
268
    HeapThunk *lookUpVar(const Identifier *id)
269
1.48G
    {
270
2.14G
        for (int i = stack.size() - 1; i >= 0; --i) {
271
2.14G
            const auto &binds = stack[i].bindings;
272
2.14G
            auto it = binds.find(id);
273
2.14G
            if (it != binds.end()) {
274
1.48G
                return it->second;
275
1.48G
            }
276
660M
            if (stack[i].isCall())
277
0
                break;
278
660M
        }
279
0
        return nullptr;
280
1.48G
    }
281
282
    /** Mark everything visible from the stack (any frame). */
283
    void mark(Heap &heap)
284
154k
    {
285
35.8M
        for (const auto &f : stack) {
286
35.8M
            f.mark(heap);
287
35.8M
        }
288
154k
    }
289
290
    Frame &top(void)
291
845M
    {
292
845M
        return stack.back();
293
845M
    }
294
295
    const Frame &top(void) const
296
0
    {
297
0
        return stack.back();
298
0
    }
299
300
    void pop(void)
301
160M
    {
302
160M
        if (top().isCall())
303
63.5M
            calls--;
304
160M
        stack.pop_back();
305
160M
    }
306
307
    /** Attempt to find a name for a given heap entity.  This may not be possible, but we try
308
     * reasonably hard.  We look in the bindings for a variable in the closest scope that
309
     * happens to point at the entity in question.  Otherwise, the best we can do is use its
310
     * type.
311
     */
312
    std::string getName(unsigned from_here, const HeapEntity *e)
313
555k
    {
314
555k
        std::string name;
315
2.25M
        for (int i = from_here - 1; i >= 0; --i) {
316
2.24M
            const auto &f = stack[i];
317
2.24M
            for (const auto &pair : f.bindings) {
318
915k
                HeapThunk *thunk = pair.second;
319
915k
                if (!thunk->filled)
320
208k
                    continue;
321
707k
                if (!thunk->content.isHeap())
322
91.6k
                    continue;
323
615k
                if (e != thunk->content.v.h)
324
544k
                    continue;
325
71.6k
                name = encode_utf8(pair.first->name);
326
71.6k
            }
327
            // Do not go into the next call frame, keep local reasoning.
328
2.24M
            if (f.isCall())
329
552k
                break;
330
2.24M
        }
331
332
555k
        if (name == "")
333
484k
            name = "anonymous";
334
555k
        if (dynamic_cast<const HeapObject *>(e)) {
335
373k
            return "object <" + name + ">";
336
373k
        } else if (auto *thunk = dynamic_cast<const HeapThunk *>(e)) {
337
96.8k
            if (thunk->name == nullptr) {
338
12.3k
                return "";  // Argument of builtin, or root (since top level functions).
339
84.5k
            } else {
340
84.5k
                return "thunk <" + encode_utf8(thunk->name->name) + ">";
341
84.5k
            }
342
96.8k
        } else {
343
85.5k
            const auto *func = static_cast<const HeapClosure *>(e);
344
85.5k
            if (func->isBuiltin()) {
345
0
                return "builtin function <" + func->builtinName + ">";
346
0
            }
347
85.5k
            return "function <" + name + ">";
348
85.5k
        }
349
555k
    }
350
351
    /** Dump the stack.
352
     *
353
     * This is useful to help debug the VM in gdb.  It is virtual to stop it
354
     * being removed by the compiler.
355
     */
356
    virtual void dump(void)
357
0
    {
358
0
        for (std::size_t i = 0; i < stack.size(); ++i) {
359
0
            std::cout << "stack[" << i << "] = " << stack[i].location << " (" << stack[i].kind
360
0
                      << ")" << std::endl;
361
0
        }
362
0
        std::cout << std::endl;
363
0
    }
364
365
    /** Creates the error object for throwing, and also populates it with the stack trace.
366
     */
367
    RuntimeError makeError(const LocationRange &loc, const std::string &msg)
368
5.03k
    {
369
5.03k
        std::vector<TraceFrame> stack_trace;
370
5.03k
        stack_trace.push_back(TraceFrame(loc));
371
2.27M
        for (int i = stack.size() - 1; i >= 0; --i) {
372
2.27M
            const auto &f = stack[i];
373
2.27M
            if (f.isCall()) {
374
555k
                if (f.context != nullptr) {
375
                    // Give the last line a name.
376
555k
                    stack_trace[stack_trace.size() - 1].name = getName(i, f.context);
377
555k
                }
378
555k
                if (f.location.isSet() || f.location.file.length() > 0)
379
442k
                    stack_trace.push_back(TraceFrame(f.location));
380
555k
            }
381
2.27M
        }
382
5.03k
        return RuntimeError(stack_trace, msg);
383
5.03k
    }
384
385
    /** New (non-call) frame. */
386
    template <class... Args>
387
    void newFrame(Args... args)
388
99.4M
    {
389
99.4M
        stack.emplace_back(args...);
390
99.4M
    }
vm.cpp:void jsonnet::internal::(anonymous namespace)::Stack::newFrame<jsonnet::internal::(anonymous namespace)::FrameKind, jsonnet::internal::AST const*>(jsonnet::internal::(anonymous namespace)::FrameKind, jsonnet::internal::AST const*)
Line
Count
Source
388
83.3M
    {
389
83.3M
        stack.emplace_back(args...);
390
83.3M
    }
vm.cpp:void jsonnet::internal::(anonymous namespace)::Stack::newFrame<jsonnet::internal::(anonymous namespace)::FrameKind, jsonnet::internal::LocationRange>(jsonnet::internal::(anonymous namespace)::FrameKind, jsonnet::internal::LocationRange)
Line
Count
Source
388
16.1M
    {
389
16.1M
        stack.emplace_back(args...);
390
16.1M
    }
391
392
    /** If there is a tailstrict annotated frame followed by some locals, pop them all. */
393
    void tailCallTrimStack(void)
394
64.5M
    {
395
65.8M
        for (int i = stack.size() - 1; i >= 0; --i) {
396
65.1M
            switch (stack[i].kind) {
397
21.3M
                case FRAME_CALL: {
398
21.3M
                    if (!stack[i].tailCall || stack[i].thunks.size() > 0) {
399
20.8M
                        return;
400
20.8M
                    }
401
                    // Remove all stack frames including this one.
402
1.95M
                    while (stack.size() > unsigned(i))
403
1.43M
                        stack.pop_back();
404
518k
                    calls--;
405
518k
                    return;
406
21.3M
                } break;
407
408
1.27M
                case FRAME_LOCAL: break;
409
410
42.4M
                default: return;
411
65.1M
            }
412
65.1M
        }
413
64.5M
    }
414
415
    /** New call frame. */
416
    void newCall(const LocationRange &loc, HeapEntity *context, HeapObject *self, unsigned offset,
417
                 const BindingFrame &up_values)
418
64.5M
    {
419
64.5M
        tailCallTrimStack();
420
64.5M
        if (calls >= limit) {
421
1.04k
            throw makeError(loc, "max stack frames exceeded.");
422
1.04k
        }
423
64.5M
        stack.emplace_back(FRAME_CALL, loc);
424
64.5M
        calls++;
425
64.5M
        top().context = context;
426
64.5M
        top().self = self;
427
64.5M
        top().offset = offset;
428
64.5M
        top().bindings = up_values;
429
64.5M
        top().tailCall = false;
430
431
64.5M
#ifndef NDEBUG
432
64.5M
        for (const auto &bind : up_values) {
433
62.9M
            if (bind.second == nullptr) {
434
0
                std::cerr << "INTERNAL ERROR: No binding for variable "
435
0
                          << encode_utf8(bind.first->name) << std::endl;
436
0
                std::abort();
437
0
            }
438
62.9M
        }
439
64.5M
#endif
440
64.5M
    }
441
442
    /** Look up the stack to find the self binding. */
443
    void getSelfBinding(HeapObject *&self, unsigned &offset)
444
48.5M
    {
445
48.5M
        self = nullptr;
446
48.5M
        offset = 0;
447
158M
        for (int i = stack.size() - 1; i >= 0; --i) {
448
158M
            if (stack[i].isCall()) {
449
48.4M
                self = stack[i].self;
450
48.4M
                offset = stack[i].offset;
451
48.4M
                return;
452
48.4M
            }
453
158M
        }
454
48.5M
    }
455
456
    /** Look up the stack to see if we're running assertions for this object. */
457
    bool alreadyExecutingInvariants(HeapObject *self)
458
16.1M
    {
459
4.56G
        for (int i = stack.size() - 1; i >= 0; --i) {
460
4.54G
            if (stack[i].kind == FRAME_INVARIANTS) {
461
13.2M
                if (stack[i].self == self)
462
45.0k
                    return true;
463
13.2M
            }
464
4.54G
        }
465
16.1M
        return false;
466
16.1M
    }
467
};
468
469
/** Typedef to save some typing. */
470
typedef std::map<std::string, VmExt> ExtMap;
471
472
/** Typedef to save some typing. */
473
typedef std::map<std::string, std::string> StrMap;
474
475
class Interpreter;
476
477
typedef const AST *(Interpreter::*BuiltinFunc)(const LocationRange &loc,
478
                                               const std::vector<Value> &args);
479
480
/** Holds the intermediate state during execution and implements the necessary functions to
481
 * implement the semantics of the language.
482
 *
483
 * The garbage collector used is a simple stop-the-world mark and sweep collector.  It runs upon
484
 * memory allocation if the heap is large enough and has grown enough since the last collection.
485
 * All reachable entities have their mark field incremented.  Then all entities with the old
486
 * mark are removed from the heap.
487
 */
488
class Interpreter {
489
    /** The heap. */
490
    Heap heap;
491
492
    /** The value last computed. */
493
    Value scratch;
494
495
    /** The stack. */
496
    Stack stack;
497
498
    /** Used to create ASTs if needed.
499
     *
500
     * This is used at import time, and in a few other cases.
501
     */
502
    Allocator *alloc;
503
504
    /** Used to "name" thunks created to cache imports. */
505
    const Identifier *idImport;
506
507
    /** Used to "name" thunks created on the inside of an array. */
508
    const Identifier *idArrayElement;
509
510
    /** Used to "name" thunks created to execute invariants. */
511
    const Identifier *idInvariant;
512
513
    /** Placehodler name for internal AST. */
514
    const Identifier *idInternal;
515
516
    /** Used to "name" thunks created to convert JSON to Jsonnet objects. */
517
    const Identifier *idJsonObjVar;
518
519
    const Identifier *idEmpty;
520
521
    /** Used to refer to idJsonObjVar. */
522
    const AST *jsonObjVar;
523
524
    /* Standard Library AST */
525
    const DesugaredObject *stdlibAST;
526
    HeapObject *stdObject;
527
528
    struct ImportCacheValue {
529
        std::string foundHere;
530
        std::string content;
531
532
        /** Thunk to store cached result of execution.
533
         *
534
         * Null if this file was only ever successfully imported with importstr/importbin.
535
         */
536
        HeapThunk *thunk;
537
    };
538
539
    /** Cache for imported Jsonnet files. */
540
    std::map<std::pair<std::string, UString>, ImportCacheValue *> cachedImports;
541
542
    /** External variables for std.extVar. */
543
    ExtMap externalVars;
544
545
    /** The callback used for loading imported files. */
546
    VmNativeCallbackMap nativeCallbacks;
547
548
    /** The callback used for loading imported files. */
549
    JsonnetImportCallback *importCallback;
550
551
    /** User context pointer for the import callback. */
552
    void *importCallbackContext;
553
554
    /** Builtin functions by name. */
555
    typedef std::map<std::string, BuiltinFunc> BuiltinMap;
556
    BuiltinMap builtins;
557
558
    /** Source values by name. Source values are values (usually functions)
559
      * implemented as Jsonnet source which we use internally in the interpreter.
560
      * In a sense they are the opposite of builtins. */
561
    typedef std::map<std::string, HeapThunk *> SourceFuncMap;
562
    SourceFuncMap sourceVals;
563
    /* Just for memory management. */
564
    std::vector<std::unique_ptr<Identifier>> sourceFuncIds;
565
566
    RuntimeError makeError(const LocationRange &loc, const std::string &msg)
567
3.98k
    {
568
3.98k
        return stack.makeError(loc, msg);
569
3.98k
    }
570
571
    /** Create an object on the heap, maybe collect garbage.
572
     * \param T Something under HeapEntity
573
     * \returns The new object
574
     */
575
    template <class T, class... Args>
576
    T *makeHeap(Args &&... args)
577
148M
    {
578
148M
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
148M
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
154k
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
154k
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
154k
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
154k
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
24.8M
            for (const auto &sourceVal : sourceVals) {
597
24.8M
                heap.markFrom(sourceVal.second);
598
24.8M
            }
599
600
            // Delete unreachable objects.
601
154k
            heap.sweep();
602
154k
        }
603
148M
        return r;
604
148M
    }
Unexecuted instantiation: vm.cpp:jsonnet::internal::(anonymous namespace)::HeapThunk* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapThunk, jsonnet::internal::Identifier const*&, jsonnet::internal::(anonymous namespace)::HeapObject* const&, unsigned int const&, jsonnet::internal::AST const* const&>(jsonnet::internal::Identifier const*&, jsonnet::internal::(anonymous namespace)::HeapObject* const&, unsigned int const&, jsonnet::internal::AST const* const&)
Unexecuted instantiation: vm.cpp:jsonnet::internal::(anonymous namespace)::HeapThunk* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapThunk, jsonnet::internal::Identifier const* const&, decltype(nullptr), int, decltype(nullptr)>(jsonnet::internal::Identifier const* const&, decltype(nullptr)&&, int&&, decltype(nullptr)&&)
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapArray* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapArray, std::__1::vector<jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::allocator<jsonnet::internal::(anonymous namespace)::HeapThunk*> > const&>(std::__1::vector<jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::allocator<jsonnet::internal::(anonymous namespace)::HeapThunk*> > const&)
Line
Count
Source
577
3.53M
    {
578
3.53M
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
3.53M
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
5.06k
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
5.06k
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
5.06k
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
5.06k
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
815k
            for (const auto &sourceVal : sourceVals) {
597
815k
                heap.markFrom(sourceVal.second);
598
815k
            }
599
600
            // Delete unreachable objects.
601
5.06k
            heap.sweep();
602
5.06k
        }
603
3.53M
        return r;
604
3.53M
    }
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapString* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapString, std::__1::basic_string<char32_t, std::__1::char_traits<char32_t>, std::__1::allocator<char32_t> > const&>(std::__1::basic_string<char32_t, std::__1::char_traits<char32_t>, std::__1::allocator<char32_t> > const&)
Line
Count
Source
577
54.5M
    {
578
54.5M
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
54.5M
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
60.7k
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
60.7k
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
60.7k
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
60.7k
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
9.78M
            for (const auto &sourceVal : sourceVals) {
597
9.78M
                heap.markFrom(sourceVal.second);
598
9.78M
            }
599
600
            // Delete unreachable objects.
601
60.7k
            heap.sweep();
602
60.7k
        }
603
54.5M
        return r;
604
54.5M
    }
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapThunk* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapThunk, jsonnet::internal::Identifier const*&, decltype(nullptr), int, decltype(nullptr)>(jsonnet::internal::Identifier const*&, decltype(nullptr)&&, int&&, decltype(nullptr)&&)
Line
Count
Source
577
822k
    {
578
822k
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
822k
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
992
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
992
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
992
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
992
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
159k
            for (const auto &sourceVal : sourceVals) {
597
159k
                heap.markFrom(sourceVal.second);
598
159k
            }
599
600
            // Delete unreachable objects.
601
992
            heap.sweep();
602
992
        }
603
822k
        return r;
604
822k
    }
Unexecuted instantiation: vm.cpp:jsonnet::internal::(anonymous namespace)::HeapRestrictedObject* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapRestrictedObject, jsonnet::internal::(anonymous namespace)::HeapObject*&, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::ObjectField::Hide, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::ObjectField::Hide> > >&>(jsonnet::internal::(anonymous namespace)::HeapObject*&, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::ObjectField::Hide, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::ObjectField::Hide> > >&)
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapClosure* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapClosure, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >, decltype(nullptr), int, std::__1::vector<jsonnet::internal::(anonymous namespace)::HeapClosure::Param, std::__1::allocator<jsonnet::internal::(anonymous namespace)::HeapClosure::Param> >&, jsonnet::internal::BuiltinFunctionBody const*&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>(std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&&, decltype(nullptr)&&, int&&, std::__1::vector<jsonnet::internal::(anonymous namespace)::HeapClosure::Param, std::__1::allocator<jsonnet::internal::(anonymous namespace)::HeapClosure::Param> >&, jsonnet::internal::BuiltinFunctionBody const*&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Line
Count
Source
577
11.6M
    {
578
11.6M
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
11.6M
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
12.6k
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
12.6k
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
12.6k
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
12.6k
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
2.04M
            for (const auto &sourceVal : sourceVals) {
597
2.04M
                heap.markFrom(sourceVal.second);
598
2.04M
            }
599
600
            // Delete unreachable objects.
601
12.6k
            heap.sweep();
602
12.6k
        }
603
11.6M
        return r;
604
11.6M
    }
Unexecuted instantiation: vm.cpp:jsonnet::internal::(anonymous namespace)::HeapComprehensionObject* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapComprehensionObject, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&, jsonnet::internal::AST const*&, jsonnet::internal::Identifier const*&, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&>(std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&, jsonnet::internal::AST const*&, jsonnet::internal::Identifier const*&, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&)
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapThunk* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapThunk, decltype(nullptr), decltype(nullptr), int, jsonnet::internal::AST const*>(decltype(nullptr)&&, decltype(nullptr)&&, int&&, jsonnet::internal::AST const*&&)
Line
Count
Source
577
5.89k
    {
578
5.89k
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
5.89k
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
0
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
0
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
0
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
0
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
0
            for (const auto &sourceVal : sourceVals) {
597
0
                heap.markFrom(sourceVal.second);
598
0
            }
599
600
            // Delete unreachable objects.
601
0
            heap.sweep();
602
0
        }
603
5.89k
        return r;
604
5.89k
    }
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapThunk* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapThunk, jsonnet::internal::Identifier*, jsonnet::internal::(anonymous namespace)::HeapObject*&, int, jsonnet::internal::AST* const&>(jsonnet::internal::Identifier*&&, jsonnet::internal::(anonymous namespace)::HeapObject*&, int&&, jsonnet::internal::AST* const&)
Line
Count
Source
577
949k
    {
578
949k
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
949k
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
0
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
0
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
0
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
0
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
0
            for (const auto &sourceVal : sourceVals) {
597
0
                heap.markFrom(sourceVal.second);
598
0
            }
599
600
            // Delete unreachable objects.
601
0
            heap.sweep();
602
0
        }
603
949k
        return r;
604
949k
    }
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapThunk* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapThunk, jsonnet::internal::Identifier const*&, jsonnet::internal::(anonymous namespace)::HeapObject*&, unsigned int&, jsonnet::internal::AST* const&>(jsonnet::internal::Identifier const*&, jsonnet::internal::(anonymous namespace)::HeapObject*&, unsigned int&, jsonnet::internal::AST* const&)
Line
Count
Source
577
41.1M
    {
578
41.1M
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
41.1M
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
32.1k
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
32.1k
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
32.1k
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
32.1k
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
5.16M
            for (const auto &sourceVal : sourceVals) {
597
5.16M
                heap.markFrom(sourceVal.second);
598
5.16M
            }
599
600
            // Delete unreachable objects.
601
32.1k
            heap.sweep();
602
32.1k
        }
603
41.1M
        return r;
604
41.1M
    }
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapClosure* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapClosure, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > > const&, jsonnet::internal::(anonymous namespace)::HeapObject*&, unsigned int&, std::__1::vector<jsonnet::internal::(anonymous namespace)::HeapClosure::Param, std::__1::allocator<jsonnet::internal::(anonymous namespace)::HeapClosure::Param> > const&, jsonnet::internal::AST*&, char const (&) [1]>(std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > > const&, jsonnet::internal::(anonymous namespace)::HeapObject*&, unsigned int&, std::__1::vector<jsonnet::internal::(anonymous namespace)::HeapClosure::Param, std::__1::allocator<jsonnet::internal::(anonymous namespace)::HeapClosure::Param> > const&, jsonnet::internal::AST*&, char const (&) [1])
Line
Count
Source
577
2.80M
    {
578
2.80M
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
2.80M
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
4.94k
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
4.94k
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
4.94k
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
4.94k
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
796k
            for (const auto &sourceVal : sourceVals) {
597
796k
                heap.markFrom(sourceVal.second);
598
796k
            }
599
600
            // Delete unreachable objects.
601
4.94k
            heap.sweep();
602
4.94k
        }
603
2.80M
        return r;
604
2.80M
    }
Unexecuted instantiation: vm.cpp:jsonnet::internal::(anonymous namespace)::HeapThunk* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapThunk, jsonnet::internal::Identifier const*&, decltype(nullptr), int, jsonnet::internal::AST*&>(jsonnet::internal::Identifier const*&, decltype(nullptr)&&, int&&, jsonnet::internal::AST*&)
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapThunk* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapThunk, jsonnet::internal::Identifier const* const&, jsonnet::internal::(anonymous namespace)::HeapObject*&, unsigned int&, jsonnet::internal::AST* const&>(jsonnet::internal::Identifier const* const&, jsonnet::internal::(anonymous namespace)::HeapObject*&, unsigned int&, jsonnet::internal::AST* const&)
Line
Count
Source
577
25.6M
    {
578
25.6M
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
25.6M
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
30.6k
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
30.6k
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
30.6k
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
30.6k
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
4.92M
            for (const auto &sourceVal : sourceVals) {
597
4.92M
                heap.markFrom(sourceVal.second);
598
4.92M
            }
599
600
            // Delete unreachable objects.
601
30.6k
            heap.sweep();
602
30.6k
        }
603
25.6M
        return r;
604
25.6M
    }
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapSimpleObject* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapSimpleObject, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapSimpleObject::Field, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapSimpleObject::Field> > >&, std::__1::list<jsonnet::internal::AST*, std::__1::allocator<jsonnet::internal::AST*> >&>(std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapSimpleObject::Field, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapSimpleObject::Field> > >&, std::__1::list<jsonnet::internal::AST*, std::__1::allocator<jsonnet::internal::AST*> >&)
Line
Count
Source
577
3.76M
    {
578
3.76M
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
3.76M
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
4.21k
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
4.21k
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
4.21k
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
4.21k
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
679k
            for (const auto &sourceVal : sourceVals) {
597
679k
                heap.markFrom(sourceVal.second);
598
679k
            }
599
600
            // Delete unreachable objects.
601
4.21k
            heap.sweep();
602
4.21k
        }
603
3.76M
        return r;
604
3.76M
    }
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapThunk* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapThunk, jsonnet::internal::Identifier const*&, jsonnet::internal::(anonymous namespace)::HeapObject*&, unsigned int&, jsonnet::internal::AST const* const&>(jsonnet::internal::Identifier const*&, jsonnet::internal::(anonymous namespace)::HeapObject*&, unsigned int&, jsonnet::internal::AST const* const&)
Line
Count
Source
577
115
    {
578
115
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
115
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
0
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
0
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
0
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
0
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
0
            for (const auto &sourceVal : sourceVals) {
597
0
                heap.markFrom(sourceVal.second);
598
0
            }
599
600
            // Delete unreachable objects.
601
0
            heap.sweep();
602
0
        }
603
115
        return r;
604
115
    }
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapExtendedObject* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapExtendedObject, jsonnet::internal::(anonymous namespace)::HeapObject*&, jsonnet::internal::(anonymous namespace)::HeapObject*&>(jsonnet::internal::(anonymous namespace)::HeapObject*&, jsonnet::internal::(anonymous namespace)::HeapObject*&)
Line
Count
Source
577
2.69M
    {
578
2.69M
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
2.69M
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
2.74k
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
2.74k
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
2.74k
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
2.74k
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
441k
            for (const auto &sourceVal : sourceVals) {
597
441k
                heap.markFrom(sourceVal.second);
598
441k
            }
599
600
            // Delete unreachable objects.
601
2.74k
            heap.sweep();
602
2.74k
        }
603
2.69M
        return r;
604
2.69M
    }
vm.cpp:jsonnet::internal::(anonymous namespace)::HeapThunk* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapThunk, jsonnet::internal::Identifier const*&, jsonnet::internal::(anonymous namespace)::HeapObject*&, unsigned int&, jsonnet::internal::AST*&>(jsonnet::internal::Identifier const*&, jsonnet::internal::(anonymous namespace)::HeapObject*&, unsigned int&, jsonnet::internal::AST*&)
Line
Count
Source
577
1.14M
    {
578
1.14M
        T *r = heap.makeEntity<T, Args...>(std::forward<Args>(args)...);
579
1.14M
        if (heap.checkHeap()) {  // Do a GC cycle?
580
            // Avoid the object we just made being collected.
581
303
            heap.markFrom(r);
582
583
            // Mark from the stack.
584
303
            stack.mark(heap);
585
586
            // Mark from the scratch register
587
303
            heap.markFrom(scratch);
588
589
            // Mark from cached imports
590
303
            for (const auto &pair : cachedImports) {
591
0
                HeapThunk *thunk = pair.second->thunk;
592
0
                if (thunk != nullptr)
593
0
                    heap.markFrom(thunk);
594
0
            }
595
596
48.7k
            for (const auto &sourceVal : sourceVals) {
597
48.7k
                heap.markFrom(sourceVal.second);
598
48.7k
            }
599
600
            // Delete unreachable objects.
601
303
            heap.sweep();
602
303
        }
603
1.14M
        return r;
604
1.14M
    }
Unexecuted instantiation: vm.cpp:jsonnet::internal::(anonymous namespace)::HeapComprehensionObject* jsonnet::internal::(anonymous namespace)::Interpreter::makeHeap<jsonnet::internal::(anonymous namespace)::HeapComprehensionObject, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&, jsonnet::internal::AST*&, jsonnet::internal::Identifier const*&, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&>(std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&, jsonnet::internal::AST*&, jsonnet::internal::Identifier const*&, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >&)
605
606
    Value makeBoolean(bool v)
607
15.2M
    {
608
15.2M
        Value r;
609
15.2M
        r.t = Value::BOOLEAN;
610
15.2M
        r.v.b = v;
611
15.2M
        return r;
612
15.2M
    }
613
614
    Value makeNumber(double v)
615
11.3M
    {
616
11.3M
        Value r;
617
11.3M
        r.t = Value::NUMBER;
618
11.3M
        r.v.d = v;
619
11.3M
        return r;
620
11.3M
    }
621
622
    Value makeNumberCheck(const LocationRange &loc, double v)
623
10.2M
    {
624
10.2M
        if (std::isnan(v)) {
625
0
            throw makeError(loc, "not a number");
626
0
        }
627
10.2M
        if (std::isinf(v)) {
628
44
            throw makeError(loc, "overflow");
629
44
        }
630
10.2M
        return makeNumber(v);
631
10.2M
    }
632
633
    Value makeNull(void)
634
205k
    {
635
205k
        Value r;
636
205k
        r.t = Value::NULL_TYPE;
637
205k
        return r;
638
205k
    }
639
640
    Value makeArray(const std::vector<HeapThunk *> &v)
641
3.53M
    {
642
3.53M
        Value r;
643
3.53M
        r.t = Value::ARRAY;
644
3.53M
        r.v.h = makeHeap<HeapArray>(v);
645
3.53M
        return r;
646
3.53M
    }
647
648
    Value makeClosure(const BindingFrame &env, HeapObject *self, unsigned offset,
649
                      const HeapClosure::Params &params, AST *body)
650
2.80M
    {
651
2.80M
        Value r;
652
2.80M
        r.t = Value::FUNCTION;
653
2.80M
        r.v.h = makeHeap<HeapClosure>(env, self, offset, params, body, "");
654
2.80M
        return r;
655
2.80M
    }
656
657
    Value makeBuiltinFromAST(const BuiltinFunctionBody *body)
658
11.6M
    {
659
11.6M
        HeapClosure::Params hc_params;
660
18.4M
        for (const auto p : body->params) {
661
18.4M
            hc_params.emplace_back(p, nullptr);
662
18.4M
        }
663
11.6M
        Value r;
664
11.6M
        r.t = Value::FUNCTION;
665
11.6M
        r.v.h = makeHeap<HeapClosure>(BindingFrame(), nullptr, 0, hc_params, body, body->name);
666
11.6M
        return r;
667
11.6M
    }
668
669
    template <class T, class... Args>
670
    Value makeObject(Args... args)
671
6.46M
    {
672
6.46M
        Value r;
673
6.46M
        r.t = Value::OBJECT;
674
6.46M
        r.v.h = makeHeap<T>(args...);
675
6.46M
        return r;
676
6.46M
    }
Unexecuted instantiation: vm.cpp:jsonnet::internal::(anonymous namespace)::Value jsonnet::internal::(anonymous namespace)::Interpreter::makeObject<jsonnet::internal::(anonymous namespace)::HeapRestrictedObject, jsonnet::internal::(anonymous namespace)::HeapObject*, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::ObjectField::Hide, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::ObjectField::Hide> > > >(jsonnet::internal::(anonymous namespace)::HeapObject*, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::ObjectField::Hide, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::ObjectField::Hide> > >)
Unexecuted instantiation: vm.cpp:jsonnet::internal::(anonymous namespace)::Value jsonnet::internal::(anonymous namespace)::Interpreter::makeObject<jsonnet::internal::(anonymous namespace)::HeapComprehensionObject, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >, jsonnet::internal::AST const*, jsonnet::internal::Identifier const*, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > > >(std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >, jsonnet::internal::AST const*, jsonnet::internal::Identifier const*, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >)
vm.cpp:jsonnet::internal::(anonymous namespace)::Value jsonnet::internal::(anonymous namespace)::Interpreter::makeObject<jsonnet::internal::(anonymous namespace)::HeapSimpleObject, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapSimpleObject::Field, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapSimpleObject::Field> > >, std::__1::list<jsonnet::internal::AST*, std::__1::allocator<jsonnet::internal::AST*> > >(std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapSimpleObject::Field, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapSimpleObject::Field> > >, std::__1::list<jsonnet::internal::AST*, std::__1::allocator<jsonnet::internal::AST*> >)
Line
Count
Source
671
3.76M
    {
672
3.76M
        Value r;
673
3.76M
        r.t = Value::OBJECT;
674
3.76M
        r.v.h = makeHeap<T>(args...);
675
3.76M
        return r;
676
3.76M
    }
vm.cpp:jsonnet::internal::(anonymous namespace)::Value jsonnet::internal::(anonymous namespace)::Interpreter::makeObject<jsonnet::internal::(anonymous namespace)::HeapExtendedObject, jsonnet::internal::(anonymous namespace)::HeapObject*, jsonnet::internal::(anonymous namespace)::HeapObject*>(jsonnet::internal::(anonymous namespace)::HeapObject*, jsonnet::internal::(anonymous namespace)::HeapObject*)
Line
Count
Source
671
2.69M
    {
672
2.69M
        Value r;
673
2.69M
        r.t = Value::OBJECT;
674
2.69M
        r.v.h = makeHeap<T>(args...);
675
2.69M
        return r;
676
2.69M
    }
Unexecuted instantiation: vm.cpp:jsonnet::internal::(anonymous namespace)::Value jsonnet::internal::(anonymous namespace)::Interpreter::makeObject<jsonnet::internal::(anonymous namespace)::HeapComprehensionObject, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >, jsonnet::internal::AST*, jsonnet::internal::Identifier const*, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > > >(std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >, jsonnet::internal::AST*, jsonnet::internal::Identifier const*, std::__1::map<jsonnet::internal::Identifier const*, jsonnet::internal::(anonymous namespace)::HeapThunk*, std::__1::less<jsonnet::internal::Identifier const*>, std::__1::allocator<std::__1::pair<jsonnet::internal::Identifier const* const, jsonnet::internal::(anonymous namespace)::HeapThunk*> > >)
677
678
    Value makeString(const UString &v)
679
54.5M
    {
680
54.5M
        Value r;
681
54.5M
        r.t = Value::STRING;
682
54.5M
        r.v.h = makeHeap<HeapString>(v);
683
54.5M
        return r;
684
54.5M
    }
685
686
    /** Auxiliary function of objectIndex.
687
     *
688
     * Traverse the object's tree from right to left, looking for an object
689
     * with the given field.  Call with offset initially set to 0.
690
     *
691
     * \param f The field we're looking for.
692
     * \param start_from Step over this many leaves first.
693
     * \param counter Return the level of "super" that contained the field.
694
     * \returns The first object with the field, or nullptr if it could not be found.
695
     */
696
    HeapLeafObject *findObject(const Identifier *f, HeapObject *curr, unsigned start_from,
697
                               unsigned &counter)
698
1.41G
    {
699
1.41G
        if (auto *ext = dynamic_cast<HeapExtendedObject *>(curr)) {
700
699M
            auto *r = findObject(f, ext->right, start_from, counter);
701
699M
            if (r)
702
9.55M
                return r;
703
689M
            auto *l = findObject(f, ext->left, start_from, counter);
704
689M
            if (l)
705
580M
                return l;
706
713M
        } else if (auto *ext = dynamic_cast<HeapRestrictedObject *>(curr)) {
707
            // The RestrictedObject is on the 'right', that is it is checked/counted first.
708
0
            if (counter >= start_from) {
709
0
                if (ext->retainedKeys.find(f) == ext->retainedKeys.end()) {
710
0
                    counter += 1 + countLeaves(ext->obj);
711
0
                    return nullptr;
712
0
                }
713
0
            }
714
0
            ++counter;
715
0
            return findObject(f, ext->obj, start_from, counter);
716
713M
        } else {
717
713M
            if (counter >= start_from) {
718
72.2M
                if (auto *simp = dynamic_cast<HeapSimpleObject *>(curr)) {
719
72.2M
                    auto it = simp->fields.find(f);
720
72.2M
                    if (it != simp->fields.end()) {
721
23.8M
                        return simp;
722
23.8M
                    }
723
72.2M
                } else if (auto *comp = dynamic_cast<HeapComprehensionObject *>(curr)) {
724
0
                    auto it = comp->compValues.find(f);
725
0
                    if (it != comp->compValues.end()) {
726
0
                        return comp;
727
0
                    }
728
0
                }
729
72.2M
            }
730
690M
            counter++;
731
690M
        }
732
799M
        return nullptr;
733
1.41G
    }
734
735
    typedef std::map<const Identifier *, ObjectField::Hide> IdHideMap;
736
737
    /** Auxiliary function.
738
     */
739
    IdHideMap objectFieldsAux(const HeapObject *obj_)
740
73.7M
    {
741
73.7M
        IdHideMap r;
742
73.7M
        if (auto *obj = dynamic_cast<const HeapSimpleObject *>(obj_)) {
743
37.5M
            for (const auto &f : obj->fields) {
744
29.6M
                r[f.first] = f.second.hide;
745
29.6M
            }
746
747
37.5M
        } else if (auto *obj = dynamic_cast<const HeapRestrictedObject *>(obj_)) {
748
0
            return obj->retainedKeys;
749
750
36.1M
        } else if (auto *obj = dynamic_cast<const HeapExtendedObject *>(obj_)) {
751
36.1M
            r = objectFieldsAux(obj->right);
752
111M
            for (const auto &pair : objectFieldsAux(obj->left)) {
753
111M
                auto it = r.find(pair.first);
754
111M
                if (it == r.end()) {
755
                    // First time it is seen
756
84.2M
                    r[pair.first] = pair.second;
757
84.2M
                } else if (it->second == ObjectField::INHERIT) {
758
                    // Seen before, but with inherited visibility so use new visibility
759
26.5M
                    r[pair.first] = pair.second;
760
26.5M
                }
761
111M
            }
762
763
36.1M
        } else if (auto *obj = dynamic_cast<const HeapComprehensionObject *>(obj_)) {
764
0
            for (const auto &f : obj->compValues)
765
0
                r[f.first] = ObjectField::INHERIT;
766
0
        }
767
73.7M
        return r;
768
73.7M
    }
769
770
    /** Auxiliary function.
771
     */
772
    std::set<const Identifier *> objectFields(const HeapObject *obj_, bool manifesting)
773
1.40M
    {
774
1.40M
        std::set<const Identifier *> r;
775
2.54M
        for (const auto &pair : objectFieldsAux(obj_)) {
776
2.54M
            if (!manifesting || pair.second != ObjectField::HIDDEN)
777
2.39M
                r.insert(pair.first);
778
2.54M
        }
779
1.40M
        return r;
780
1.40M
    }
781
782
    /** Import another Jsonnet file.
783
     *
784
     * If the file has already been imported, then use that version.  This maintains
785
     * referential transparency in the case of writes to disk during execution.  The
786
     * cache holds a thunk in order to cache the resulting value of execution.
787
     *
788
     * \param loc Location of the import statement.
789
     * \param file Path to the filename.
790
     */
791
    HeapThunk *import(const LocationRange &loc, const LiteralString *file)
792
45
    {
793
45
        ImportCacheValue *input = importData(loc, file);
794
45
        if (input->thunk == nullptr) {
795
0
            Tokens tokens = jsonnet_lex(input->foundHere, input->content.c_str());
796
0
            AST *expr = jsonnet_parse(alloc, tokens);
797
0
            jsonnet_desugar(alloc, expr, nullptr);
798
0
            jsonnet_static_analysis(expr);
799
            // If no errors then populate cache.
800
0
            auto *thunk = makeHeap<HeapThunk>(idImport, nullptr, 0, expr);
801
0
            input->thunk = thunk;
802
0
        }
803
45
        return input->thunk;
804
45
    }
805
806
    /** Import a file as a string or byte array.
807
     *
808
     * If the file has already been imported, then use that version.  This maintains
809
     * referential transparency in the case of writes to disk during execution.
810
     *
811
     * \param loc Location of the import statement.
812
     * \param file Path to the filename.
813
     * \param found_here If non-null, used to store the actual path of the file
814
     */
815
    ImportCacheValue *importData(const LocationRange &loc, const LiteralString *file)
816
74
    {
817
        // `dir` is passed to the importCallback, which may be externally defined.
818
        // For backwards compatibility, we need to keep the trailing directory separator.
819
        // For example, the default callback in libjsonnet.cpp joins paths with simple
820
        // string concatenation. Other (external) implementations might do the same.
821
74
        std::string dir = path_dir_with_trailing_separator(loc.file);
822
823
74
        const UString &path = file->value;
824
825
74
        std::pair<std::string, UString> key(dir, path);
826
74
        ImportCacheValue *cached_value = cachedImports[key];
827
74
        if (cached_value != nullptr)
828
0
            return cached_value;
829
830
74
        char *found_here_cptr;
831
74
        char *buf = NULL;
832
74
        size_t buflen = 0;
833
74
        int result = importCallback(importCallbackContext,
834
74
                                    dir.c_str(),
835
74
                                    encode_utf8(path).c_str(),
836
74
                                    &found_here_cptr,
837
74
                                    &buf,
838
74
                                    &buflen);
839
840
74
        std::string input(buf, buflen);
841
74
        ::free(buf);
842
843
74
        if (result == 1) {  // failure
844
74
            std::string epath = encode_utf8(jsonnet_string_escape(path, false));
845
74
            std::string msg = "couldn't open import \"" + epath + "\": ";
846
74
            msg += input;
847
74
            throw makeError(loc, msg);
848
74
        }
849
850
0
        auto *input_ptr = new ImportCacheValue();
851
0
        input_ptr->foundHere = found_here_cptr;
852
0
        input_ptr->content = input;
853
0
        input_ptr->thunk = nullptr;  // May be filled in later by import().
854
0
        ::free(found_here_cptr);
855
0
        cachedImports[key] = input_ptr;
856
0
        return input_ptr;
857
74
    }
858
859
    /** Capture the required variables from the environment. */
860
    BindingFrame capture(const std::vector<const Identifier *> &free_vars)
861
74.6M
    {
862
74.6M
        BindingFrame env;
863
1.44G
        for (auto fv : free_vars) {
864
1.44G
            auto *th = stack.lookUpVar(fv);
865
1.44G
            env[fv] = th;
866
1.44G
        }
867
74.6M
        return env;
868
74.6M
    }
869
870
    /** Count the number of leaves in the tree.
871
     *
872
     * \param obj The root of the tree.
873
     * \returns The number of leaves.
874
     */
875
    unsigned countLeaves(HeapObject *obj)
876
2.93G
    {
877
2.93G
        if (auto *ext = dynamic_cast<HeapExtendedObject *>(obj)) {
878
1.46G
            return countLeaves(ext->left) + countLeaves(ext->right);
879
1.47G
        } else if (auto *ext = dynamic_cast<HeapRestrictedObject *>(obj)) {
880
0
            return countLeaves(ext->obj) + 1;
881
1.47G
        } else {
882
            // Must be a HeapLeafObject.
883
1.47G
            return 1;
884
1.47G
        }
885
2.93G
    }
886
887
5.89k
    void prepareSourceValThunks() {
888
949k
        for (const auto &field : stdlibAST->fields) {
889
949k
            AST *nameAST = field.name;
890
949k
            if (nameAST->type != AST_LITERAL_STRING) {
891
                // Skip any fields without a known name.
892
0
                continue;
893
0
            }
894
949k
            UString name = dynamic_cast<LiteralString *>(nameAST)->value;
895
896
949k
            sourceFuncIds.emplace_back(new Identifier(name));
897
949k
            auto *th = makeHeap<HeapThunk>(sourceFuncIds.back().get(), stdObject, 0, field.body);
898
949k
            sourceVals[encode_utf8(name)] = th;
899
949k
        }
900
5.89k
    }
901
902
    /** Safely converts a double to an int64_t, with range and validity checks.
903
    *
904
    * This function is used primarily for bitwise operations which require integer operands.
905
    * It performs two safety checks:
906
    * 1. Verifies the value is finite (not NaN or Infinity)
907
    * 2. Ensures the value is within the safe integer range [-2^53, 2^53]
908
    *
909
    * The safe integer range limitation is necessary because IEEE 754 double precision
910
    * floating point numbers can only precisely represent integers in the range [-2^53, 2^53].
911
    * Beyond this range, precision is lost, which would lead to unpredictable results
912
    * in bitwise operations that depend on exact bit patterns.
913
    *
914
    * \param value The double value to convert
915
    * \param loc The location in source code (for error reporting)
916
    * \throws RuntimeError if value is not finite or outside the safe integer range
917
    * \returns The value converted to int64_t
918
    */
919
98.1k
    int64_t safeDoubleToInt64(double value, const internal::LocationRange& loc) {
920
98.1k
        if (std::isnan(value) || std::isinf(value)) {
921
0
            throw internal::StaticError(loc, "numeric value is not finite");
922
0
        }
923
924
        // Constants for safe double-to-int conversion
925
        // Jsonnet uses IEEE 754 doubles, which precisely represent integers in the range [-2^53 + 1, 2^53 - 1].
926
98.1k
        constexpr int64_t DOUBLE_MAX_SAFE_INTEGER = (1LL << 53) - 1;
927
98.1k
        constexpr int64_t DOUBLE_MIN_SAFE_INTEGER = -((1LL << 53) - 1);
928
929
        // Check if the value is within the safe integer range
930
98.1k
        if (value < DOUBLE_MIN_SAFE_INTEGER || value > DOUBLE_MAX_SAFE_INTEGER) {
931
9
            throw makeError(loc, "numeric value outside safe integer range for bitwise operation.");
932
9
        }
933
98.1k
        return static_cast<int64_t>(value);
934
98.1k
    }
935
936
   public:
937
    /** Create a new interpreter.
938
     *
939
     * \param loc The location range of the file to be executed.
940
     */
941
    Interpreter(Allocator *alloc, const ExtMap &ext_vars, unsigned max_stack, double gc_min_objects,
942
                double gc_growth_trigger, const VmNativeCallbackMap &native_callbacks,
943
                JsonnetImportCallback *import_callback, void *import_callback_context)
944
945
5.89k
        : heap(gc_min_objects, gc_growth_trigger),
946
5.89k
          stack(max_stack),
947
5.89k
          alloc(alloc),
948
5.89k
          idImport(alloc->makeIdentifier(U"import")),
949
5.89k
          idArrayElement(alloc->makeIdentifier(U"array_element")),
950
5.89k
          idInvariant(alloc->makeIdentifier(U"object_assert")),
951
5.89k
          idInternal(alloc->makeIdentifier(U"__internal__")),
952
5.89k
          idJsonObjVar(alloc->makeIdentifier(U"_")),
953
5.89k
          idEmpty(alloc->makeIdentifier(U"")),
954
5.89k
          jsonObjVar(alloc->make<Var>(LocationRange(), Fodder{}, idJsonObjVar)),
955
5.89k
          externalVars(ext_vars),
956
5.89k
          importCallback(import_callback),
957
5.89k
          importCallbackContext(import_callback_context)
958
5.89k
    {
959
5.89k
        scratch = makeNull();
960
        // Add a prefix to avoid conflicting with names from `native_callbacks`.
961
5.89k
        builtins["std:makeArray"] = &Interpreter::builtinMakeArray;
962
5.89k
        builtins["std:pow"] = &Interpreter::builtinPow;
963
5.89k
        builtins["std:floor"] = &Interpreter::builtinFloor;
964
5.89k
        builtins["std:ceil"] = &Interpreter::builtinCeil;
965
5.89k
        builtins["std:sqrt"] = &Interpreter::builtinSqrt;
966
5.89k
        builtins["std:sin"] = &Interpreter::builtinSin;
967
5.89k
        builtins["std:cos"] = &Interpreter::builtinCos;
968
5.89k
        builtins["std:tan"] = &Interpreter::builtinTan;
969
5.89k
        builtins["std:asin"] = &Interpreter::builtinAsin;
970
5.89k
        builtins["std:acos"] = &Interpreter::builtinAcos;
971
5.89k
        builtins["std:atan"] = &Interpreter::builtinAtan;
972
5.89k
        builtins["std:type"] = &Interpreter::builtinType;
973
5.89k
        builtins["std:filter"] = &Interpreter::builtinFilter;
974
5.89k
        builtins["std:objectHasEx"] = &Interpreter::builtinObjectHasEx;
975
5.89k
        builtins["std:length"] = &Interpreter::builtinLength;
976
5.89k
        builtins["std:objectFieldsEx"] = &Interpreter::builtinObjectFieldsEx;
977
5.89k
        builtins["std:objectRemoveKey"] = &Interpreter::builtinObjectRemoveKey;
978
5.89k
        builtins["std:codepoint"] = &Interpreter::builtinCodepoint;
979
5.89k
        builtins["std:char"] = &Interpreter::builtinChar;
980
5.89k
        builtins["std:log"] = &Interpreter::builtinLog;
981
5.89k
        builtins["std:exp"] = &Interpreter::builtinExp;
982
5.89k
        builtins["std:mantissa"] = &Interpreter::builtinMantissa;
983
5.89k
        builtins["std:exponent"] = &Interpreter::builtinExponent;
984
5.89k
        builtins["std:modulo"] = &Interpreter::builtinModulo;
985
5.89k
        builtins["std:extVar"] = &Interpreter::builtinExtVar;
986
5.89k
        builtins["std:primitiveEquals"] = &Interpreter::builtinPrimitiveEquals;
987
5.89k
        builtins["std:native"] = &Interpreter::builtinNative;
988
5.89k
        builtins["std:md5"] = &Interpreter::builtinMd5;
989
5.89k
        builtins["std:trace"] = &Interpreter::builtinTrace;
990
5.89k
        builtins["std:splitLimit"] = &Interpreter::builtinSplitLimit;
991
5.89k
        builtins["std:substr"] = &Interpreter::builtinSubstr;
992
5.89k
        builtins["std:range"] = &Interpreter::builtinRange;
993
5.89k
        builtins["std:strReplace"] = &Interpreter::builtinStrReplace;
994
5.89k
        builtins["std:asciiLower"] = &Interpreter::builtinAsciiLower;
995
5.89k
        builtins["std:asciiUpper"] = &Interpreter::builtinAsciiUpper;
996
5.89k
        builtins["std:join"] = &Interpreter::builtinJoin;
997
5.89k
        builtins["std:parseJson"] = &Interpreter::builtinParseJson;
998
5.89k
        builtins["std:parseYaml"] = &Interpreter::builtinParseYaml;
999
5.89k
        builtins["std:encodeUTF8"] = &Interpreter::builtinEncodeUTF8;
1000
5.89k
        builtins["std:decodeUTF8"] = &Interpreter::builtinDecodeUTF8;
1001
5.89k
        builtins["std:atan2"] = &Interpreter::builtinAtan2;
1002
5.89k
        builtins["std:hypot"] = &Interpreter::builtinHypot;
1003
1004
        // Add a prefix `native:` to names of provided callbacks to ensure they can't clash with the builtins above.
1005
        // Although we have separate lookup tables for them, the HeapClosure and BuiltinFunctionBody types just hold
1006
        // function "name" as a std::string to identify the function.
1007
5.89k
        for (const auto& [name, cb] : native_callbacks) {
1008
0
            nativeCallbacks.emplace("native:" + name, cb);
1009
0
        }
1010
1011
5.89k
        DesugaredObject *stdlib = makeStdlibAST(alloc, "__internal__");
1012
5.89k
        jsonnet_static_analysis(stdlib);
1013
5.89k
        stdlibAST = stdlib; // stdlibAST is const, so we need to do analysis before this assignment
1014
5.89k
        auto stdThunk = makeHeap<HeapThunk>(nullptr, nullptr, 0, static_cast<const AST*>(stdlibAST));
1015
5.89k
        stack.newCall(stdThunk->body->location, stdThunk, stdThunk->self, stdThunk->offset, stdThunk->upValues);
1016
5.89k
        evaluate(stdThunk->body, 0);
1017
5.89k
        stdObject = dynamic_cast<HeapObject*>(scratch.v.h);
1018
5.89k
        prepareSourceValThunks();
1019
5.89k
    }
1020
1021
1022
    /** Clean up the heap, stack, stash, and builtin function ASTs. */
1023
    ~Interpreter()
1024
5.89k
    {
1025
5.89k
        for (const auto &pair : cachedImports) {
1026
74
            delete pair.second;
1027
74
        }
1028
5.89k
    }
1029
1030
    const Value &getScratchRegister(void)
1031
0
    {
1032
0
        return scratch;
1033
0
    }
1034
1035
    void setScratchRegister(const Value &v)
1036
0
    {
1037
0
        scratch = v;
1038
0
    }
1039
1040
    /** Raise an error if the arguments aren't the expected types. */
1041
    void validateBuiltinArgs(const LocationRange &loc, const std::string &name,
1042
                             const std::vector<Value> &args, const std::vector<Value::Type> params)
1043
351k
    {
1044
351k
        if (args.size() == params.size()) {
1045
1.02M
            for (std::size_t i = 0; i < args.size(); ++i) {
1046
669k
                if (args[i].t != params[i])
1047
0
                    goto bad;
1048
669k
            }
1049
351k
            return;
1050
351k
        }
1051
0
    bad:;
1052
0
        std::stringstream ss;
1053
0
        ss << "Builtin function " + name + " expected (";
1054
0
        const char *prefix = "";
1055
0
        for (auto p : params) {
1056
0
            ss << prefix << type_str(p);
1057
0
            prefix = ", ";
1058
0
        }
1059
0
        ss << ") but got (";
1060
0
        prefix = "";
1061
0
        for (auto a : args) {
1062
0
            ss << prefix << type_str(a);
1063
0
            prefix = ", ";
1064
0
        }
1065
0
        ss << ")";
1066
0
        throw makeError(loc, ss.str());
1067
351k
    }
1068
1069
    const AST *builtinMakeArray(const LocationRange &loc, const std::vector<Value> &args)
1070
0
    {
1071
0
        Frame &f = stack.top();
1072
0
        validateBuiltinArgs(loc, "makeArray", args, {Value::NUMBER, Value::FUNCTION});
1073
0
        long sz = long(args[0].v.d);
1074
0
        if (sz < 0) {
1075
0
            std::stringstream ss;
1076
0
            ss << "makeArray requires size >= 0, got " << sz;
1077
0
            throw makeError(loc, ss.str());
1078
0
        }
1079
0
        auto *func = static_cast<const HeapClosure *>(args[1].v.h);
1080
0
        std::vector<HeapThunk *> elements;
1081
0
        if (func->params.size() != 1) {
1082
0
            std::stringstream ss;
1083
0
            ss << "makeArray function must take 1 param, got: " << func->params.size();
1084
0
            throw makeError(loc, ss.str());
1085
0
        }
1086
0
        elements.resize(sz);
1087
0
        for (long i = 0; i < sz; ++i) {
1088
0
            auto *th = makeHeap<HeapThunk>(idArrayElement, func->self, func->offset, func->body);
1089
            // The next line stops the new thunks from being GCed.
1090
0
            f.thunks.push_back(th);
1091
0
            th->upValues = func->upValues;
1092
1093
0
            auto *el = makeHeap<HeapThunk>(func->params[0].id, nullptr, 0, nullptr);
1094
0
            el->fill(makeNumber(i));  // i guaranteed not to be inf/NaN
1095
0
            th->upValues[func->params[0].id] = el;
1096
0
            elements[i] = th;
1097
0
        }
1098
0
        scratch = makeArray(elements);
1099
0
        return nullptr;
1100
0
    }
1101
1102
    const AST *builtinPow(const LocationRange &loc, const std::vector<Value> &args)
1103
2.62k
    {
1104
2.62k
        validateBuiltinArgs(loc, "pow", args, {Value::NUMBER, Value::NUMBER});
1105
2.62k
        scratch = makeNumberCheck(loc, std::pow(args[0].v.d, args[1].v.d));
1106
2.62k
        return nullptr;
1107
2.62k
    }
1108
1109
    const AST *builtinFloor(const LocationRange &loc, const std::vector<Value> &args)
1110
29.0k
    {
1111
29.0k
        validateBuiltinArgs(loc, "floor", args, {Value::NUMBER});
1112
29.0k
        scratch = makeNumberCheck(loc, std::floor(args[0].v.d));
1113
29.0k
        return nullptr;
1114
29.0k
    }
1115
1116
    const AST *builtinCeil(const LocationRange &loc, const std::vector<Value> &args)
1117
0
    {
1118
0
        validateBuiltinArgs(loc, "ceil", args, {Value::NUMBER});
1119
0
        scratch = makeNumberCheck(loc, std::ceil(args[0].v.d));
1120
0
        return nullptr;
1121
0
    }
1122
1123
    const AST *builtinSqrt(const LocationRange &loc, const std::vector<Value> &args)
1124
0
    {
1125
0
        validateBuiltinArgs(loc, "sqrt", args, {Value::NUMBER});
1126
0
        scratch = makeNumberCheck(loc, std::sqrt(args[0].v.d));
1127
0
        return nullptr;
1128
0
    }
1129
1130
    const AST *builtinSin(const LocationRange &loc, const std::vector<Value> &args)
1131
0
    {
1132
0
        validateBuiltinArgs(loc, "sin", args, {Value::NUMBER});
1133
0
        scratch = makeNumberCheck(loc, std::sin(args[0].v.d));
1134
0
        return nullptr;
1135
0
    }
1136
1137
    const AST *builtinCos(const LocationRange &loc, const std::vector<Value> &args)
1138
0
    {
1139
0
        validateBuiltinArgs(loc, "cos", args, {Value::NUMBER});
1140
0
        scratch = makeNumberCheck(loc, std::cos(args[0].v.d));
1141
0
        return nullptr;
1142
0
    }
1143
1144
    const AST *builtinTan(const LocationRange &loc, const std::vector<Value> &args)
1145
0
    {
1146
0
        validateBuiltinArgs(loc, "tan", args, {Value::NUMBER});
1147
0
        scratch = makeNumberCheck(loc, std::tan(args[0].v.d));
1148
0
        return nullptr;
1149
0
    }
1150
1151
    const AST *builtinAsin(const LocationRange &loc, const std::vector<Value> &args)
1152
0
    {
1153
0
        validateBuiltinArgs(loc, "asin", args, {Value::NUMBER});
1154
0
        scratch = makeNumberCheck(loc, std::asin(args[0].v.d));
1155
0
        return nullptr;
1156
0
    }
1157
1158
    const AST *builtinAcos(const LocationRange &loc, const std::vector<Value> &args)
1159
0
    {
1160
0
        validateBuiltinArgs(loc, "acos", args, {Value::NUMBER});
1161
0
        scratch = makeNumberCheck(loc, std::acos(args[0].v.d));
1162
0
        return nullptr;
1163
0
    }
1164
1165
    const AST *builtinAtan(const LocationRange &loc, const std::vector<Value> &args)
1166
0
    {
1167
0
        validateBuiltinArgs(loc, "atan", args, {Value::NUMBER});
1168
0
        scratch = makeNumberCheck(loc, std::atan(args[0].v.d));
1169
0
        return nullptr;
1170
0
    }
1171
1172
    const AST *builtinAtan2(const LocationRange &loc, const std::vector<Value> &args)
1173
0
    {
1174
0
        validateBuiltinArgs(loc, "atan2", args, {Value::NUMBER, Value::NUMBER});
1175
0
        scratch = makeNumberCheck(loc, std::atan2(args[0].v.d, args[1].v.d));
1176
0
        return nullptr;
1177
0
    }
1178
1179
    const AST *builtinHypot(const LocationRange &loc, const std::vector<Value> &args)
1180
0
    {
1181
0
        validateBuiltinArgs(loc, "hypot", args, {Value::NUMBER, Value::NUMBER});
1182
0
        scratch = makeNumberCheck(loc, std::hypot(args[0].v.d, args[1].v.d));
1183
0
        return nullptr;
1184
0
    }
1185
1186
    const AST *builtinType(const LocationRange &, const std::vector<Value> &args)
1187
4.07M
    {
1188
4.07M
        switch (args[0].t) {
1189
293k
            case Value::NULL_TYPE: scratch = makeString(U"null"); return nullptr;
1190
1191
358k
            case Value::BOOLEAN: scratch = makeString(U"boolean"); return nullptr;
1192
1193
606k
            case Value::NUMBER: scratch = makeString(U"number"); return nullptr;
1194
1195
306k
            case Value::ARRAY: scratch = makeString(U"array"); return nullptr;
1196
1197
49
            case Value::FUNCTION: scratch = makeString(U"function"); return nullptr;
1198
1199
258k
            case Value::OBJECT: scratch = makeString(U"object"); return nullptr;
1200
1201
2.25M
            case Value::STRING: scratch = makeString(U"string"); return nullptr;
1202
4.07M
        }
1203
0
        return nullptr;  // Quiet, compiler.
1204
4.07M
    }
1205
1206
    const AST *builtinFilter(const LocationRange &loc, const std::vector<Value> &args)
1207
0
    {
1208
0
        Frame &f = stack.top();
1209
0
        validateBuiltinArgs(loc, "filter", args, {Value::FUNCTION, Value::ARRAY});
1210
0
        auto *func = static_cast<HeapClosure *>(args[0].v.h);
1211
0
        auto *arr = static_cast<HeapArray *>(args[1].v.h);
1212
0
        if (func->params.size() != 1) {
1213
0
            throw makeError(loc, "filter function takes 1 parameter.");
1214
0
        }
1215
0
        if (arr->elements.size() == 0) {
1216
0
            scratch = makeArray({});
1217
0
        } else {
1218
0
            f.kind = FRAME_BUILTIN_FILTER;
1219
0
            f.val = args[0];
1220
0
            f.val2 = args[1];
1221
0
            f.thunks.clear();
1222
0
            f.elementId = 0;
1223
1224
0
            auto *thunk = arr->elements[f.elementId];
1225
0
            BindingFrame bindings = func->upValues;
1226
0
            bindings[func->params[0].id] = thunk;
1227
0
            stack.newCall(loc, func, func->self, func->offset, bindings);
1228
0
            return func->body;
1229
0
        }
1230
0
        return nullptr;
1231
0
    }
1232
1233
    const AST *builtinObjectHasEx(const LocationRange &loc, const std::vector<Value> &args)
1234
0
    {
1235
0
        validateBuiltinArgs(
1236
0
            loc, "objectHasEx", args, {Value::OBJECT, Value::STRING, Value::BOOLEAN});
1237
0
        const auto *obj = static_cast<const HeapObject *>(args[0].v.h);
1238
0
        const auto *str = static_cast<const HeapString *>(args[1].v.h);
1239
0
        bool include_hidden = args[2].v.b;
1240
0
        bool found = false;
1241
0
        for (const auto &field : objectFields(obj, !include_hidden)) {
1242
0
            if (field->name == str->value) {
1243
0
                found = true;
1244
0
                break;
1245
0
            }
1246
0
        }
1247
0
        scratch = makeBoolean(found);
1248
0
        return nullptr;
1249
0
    }
1250
1251
    const AST *builtinLength(const LocationRange &loc, const std::vector<Value> &args)
1252
868k
    {
1253
868k
        if (args.size() != 1) {
1254
0
            throw makeError(loc, "length takes 1 parameter.");
1255
0
        }
1256
868k
        HeapEntity *e = args[0].v.h;
1257
868k
        switch (args[0].t) {
1258
1
            case Value::OBJECT: {
1259
1
                auto fields = objectFields(static_cast<HeapObject *>(e), true);
1260
1
                scratch = makeNumber(fields.size());
1261
1
            } break;
1262
1263
470k
            case Value::ARRAY:
1264
470k
                scratch = makeNumber(static_cast<HeapArray *>(e)->elements.size());
1265
470k
                break;
1266
1267
397k
            case Value::STRING:
1268
397k
                scratch = makeNumber(static_cast<HeapString *>(e)->value.length());
1269
397k
                break;
1270
1271
0
            case Value::FUNCTION:
1272
0
                scratch = makeNumber(static_cast<HeapClosure *>(e)->params.size());
1273
0
                break;
1274
1275
13
            default:
1276
13
                throw makeError(loc,
1277
13
                                "length operates on strings, objects, "
1278
13
                                "and arrays, got " +
1279
13
                                    type_str(args[0]));
1280
868k
        }
1281
868k
        return nullptr;
1282
868k
    }
1283
1284
    const AST *builtinObjectFieldsEx(const LocationRange &loc, const std::vector<Value> &args)
1285
242k
    {
1286
242k
        validateBuiltinArgs(loc, "objectFieldsEx", args, {Value::OBJECT, Value::BOOLEAN});
1287
242k
        const auto *obj = static_cast<HeapObject *>(args[0].v.h);
1288
242k
        bool include_hidden = args[1].v.b;
1289
        // Stash in a set first to sort them.
1290
242k
        std::set<UString> fields;
1291
822k
        for (const auto &field : objectFields(obj, !include_hidden)) {
1292
822k
            fields.insert(field->name);
1293
822k
        }
1294
242k
        scratch = makeArray({});
1295
242k
        auto &elements = static_cast<HeapArray *>(scratch.v.h)->elements;
1296
822k
        for (const auto &field : fields) {
1297
822k
            auto *th = makeHeap<HeapThunk>(idArrayElement, nullptr, 0, nullptr);
1298
822k
            elements.push_back(th);
1299
822k
            th->fill(makeString(field));
1300
822k
        }
1301
242k
        return nullptr;
1302
242k
    }
1303
1304
    const AST *builtinObjectRemoveKey(const LocationRange &loc, const std::vector<Value> &args)
1305
0
    {
1306
0
        validateBuiltinArgs(loc, "objectRemoveKey", args, {Value::OBJECT, Value::STRING});
1307
0
        auto *obj = static_cast<HeapObject *>(args[0].v.h);
1308
0
        const auto *key = static_cast<HeapString *>(args[1].v.h);
1309
0
        const auto *key_id = alloc->makeIdentifier(key->value);
1310
0
        auto fields = objectFieldsAux(obj);
1311
0
        const auto it = fields.find(key_id);
1312
0
        if (it != fields.end()) {
1313
0
            fields.erase(it);
1314
0
        }
1315
        // TODO: If the key isn't found perhaps we can evaluate to the existing `obj`?
1316
0
        scratch = makeObject<HeapRestrictedObject>(obj, fields);
1317
0
        return nullptr;
1318
0
    }
1319
1320
    const AST *builtinCodepoint(const LocationRange &loc, const std::vector<Value> &args)
1321
0
    {
1322
0
        validateBuiltinArgs(loc, "codepoint", args, {Value::STRING});
1323
0
        const UString &str = static_cast<HeapString *>(args[0].v.h)->value;
1324
0
        if (str.length() != 1) {
1325
0
            std::stringstream ss;
1326
0
            ss << "codepoint takes a string of length 1, got length " << str.length();
1327
0
            throw makeError(loc, ss.str());
1328
0
        }
1329
0
        char32_t c = static_cast<HeapString *>(args[0].v.h)->value[0];
1330
0
        scratch = makeNumber((unsigned long)(c));
1331
0
        return nullptr;
1332
0
    }
1333
1334
    const AST *builtinChar(const LocationRange &loc, const std::vector<Value> &args)
1335
2.03k
    {
1336
2.03k
        validateBuiltinArgs(loc, "char", args, {Value::NUMBER});
1337
2.03k
        long l = long(args[0].v.d);
1338
2.03k
        if (l < 0) {
1339
92
            std::stringstream ss;
1340
92
            ss << "codepoints must be >= 0, got " << l;
1341
92
            throw makeError(loc, ss.str());
1342
92
        }
1343
1.94k
        if (l >= JSONNET_CODEPOINT_MAX) {
1344
62
            std::stringstream ss;
1345
62
            ss << "invalid unicode codepoint, got " << l;
1346
62
            throw makeError(loc, ss.str());
1347
62
        }
1348
1.88k
        char32_t c = l;
1349
1.88k
        scratch = makeString(UString(&c, 1));
1350
1.88k
        return nullptr;
1351
1.94k
    }
1352
1353
    const AST *builtinLog(const LocationRange &loc, const std::vector<Value> &args)
1354
3.31k
    {
1355
3.31k
        validateBuiltinArgs(loc, "log", args, {Value::NUMBER});
1356
3.31k
        scratch = makeNumberCheck(loc, std::log(args[0].v.d));
1357
3.31k
        return nullptr;
1358
3.31k
    }
1359
1360
    const AST *builtinExp(const LocationRange &loc, const std::vector<Value> &args)
1361
0
    {
1362
0
        validateBuiltinArgs(loc, "exp", args, {Value::NUMBER});
1363
0
        scratch = makeNumberCheck(loc, std::exp(args[0].v.d));
1364
0
        return nullptr;
1365
0
    }
1366
1367
    const AST *builtinMantissa(const LocationRange &loc, const std::vector<Value> &args)
1368
0
    {
1369
0
        validateBuiltinArgs(loc, "mantissa", args, {Value::NUMBER});
1370
0
        int exp;
1371
0
        double m = std::frexp(args[0].v.d, &exp);
1372
0
        scratch = makeNumberCheck(loc, m);
1373
0
        return nullptr;
1374
0
    }
1375
1376
    const AST *builtinExponent(const LocationRange &loc, const std::vector<Value> &args)
1377
0
    {
1378
0
        validateBuiltinArgs(loc, "exponent", args, {Value::NUMBER});
1379
0
        int exp;
1380
0
        std::frexp(args[0].v.d, &exp);
1381
0
        scratch = makeNumberCheck(loc, exp);
1382
0
        return nullptr;
1383
0
    }
1384
1385
    const AST *builtinModulo(const LocationRange &loc, const std::vector<Value> &args)
1386
71.4k
    {
1387
71.4k
        validateBuiltinArgs(loc, "modulo", args, {Value::NUMBER, Value::NUMBER});
1388
71.4k
        double a = args[0].v.d;
1389
71.4k
        double b = args[1].v.d;
1390
71.4k
        if (b == 0)
1391
25
            throw makeError(loc, "division by zero.");
1392
71.3k
        scratch = makeNumberCheck(loc, std::fmod(a, b));
1393
71.3k
        return nullptr;
1394
71.4k
    }
1395
1396
    const AST *builtinExtVar(const LocationRange &loc, const std::vector<Value> &args)
1397
0
    {
1398
0
        validateBuiltinArgs(loc, "extVar", args, {Value::STRING});
1399
0
        const UString &var = static_cast<HeapString *>(args[0].v.h)->value;
1400
0
        std::string var8 = encode_utf8(var);
1401
0
        auto it = externalVars.find(var8);
1402
0
        if (it == externalVars.end()) {
1403
0
            std::string msg = "undefined external variable: " + var8;
1404
0
            throw makeError(loc, msg);
1405
0
        }
1406
0
        const VmExt &ext = it->second;
1407
0
        if (ext.isCode) {
1408
0
            std::string filename = "<extvar:" + var8 + ">";
1409
0
            Tokens tokens = jsonnet_lex(filename, ext.data.c_str());
1410
0
            AST *expr = jsonnet_parse(alloc, tokens);
1411
0
            jsonnet_desugar(alloc, expr, nullptr);
1412
0
            jsonnet_static_analysis(expr);
1413
0
            stack.pop();
1414
0
            return expr;
1415
0
        } else {
1416
0
            scratch = makeString(decode_utf8(ext.data));
1417
0
            return nullptr;
1418
0
        }
1419
0
    }
1420
1421
    const AST *builtinPrimitiveEquals(const LocationRange &loc, const std::vector<Value> &args)
1422
6.38M
    {
1423
6.38M
        if (args.size() != 2) {
1424
0
            std::stringstream ss;
1425
0
            ss << "primitiveEquals takes 2 parameters, got " << args.size();
1426
0
            throw makeError(loc, ss.str());
1427
0
        }
1428
6.38M
        if (args[0].t != args[1].t) {
1429
0
            scratch = makeBoolean(false);
1430
0
            return nullptr;
1431
0
        }
1432
6.38M
        bool r;
1433
6.38M
        switch (args[0].t) {
1434
113k
            case Value::BOOLEAN: r = args[0].v.b == args[1].v.b; break;
1435
1436
260k
            case Value::NUMBER: r = args[0].v.d == args[1].v.d; break;
1437
1438
5.91M
            case Value::STRING:
1439
5.91M
                r = static_cast<HeapString *>(args[0].v.h)->value ==
1440
5.91M
                    static_cast<HeapString *>(args[1].v.h)->value;
1441
5.91M
                break;
1442
1443
103k
            case Value::NULL_TYPE: r = true; break;
1444
1445
0
            case Value::FUNCTION: throw makeError(loc, "cannot test equality of functions"); break;
1446
1447
0
            default:
1448
0
                throw makeError(loc,
1449
0
                                "primitiveEquals operates on primitive "
1450
0
                                "types, got " +
1451
0
                                    type_str(args[0]));
1452
6.38M
        }
1453
6.38M
        scratch = makeBoolean(r);
1454
6.38M
        return nullptr;
1455
6.38M
    }
1456
1457
    const AST *builtinNative(const LocationRange &loc, const std::vector<Value> &args)
1458
0
    {
1459
0
        validateBuiltinArgs(loc, "native", args, {Value::STRING});
1460
1461
        // Add a prefix to avoid conflicting with any name in `std`.
1462
0
        std::string builtin_name = "native:" + encode_utf8(static_cast<HeapString *>(args[0].v.h)->value);
1463
1464
0
        VmNativeCallbackMap::const_iterator nit = nativeCallbacks.find(builtin_name);
1465
0
        if (nit == nativeCallbacks.end()) {
1466
0
            scratch = makeNull();
1467
0
        } else {
1468
0
            const VmNativeCallback &cb = nit->second;
1469
0
            const auto &params = cb.params;
1470
0
            const auto body = alloc->makeBuiltinBody(LocationRange{}, builtin_name,
1471
0
                [this, &params]()->Identifiers {
1472
0
                    Identifiers ids;
1473
0
                    for (const auto &name : params) {
1474
0
                        ids.emplace_back(this->alloc->makeIdentifier(decode_utf8(name)));
1475
0
                    }
1476
0
                    return ids;
1477
0
                });
1478
0
            scratch = makeBuiltinFromAST(body);
1479
0
        }
1480
0
        return nullptr;
1481
0
    }
1482
1483
    const AST *builtinMd5(const LocationRange &loc, const std::vector<Value> &args)
1484
0
    {
1485
0
        validateBuiltinArgs(loc, "md5", args, {Value::STRING});
1486
1487
0
        std::string value = encode_utf8(static_cast<HeapString *>(args[0].v.h)->value);
1488
1489
0
        scratch = makeString(decode_utf8(md5(value)));
1490
0
        return nullptr;
1491
0
    }
1492
1493
    const AST *builtinEncodeUTF8(const LocationRange &loc, const std::vector<Value> &args)
1494
0
    {
1495
0
        validateBuiltinArgs(loc, "encodeUTF8", args, {Value::STRING});
1496
1497
0
        std::string byteString = encode_utf8(static_cast<HeapString *>(args[0].v.h)->value);
1498
1499
0
        scratch = makeArray({});
1500
0
        auto &elements = static_cast<HeapArray *>(scratch.v.h)->elements;
1501
0
        for (const auto c : byteString) {
1502
0
            auto *th = makeHeap<HeapThunk>(idArrayElement, nullptr, 0, nullptr);
1503
0
            elements.push_back(th);
1504
0
            th->fill(makeNumber(uint8_t(c)));
1505
0
        }
1506
0
        return nullptr;
1507
0
    }
1508
1509
    const AST *decodeUTF8(void)
1510
0
    {
1511
0
        Frame &f = stack.top();
1512
0
        const auto& elements = static_cast<HeapArray*>(f.val.v.h)->elements;
1513
0
        while (f.elementId < elements.size()) {
1514
0
            auto *th = elements[f.elementId];
1515
0
            if (th->filled) {
1516
0
                auto b = th->content;
1517
0
                if (b.t != Value::NUMBER) {
1518
0
                    std::stringstream ss;
1519
0
                    ss << "Element " << f.elementId << " of the provided array was not a number";
1520
0
                    throw makeError(stack.top().location, ss.str());
1521
0
                } else {
1522
0
                    double d = b.v.d;
1523
0
                    if (d < 0 || d > 255 || d != int(d)) {
1524
0
                        std::stringstream ss;
1525
0
                        ss << "Element " << f.elementId << " of the provided array was not an integer in range [0,255]";
1526
0
                        throw makeError(stack.top().location, ss.str());
1527
0
                    }
1528
0
                    f.bytes.push_back(uint8_t(d));
1529
0
                }
1530
0
                f.elementId++;
1531
0
            } else {
1532
0
                stack.newCall(f.location, th, th->self, th->offset, th->upValues);
1533
0
                return th->body;
1534
0
            }
1535
0
        }
1536
0
        scratch = makeString(decode_utf8(f.bytes));
1537
0
        return nullptr;
1538
0
    }
1539
1540
    const AST *builtinDecodeUTF8(const LocationRange &loc, const std::vector<Value> &args)
1541
0
    {
1542
0
        validateBuiltinArgs(loc, "decodeUTF8", args, {Value::ARRAY});
1543
1544
0
        Frame &f = stack.top();
1545
0
        f.kind = FRAME_BUILTIN_DECODE_UTF8;
1546
0
        f.val = args[0]; // arr
1547
0
        f.bytes.clear();
1548
0
        f.elementId = 0;
1549
0
        return decodeUTF8();
1550
0
    }
1551
1552
    const AST *builtinTrace(const LocationRange &loc, const std::vector<Value> &args)
1553
0
    {
1554
0
        if(args[0].t != Value::STRING) {
1555
0
            std::stringstream ss;
1556
0
            ss << "Builtin function trace expected string as first parameter but "
1557
0
               << "got " << type_str(args[0].t);
1558
0
            throw makeError(loc, ss.str());
1559
0
        }
1560
1561
0
        std::string str = encode_utf8(static_cast<HeapString *>(args[0].v.h)->value);
1562
0
        std::cerr << "TRACE: " << loc.file << ":" << loc.begin.line << " " <<  str
1563
0
            << std::endl;
1564
1565
0
        scratch = args[1];
1566
0
        return nullptr;
1567
0
    }
1568
1569
    const AST *builtinSplitLimit(const LocationRange &loc, const std::vector<Value> &args)
1570
0
    {
1571
0
        validateBuiltinArgs(loc, "splitLimit", args, {Value::STRING, Value::STRING, Value::NUMBER});
1572
0
        const auto *str = static_cast<const HeapString *>(args[0].v.h);
1573
0
        const auto *c = static_cast<const HeapString *>(args[1].v.h);
1574
0
        long maxsplits = long(args[2].v.d);
1575
0
        unsigned start = 0;
1576
0
        unsigned test = 0;
1577
0
        scratch = makeArray({});
1578
0
        auto &elements = static_cast<HeapArray *>(scratch.v.h)->elements;
1579
0
        while (test < str->value.size() && (maxsplits == -1 ||
1580
0
                                            size_t(maxsplits) > elements.size())) {
1581
0
            if (c->value == str->value.substr(test, c->value.size())) {
1582
0
                auto *th = makeHeap<HeapThunk>(idArrayElement, nullptr, 0, nullptr);
1583
0
                elements.push_back(th);
1584
0
                th->fill(makeString(str->value.substr(start, test - start)));
1585
0
                start = test + c->value.size();
1586
0
                test = start;
1587
0
            } else {
1588
0
                ++test;
1589
0
            }
1590
0
        }
1591
0
        auto *th = makeHeap<HeapThunk>(idArrayElement, nullptr, 0, nullptr);
1592
0
        elements.push_back(th);
1593
0
        th->fill(makeString(str->value.substr(start)));
1594
1595
0
        return nullptr;
1596
0
    }
1597
1598
    const AST *builtinSubstr(const LocationRange &loc, const std::vector<Value> &args)
1599
955
    {
1600
955
        validateBuiltinArgs(loc, "substr", args, {Value::STRING, Value::NUMBER, Value::NUMBER});
1601
955
        const auto *str = static_cast<const HeapString *>(args[0].v.h);
1602
955
        long from = long(args[1].v.d);
1603
955
        long len = long(args[2].v.d);
1604
955
        if (from < 0) {
1605
0
            std::stringstream ss;
1606
0
            ss << "substr second parameter should be greater than zero, got " << from;
1607
0
            throw makeError(loc, ss.str());
1608
0
        }
1609
955
        if (len < 0) {
1610
0
            std::stringstream ss;
1611
0
            ss << "substr third parameter should be greater than zero, got " << len;
1612
0
            throw makeError(loc, ss.str());
1613
0
        }
1614
955
        if (static_cast<unsigned long>(from) > str->value.size()) {
1615
0
            scratch = makeString(UString());
1616
0
            return nullptr;
1617
0
        }
1618
955
        if (size_t(len + from) > str->value.size()) {
1619
0
          len = str->value.size() - from;
1620
0
        }
1621
955
        scratch = makeString(str->value.substr(from, len));
1622
955
        return nullptr;
1623
955
    }
1624
1625
    const AST *builtinRange(const LocationRange &loc, const std::vector<Value> &args)
1626
0
    {
1627
0
        validateBuiltinArgs(loc, "range", args, {Value::NUMBER, Value::NUMBER});
1628
0
        long from = long(args[0].v.d);
1629
0
        long to = long(args[1].v.d);
1630
0
        long len = to - from + 1;
1631
0
        scratch = makeArray({});
1632
0
        if (len > 0) {
1633
0
            auto &elements = static_cast<HeapArray *>(scratch.v.h)->elements;
1634
0
            for (int i = 0; i < len; ++i) {
1635
0
                auto *th = makeHeap<HeapThunk>(idArrayElement, nullptr, 0, nullptr);
1636
0
                elements.push_back(th);
1637
0
                th->fill(makeNumber(from + i));
1638
0
            }
1639
0
        }
1640
0
        return nullptr;
1641
0
    }
1642
1643
    const AST *builtinStrReplace(const LocationRange &loc, const std::vector<Value> &args)
1644
0
    {
1645
0
        validateBuiltinArgs(loc, "strReplace", args, {Value::STRING, Value::STRING, Value::STRING});
1646
0
        const auto *str = static_cast<const HeapString *>(args[0].v.h);
1647
0
        const auto *from = static_cast<const HeapString *>(args[1].v.h);
1648
0
        const auto *to = static_cast<const HeapString *>(args[2].v.h);
1649
0
        if (from->value.empty()) {
1650
0
          throw makeError(loc, "'from' string must not be zero length.");
1651
0
        }
1652
0
        UString new_str(str->value);
1653
0
        UString::size_type pos = 0;
1654
0
        while (pos < new_str.size()) {
1655
0
            auto index = new_str.find(from->value, pos);
1656
0
            if (index == new_str.npos) {
1657
0
                break;
1658
0
            }
1659
0
            new_str.replace(index, from->value.size(), to->value);
1660
0
            pos = index + to->value.size();
1661
0
        }
1662
0
        scratch = makeString(new_str);
1663
0
        return nullptr;
1664
0
    }
1665
1666
    const AST *builtinAsciiLower(const LocationRange &loc, const std::vector<Value> &args)
1667
0
    {
1668
0
        validateBuiltinArgs(loc, "asciiLower", args, {Value::STRING});
1669
0
        const auto *str = static_cast<const HeapString *>(args[0].v.h);
1670
0
        UString new_str(str->value);
1671
0
        for (size_t i = 0; i < new_str.size(); ++i) {
1672
0
            if (new_str[i] >= 'A' && new_str[i] <= 'Z') {
1673
0
                new_str[i] = new_str[i] - 'A' + 'a';
1674
0
            }
1675
0
        }
1676
0
        scratch = makeString(new_str);
1677
0
        return nullptr;
1678
0
    }
1679
1680
    const AST *builtinAsciiUpper(const LocationRange &loc, const std::vector<Value> &args)
1681
0
    {
1682
0
        validateBuiltinArgs(loc, "asciiUpper", args, {Value::STRING});
1683
0
        const auto *str = static_cast<const HeapString *>(args[0].v.h);
1684
0
        UString new_str(str->value);
1685
0
        for (size_t i = 0; i < new_str.size(); ++i) {
1686
0
            if (new_str[i] >= 'a' && new_str[i] <= 'z') {
1687
0
                new_str[i] = new_str[i] - 'a' + 'A';
1688
0
            }
1689
0
        }
1690
0
        scratch = makeString(new_str);
1691
0
        return nullptr;
1692
0
    }
1693
1694
    const AST *builtinParseJson(const LocationRange &loc, const std::vector<Value> &args)
1695
0
    {
1696
0
        validateBuiltinArgs(loc, "parseJson", args, {Value::STRING});
1697
1698
0
        std::string value = encode_utf8(static_cast<HeapString *>(args[0].v.h)->value);
1699
1700
0
        try {
1701
0
            auto j = json::parse(value);
1702
1703
0
            bool filled;
1704
0
            otherJsonToHeap(j, filled, scratch);
1705
0
        } catch (const json::parse_error &e) {
1706
0
            throw makeError(loc, e.what());
1707
0
        }
1708
1709
0
        return nullptr;
1710
0
    }
1711
1712
    const AST *builtinParseYaml(const LocationRange &loc, const std::vector<Value> &args)
1713
0
    {
1714
0
        validateBuiltinArgs(loc, "parseYaml", args, {Value::STRING});
1715
0
        std::string value = encode_utf8(static_cast<HeapString *>(args[0].v.h)->value);
1716
0
        json j;
1717
0
        try {
1718
            // Use a custom EventHandler so we can attach error handling.
1719
0
            ryml::EventHandlerTree et{ryml::Callbacks{
1720
0
                nullptr, nullptr, nullptr, &Interpreter::handleRapidYamlError
1721
0
            }};
1722
0
            ryml::Parser pe(&et);
1723
0
            ryml::Tree tree = ryml::parse_in_arena(&pe, ryml::to_csubstr(value));
1724
1725
0
            if (tree.type(tree.root_id()).is_notype()) {
1726
                // Nothing to do; `j` is already null!
1727
0
            } else if (tree.is_stream(tree.root_id())) {
1728
0
                for (ryml::ConstNodeRef node : tree.crootref().children()) {
1729
0
                    std::ostringstream jsonText;
1730
0
                    jsonText << ryml::as_json(node);
1731
0
                    j.push_back(json::parse(jsonText.str()));
1732
0
                }
1733
0
            } else {
1734
0
                std::ostringstream jsonText;
1735
0
                jsonText << ryml::as_json(tree);
1736
0
                j = json::parse(jsonText.str());
1737
0
            }
1738
0
        } catch (const RapidYamlError& exc) {
1739
0
            throw makeError(loc, exc.msg_);
1740
0
        }
1741
0
        bool filled_unused;
1742
0
        otherJsonToHeap(j, filled_unused, scratch);
1743
0
        return nullptr;
1744
0
    }
1745
1746
0
    const std::vector<std::string> split(const std::string& s, const std::string& delimiter) {
1747
0
        size_t pos_start = 0, pos_end, delim_len = delimiter.length();
1748
0
        std::string token;
1749
0
        std::vector<std::string> res;
1750
0
1751
0
        while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) {
1752
0
            token = s.substr(pos_start, pos_end - pos_start);
1753
0
            pos_start = pos_end + delim_len;
1754
0
            res.push_back(token);
1755
0
        }
1756
0
1757
0
        res.push_back(s.substr(pos_start));
1758
0
        return res;
1759
0
    }
1760
1761
0
    void otherJsonToHeap(const json &v, bool &filled, Value &attach) {
1762
        // In order to not anger the garbage collector, assign to attach immediately after
1763
        // making the heap object.
1764
0
        switch (v.type()) {
1765
0
            case json::value_t::string:
1766
0
                attach = makeString(decode_utf8(v.get<std::string>()));
1767
0
                filled = true;
1768
0
                break;
1769
1770
0
            case json::value_t::boolean:
1771
0
                attach = makeBoolean(v.get<bool>());
1772
0
                filled = true;
1773
0
                break;
1774
1775
0
            case json::value_t::number_integer:
1776
0
            case json::value_t::number_unsigned:
1777
0
            case json::value_t::number_float:
1778
0
                attach = makeNumber(v.get<double>());
1779
0
                filled = true;
1780
0
                break;
1781
1782
0
            case json::value_t::null:
1783
0
                attach = makeNull();
1784
0
                filled = true;
1785
0
                break;
1786
1787
0
            case json::value_t::array:{
1788
0
                attach = makeArray(std::vector<HeapThunk *>{});
1789
0
                filled = true;
1790
0
                auto *arr = static_cast<HeapArray *>(attach.v.h);
1791
0
                for (size_t i = 0; i < v.size(); ++i) {
1792
0
                    arr->elements.push_back(makeHeap<HeapThunk>(idArrayElement, nullptr, 0, nullptr));
1793
0
                    otherJsonToHeap(v[i], arr->elements[i]->filled, arr->elements[i]->content);
1794
0
                }
1795
0
            } break;
1796
1797
0
            case json::value_t::object: {
1798
0
                attach = makeObject<HeapComprehensionObject>(
1799
0
                    BindingFrame{}, jsonObjVar, idJsonObjVar, BindingFrame{});
1800
0
                filled = true;
1801
0
                auto *obj = static_cast<HeapComprehensionObject *>(attach.v.h);
1802
0
                for (auto it = v.begin(); it != v.end(); ++it) {
1803
0
                    auto *thunk = makeHeap<HeapThunk>(idJsonObjVar, nullptr, 0, nullptr);
1804
0
                    obj->compValues[alloc->makeIdentifier(decode_utf8(it.key()))] = thunk;
1805
0
                    otherJsonToHeap(it.value(), thunk->filled, thunk->content);
1806
0
                }
1807
0
            } break;
1808
1809
0
            case json::value_t::discarded: {
1810
0
                abort();
1811
0
            }
1812
1813
0
            default: {
1814
                // Newer nlohmann json.hpp (from v3.8.0 https://github.com/nlohmann/json/pull/1662)
1815
                // add a `value_t::binary` type, used when dealing with some JSON-adjacent binary
1816
                // formats (BSON, CBOR, etc). Since it doesn't exist prior to v3.8.0 we can't match
1817
                // on it explicitly, but we can just treat any unknown type as an error.
1818
0
                abort();
1819
0
            }
1820
0
        }
1821
0
    }
1822
1823
    void joinString(bool &first, UString &running, const Value &sep, unsigned idx, const Value &elt)
1824
0
    {
1825
0
        if (elt.t == Value::NULL_TYPE) {
1826
0
            return;
1827
0
        }
1828
0
        if (elt.t != Value::STRING) {
1829
0
            std::stringstream ss;
1830
0
            ss << "expected string but arr[" << idx << "] was " << type_str(elt);
1831
0
            throw makeError(stack.top().location, ss.str());
1832
0
        }
1833
0
        if (!first) {
1834
0
            running.append(static_cast<HeapString *>(sep.v.h)->value);
1835
0
        }
1836
0
        first = false;
1837
0
        running.append(static_cast<HeapString *>(elt.v.h)->value);
1838
0
    }
1839
1840
    const AST *joinStrings(void)
1841
0
    {
1842
0
        Frame &f = stack.top();
1843
0
        const auto& elements = static_cast<HeapArray*>(f.val2.v.h)->elements;
1844
0
        while (f.elementId < elements.size()) {
1845
0
            auto *th = elements[f.elementId];
1846
0
            if (th->filled) {
1847
0
                joinString(f.first, f.str, f.val, f.elementId, th->content);
1848
0
                f.elementId++;
1849
0
            } else {
1850
0
                stack.newCall(f.location, th, th->self, th->offset, th->upValues);
1851
0
                return th->body;
1852
0
            }
1853
0
        }
1854
0
        scratch = makeString(f.str);
1855
0
        return nullptr;
1856
0
    }
1857
1858
    void joinArray(bool &first, std::vector<HeapThunk*> &running, const Value &sep, unsigned idx,
1859
                   const Value &elt)
1860
0
    {
1861
0
        if (elt.t == Value::NULL_TYPE) {
1862
0
            return;
1863
0
        }
1864
0
        if (elt.t != Value::ARRAY) {
1865
0
            std::stringstream ss;
1866
0
            ss << "expected array but arr[" << idx << "] was " << type_str(elt);
1867
0
            throw makeError(stack.top().location, ss.str());
1868
0
        }
1869
0
        if (!first) {
1870
0
            auto& elts = static_cast<HeapArray *>(sep.v.h)->elements;
1871
0
            running.insert(running.end(), elts.begin(), elts.end());
1872
0
        }
1873
0
        first = false;
1874
0
        auto& elts = static_cast<HeapArray *>(elt.v.h)->elements;
1875
0
        running.insert(running.end(), elts.begin(), elts.end());
1876
0
    }
1877
1878
    const AST *joinArrays(void)
1879
0
    {
1880
0
        Frame &f = stack.top();
1881
0
        const auto& elements = static_cast<HeapArray*>(f.val2.v.h)->elements;
1882
0
        while (f.elementId < elements.size()) {
1883
0
            auto *th = elements[f.elementId];
1884
0
            if (th->filled) {
1885
0
                joinArray(f.first, f.thunks, f.val, f.elementId, th->content);
1886
0
                f.elementId++;
1887
0
            } else {
1888
0
                stack.newCall(f.location, th, th->self, th->offset, th->upValues);
1889
0
                return th->body;
1890
0
            }
1891
0
        }
1892
0
        scratch = makeArray(f.thunks);
1893
0
        return nullptr;
1894
0
    }
1895
1896
    const AST *builtinJoin(const LocationRange &loc, const std::vector<Value> &args)
1897
0
    {
1898
0
        if (args[0].t != Value::ARRAY && args[0].t != Value::STRING) {
1899
0
            std::stringstream ss;
1900
0
            ss << "join first parameter should be string or array, got " << type_str(args[0]);
1901
0
            throw makeError(loc, ss.str());
1902
0
        }
1903
0
        if (args[1].t != Value::ARRAY) {
1904
0
            std::stringstream ss;
1905
0
            ss << "join second parameter should be array, got " << type_str(args[1]);
1906
0
            throw makeError(loc, ss.str());
1907
0
        }
1908
0
        Frame &f = stack.top();
1909
0
        if (args[0].t == Value::STRING) {
1910
0
            f.kind = FRAME_BUILTIN_JOIN_STRINGS;
1911
0
            f.val = args[0];  // sep
1912
0
            f.val2 = args[1];  // arr
1913
0
            f.str.clear();
1914
0
            f.first = true;
1915
0
            f.elementId = 0;
1916
0
            return joinStrings();
1917
0
        } else {
1918
0
            f.kind = FRAME_BUILTIN_JOIN_ARRAYS;
1919
0
            f.val = args[0];  // sep
1920
0
            f.val2 = args[1];  // arr
1921
0
            f.thunks.clear();
1922
0
            f.first = true;
1923
0
            f.elementId = 0;
1924
0
            return joinArrays();
1925
0
        }
1926
0
    }
1927
1928
    void jsonToHeap(const std::unique_ptr<JsonnetJsonValue> &v, bool &filled, Value &attach)
1929
0
    {
1930
        // In order to not anger the garbage collector, assign to attach immediately after
1931
        // making the heap object.
1932
0
        switch (v->kind) {
1933
0
            case JsonnetJsonValue::STRING:
1934
0
                attach = makeString(decode_utf8(v->string));
1935
0
                filled = true;
1936
0
                break;
1937
1938
0
            case JsonnetJsonValue::BOOL:
1939
0
                attach = makeBoolean(v->number != 0.0);
1940
0
                filled = true;
1941
0
                break;
1942
1943
0
            case JsonnetJsonValue::NUMBER:
1944
0
                attach = makeNumber(v->number);
1945
0
                filled = true;
1946
0
                break;
1947
1948
0
            case JsonnetJsonValue::NULL_KIND:
1949
0
                attach = makeNull();
1950
0
                filled = true;
1951
0
                break;
1952
1953
0
            case JsonnetJsonValue::ARRAY: {
1954
0
                attach = makeArray(std::vector<HeapThunk *>{});
1955
0
                filled = true;
1956
0
                auto *arr = static_cast<HeapArray *>(attach.v.h);
1957
0
                for (size_t i = 0; i < v->elements.size(); ++i) {
1958
0
                    arr->elements.push_back(
1959
0
                        makeHeap<HeapThunk>(idArrayElement, nullptr, 0, nullptr));
1960
0
                    jsonToHeap(v->elements[i], arr->elements[i]->filled, arr->elements[i]->content);
1961
0
                }
1962
0
            } break;
1963
1964
0
            case JsonnetJsonValue::OBJECT: {
1965
0
                attach = makeObject<HeapComprehensionObject>(
1966
0
                    BindingFrame{}, jsonObjVar, idJsonObjVar, BindingFrame{});
1967
0
                filled = true;
1968
0
                auto *obj = static_cast<HeapComprehensionObject *>(attach.v.h);
1969
0
                for (const auto &pair : v->fields) {
1970
0
                    auto *thunk = makeHeap<HeapThunk>(idJsonObjVar, nullptr, 0, nullptr);
1971
0
                    obj->compValues[alloc->makeIdentifier(decode_utf8(pair.first))] = thunk;
1972
0
                    jsonToHeap(pair.second, thunk->filled, thunk->content);
1973
0
                }
1974
0
            } break;
1975
0
        }
1976
0
    }
1977
1978
    UString toString(const LocationRange &loc)
1979
500k
    {
1980
500k
        return manifestJson(loc, false, U"");
1981
500k
    }
1982
1983
    /** Recursively collect an object's invariants.
1984
     *
1985
     * \param curr
1986
     * \param self
1987
     * \param offset
1988
     * \param thunks
1989
     */
1990
    void objectInvariants(HeapObject *curr, HeapObject *self, unsigned &counter,
1991
                          std::vector<HeapThunk *> &thunks)
1992
82.2M
    {
1993
82.2M
        if (auto *ext = dynamic_cast<HeapExtendedObject *>(curr)) {
1994
33.0M
            objectInvariants(ext->right, self, counter, thunks);
1995
33.0M
            objectInvariants(ext->left, self, counter, thunks);
1996
49.1M
        } else if (auto *ext = dynamic_cast<HeapRestrictedObject *>(curr)) {
1997
0
            objectInvariants(ext->obj, self, counter, thunks);
1998
49.1M
        } else {
1999
49.1M
            if (auto *simp = dynamic_cast<HeapSimpleObject *>(curr)) {
2000
49.1M
                for (AST *assert : simp->asserts) {
2001
1.14M
                    auto *el_th = makeHeap<HeapThunk>(idInvariant, self, counter, assert);
2002
1.14M
                    el_th->upValues = simp->upValues;
2003
1.14M
                    thunks.push_back(el_th);
2004
1.14M
                }
2005
49.1M
            }
2006
49.1M
            counter++;
2007
49.1M
        }
2008
82.2M
    }
2009
2010
    /** Index an object's field.
2011
     *
2012
     * \param loc Location where the e.f occurred.
2013
     * \param obj The target
2014
     * \param f The field
2015
     */
2016
    const AST *objectIndex(const LocationRange &loc, HeapObject *obj, const Identifier *f,
2017
                           unsigned offset)
2018
20.1M
    {
2019
20.1M
        unsigned found_at = 0;
2020
20.1M
        HeapObject *self = obj;
2021
20.1M
        HeapLeafObject *found = findObject(f, obj, offset, found_at);
2022
20.1M
        if (found == nullptr) {
2023
42
            throw makeError(loc, "field does not exist: " + encode_utf8(f->name));
2024
42
        }
2025
20.1M
        if (auto *simp = dynamic_cast<HeapSimpleObject *>(found)) {
2026
20.1M
            auto it = simp->fields.find(f);
2027
20.1M
            const AST *body = it->second.body;
2028
2029
20.1M
            stack.newCall(loc, simp, self, found_at, simp->upValues);
2030
20.1M
            return body;
2031
20.1M
        } else {
2032
            // If a HeapLeafObject is not HeapSimpleObject, it must be HeapComprehensionObject.
2033
0
            auto *comp = static_cast<HeapComprehensionObject *>(found);
2034
0
            auto it = comp->compValues.find(f);
2035
0
            auto *th = it->second;
2036
0
            BindingFrame binds = comp->upValues;
2037
0
            binds[comp->id] = th;
2038
0
            stack.newCall(loc, comp, self, found_at, binds);
2039
0
            return comp->value;
2040
0
        }
2041
20.1M
    }
2042
2043
    void runInvariants(const LocationRange &loc, HeapObject *self)
2044
1.16M
    {
2045
1.16M
        if (stack.alreadyExecutingInvariants(self))
2046
2.29k
            return;
2047
2048
1.16M
        unsigned counter = 0;
2049
1.16M
        unsigned initial_stack_size = stack.size();
2050
1.16M
        stack.newFrame(FRAME_INVARIANTS, loc);
2051
1.16M
        std::vector<HeapThunk *> &thunks = stack.top().thunks;
2052
1.16M
        objectInvariants(self, self, counter, thunks);
2053
1.16M
        if (thunks.size() == 0) {
2054
1.14M
            stack.pop();
2055
1.14M
            return;
2056
1.14M
        }
2057
22.6k
        HeapThunk *thunk = thunks[0];
2058
22.6k
        stack.top().elementId = 1;
2059
22.6k
        stack.top().self = self;
2060
22.6k
        stack.newCall(loc, thunk, thunk->self, thunk->offset, thunk->upValues);
2061
22.6k
        evaluate(thunk->body, initial_stack_size);
2062
22.6k
    }
2063
2064
    /** Call a sourceVal function with given arguments.
2065
     *
2066
     * This function requires all arguments to be positional. It also does not
2067
     * support default arguments. This is intended to be used internally so,
2068
     * error checking is also skipped.
2069
     */
2070
28.3k
    const AST *callSourceVal(const AST *ast, HeapThunk *sourceVal, std::vector<HeapThunk*> args) {
2071
28.3k
        assert(sourceVal != nullptr);
2072
28.3k
        assert(sourceVal->filled);
2073
28.3k
        assert(sourceVal->content.t == Value::FUNCTION);
2074
28.3k
        auto *func = static_cast<HeapClosure *>(sourceVal->content.v.h);
2075
28.3k
        BindingFrame up_values = func->upValues;
2076
84.9k
        for (size_t i = 0; i < args.size(); ++i) {
2077
56.6k
            up_values.insert({func->params[i].id, args[i]});
2078
56.6k
        }
2079
28.3k
        stack.newCall(ast->location, func, func->self, func->offset, up_values);
2080
28.3k
        return func->body;
2081
28.3k
    }
2082
2083
    /** Evaluate the given AST to a value.
2084
     *
2085
     * Rather than call itself recursively, this function maintains a separate stack of
2086
     * partially-evaluated constructs.  First, the AST is handled depending on its type.  If
2087
     * this cannot be completed without evaluating another AST (e.g. a sub expression) then a
2088
     * frame is pushed onto the stack containing the partial state, and the code jumps back to
2089
     * the beginning of this function.  Once there are no more ASTs to evaluate, the code
2090
     * executes the second part of the function to unwind the stack.  If the stack cannot be
2091
     * completely unwound without evaluating an AST then it jumps back to the beginning of the
2092
     * function again.  The process terminates when the AST has been processed and the stack is
2093
     * the same size it was at the beginning of the call to evaluate.
2094
     */
2095
    void evaluate(const AST *ast_, unsigned initial_stack_size)
2096
9.80M
    {
2097
188M
    recurse:
2098
2099
188M
        switch (ast_->type) {
2100
14.9M
            case AST_APPLY: {
2101
14.9M
                const auto &ast = *static_cast<const Apply *>(ast_);
2102
14.9M
                stack.newFrame(FRAME_APPLY_TARGET, ast_);
2103
14.9M
                ast_ = ast.target;
2104
14.9M
                goto recurse;
2105
0
            } break;
2106
2107
1.91M
            case AST_ARRAY: {
2108
1.91M
                const auto &ast = *static_cast<const Array *>(ast_);
2109
1.91M
                HeapObject *self;
2110
1.91M
                unsigned offset;
2111
1.91M
                stack.getSelfBinding(self, offset);
2112
1.91M
                scratch = makeArray({});
2113
1.91M
                auto &elements = static_cast<HeapArray *>(scratch.v.h)->elements;
2114
15.8M
                for (const auto &el : ast.elements) {
2115
15.8M
                    auto *el_th = makeHeap<HeapThunk>(idArrayElement, self, offset, el.expr);
2116
15.8M
                    el_th->upValues = capture(el.expr->freeVariables);
2117
15.8M
                    elements.push_back(el_th);
2118
15.8M
                }
2119
1.91M
            } break;
2120
2121
8.36M
            case AST_BINARY: {
2122
8.36M
                const auto &ast = *static_cast<const Binary *>(ast_);
2123
8.36M
                stack.newFrame(FRAME_BINARY_LEFT, ast_);
2124
8.36M
                ast_ = ast.left;
2125
8.36M
                goto recurse;
2126
0
            } break;
2127
2128
11.6M
            case AST_BUILTIN_FUNCTION: {
2129
11.6M
                const auto &ast = *static_cast<const BuiltinFunction *>(ast_);
2130
11.6M
                scratch = makeBuiltinFromAST(ast.body);
2131
11.6M
            } break;
2132
2133
0
            case AST_BUILTIN_FUNCTION_BODY: {
2134
                // We can reach here if we end up evaluating a HeapThunk that was constructed
2135
                // from a built-in function. Synthesize a FRAME_BUILTIN_FORCE_THUNKS to call it.
2136
0
                assert(stack.top().isCall());
2137
0
                const auto fbody = static_cast<const BuiltinFunctionBody*>(ast_);
2138
2139
0
                const BindingFrame up_values = stack.top().bindings;
2140
0
                std::vector<HeapThunk*> arg_thunks;
2141
0
                for (const auto &p : fbody->params) {
2142
0
                    const auto it = up_values.find(p);
2143
0
                    if (it != up_values.end()) {
2144
0
                        arg_thunks.push_back(it->second);
2145
0
                    } else {
2146
0
                        JSONNET_UNREACHABLE();
2147
0
                    }
2148
0
                }
2149
0
                const LocationRange &loc = stack.top().location;
2150
2151
0
                stack.newFrame(FRAME_BUILTIN_FORCE_THUNKS, loc);
2152
0
                stack.top().bindings = up_values;
2153
0
                stack.top().thunks = arg_thunks;
2154
0
                stack.top().val = makeBuiltinFromAST(fbody);
2155
0
            } break;
2156
2157
11.3M
            case AST_CONDITIONAL: {
2158
11.3M
                const auto &ast = *static_cast<const Conditional *>(ast_);
2159
11.3M
                stack.newFrame(FRAME_IF, ast_);
2160
11.3M
                ast_ = ast.cond;
2161
11.3M
                goto recurse;
2162
0
            } break;
2163
2164
8.44k
            case AST_ERROR: {
2165
8.44k
                const auto &ast = *static_cast<const Error *>(ast_);
2166
8.44k
                stack.newFrame(FRAME_ERROR, ast_);
2167
8.44k
                ast_ = ast.expr;
2168
8.44k
                goto recurse;
2169
0
            } break;
2170
2171
2.80M
            case AST_FUNCTION: {
2172
2.80M
                const auto &ast = *static_cast<const Function *>(ast_);
2173
2.80M
                auto env = capture(ast.freeVariables);
2174
2.80M
                HeapObject *self;
2175
2.80M
                unsigned offset;
2176
2.80M
                stack.getSelfBinding(self, offset);
2177
2.80M
                HeapClosure::Params params;
2178
2.80M
                params.reserve(ast.params.size());
2179
5.32M
                for (const auto &p : ast.params) {
2180
5.32M
                    params.emplace_back(p.id, p.expr);
2181
5.32M
                }
2182
2.80M
                scratch = makeClosure(env, self, offset, params, ast.body);
2183
2.80M
            } break;
2184
2185
45
            case AST_IMPORT: {
2186
45
                const auto &ast = *static_cast<const Import *>(ast_);
2187
45
                HeapThunk *thunk = import(ast.location, ast.file);
2188
45
                if (thunk->filled) {
2189
0
                    scratch = thunk->content;
2190
45
                } else {
2191
45
                    stack.newCall(ast.location, thunk, thunk->self, thunk->offset, thunk->upValues);
2192
45
                    ast_ = thunk->body;
2193
45
                    goto recurse;
2194
45
                }
2195
45
            } break;
2196
2197
20
            case AST_IMPORTSTR: {
2198
20
                const auto &ast = *static_cast<const Importstr *>(ast_);
2199
20
                const ImportCacheValue *value = importData(ast.location, ast.file);
2200
20
                scratch = makeString(decode_utf8(value->content));
2201
20
            } break;
2202
2203
9
            case AST_IMPORTBIN: {
2204
9
                const auto &ast = *static_cast<const Importbin *>(ast_);
2205
9
                const ImportCacheValue *value = importData(ast.location, ast.file);
2206
9
                scratch = makeArray({});
2207
9
                auto &elements = static_cast<HeapArray *>(scratch.v.h)->elements;
2208
9
                elements.reserve(value->content.size());
2209
9
                for (const auto c : value->content) {
2210
0
                    auto *th = makeHeap<HeapThunk>(idArrayElement, nullptr, 0, nullptr);
2211
0
                    elements.push_back(th);
2212
0
                    th->fill(makeNumber(uint8_t(c)));
2213
0
                }
2214
9
            } break;
2215
2216
4.13M
            case AST_IN_SUPER: {
2217
4.13M
                const auto &ast = *static_cast<const InSuper *>(ast_);
2218
4.13M
                stack.newFrame(FRAME_IN_SUPER_ELEMENT, ast_);
2219
4.13M
                ast_ = ast.element;
2220
4.13M
                goto recurse;
2221
45
            } break;
2222
2223
15.6M
            case AST_INDEX: {
2224
15.6M
                const auto &ast = *static_cast<const Index *>(ast_);
2225
15.6M
                stack.newFrame(FRAME_INDEX_TARGET, ast_);
2226
15.6M
                ast_ = ast.target;
2227
15.6M
                goto recurse;
2228
45
            } break;
2229
2230
7.90M
            case AST_LOCAL: {
2231
7.90M
                const auto &ast = *static_cast<const Local *>(ast_);
2232
7.90M
                stack.newFrame(FRAME_LOCAL, ast_);
2233
7.90M
                Frame &f = stack.top();
2234
                // First build all the thunks and bind them.
2235
7.90M
                HeapObject *self;
2236
7.90M
                unsigned offset;
2237
7.90M
                stack.getSelfBinding(self, offset);
2238
25.6M
                for (const auto &bind : ast.binds) {
2239
                    // Note that these 2 lines must remain separate to avoid the GC running
2240
                    // when bindings has a nullptr for key bind.first.
2241
25.6M
                    auto *th = makeHeap<HeapThunk>(bind.var, self, offset, bind.body);
2242
25.6M
                    f.bindings[bind.var] = th;
2243
25.6M
                }
2244
                // Now capture the environment (including the new thunks, to make cycles).
2245
25.6M
                for (const auto &bind : ast.binds) {
2246
25.6M
                    auto *thunk = f.bindings[bind.var];
2247
25.6M
                    thunk->upValues = capture(bind.body->freeVariables);
2248
25.6M
                }
2249
7.90M
                ast_ = ast.body;
2250
7.90M
                goto recurse;
2251
45
            } break;
2252
2253
781k
            case AST_LITERAL_BOOLEAN: {
2254
781k
                const auto &ast = *static_cast<const LiteralBoolean *>(ast_);
2255
781k
                scratch = makeBoolean(ast.value);
2256
781k
            } break;
2257
2258
9.50M
            case AST_LITERAL_NUMBER: {
2259
9.50M
                const auto &ast = *static_cast<const LiteralNumber *>(ast_);
2260
9.50M
                scratch = makeNumberCheck(ast_->location, ast.value);
2261
9.50M
            } break;
2262
2263
47.3M
            case AST_LITERAL_STRING: {
2264
47.3M
                const auto &ast = *static_cast<const LiteralString *>(ast_);
2265
47.3M
                scratch = makeString(ast.value);
2266
47.3M
            } break;
2267
2268
199k
            case AST_LITERAL_NULL: {
2269
199k
                scratch = makeNull();
2270
199k
            } break;
2271
2272
3.76M
            case AST_DESUGARED_OBJECT: {
2273
3.76M
                const auto &ast = *static_cast<const DesugaredObject *>(ast_);
2274
3.76M
                if (ast.fields.empty()) {
2275
2.36M
                    auto env = capture(ast.freeVariables);
2276
2.36M
                    std::map<const Identifier *, HeapSimpleObject::Field> fields;
2277
2.36M
                    scratch = makeObject<HeapSimpleObject>(env, fields, ast.asserts);
2278
2.36M
                } else {
2279
1.40M
                    auto env = capture(ast.freeVariables);
2280
1.40M
                    stack.newFrame(FRAME_OBJECT, ast_);
2281
1.40M
                    auto fit = ast.fields.begin();
2282
1.40M
                    stack.top().fit = fit;
2283
1.40M
                    ast_ = fit->name;
2284
1.40M
                    goto recurse;
2285
1.40M
                }
2286
3.76M
            } break;
2287
2288
2.36M
            case AST_OBJECT_COMPREHENSION_SIMPLE: {
2289
112
                const auto &ast = *static_cast<const ObjectComprehensionSimple *>(ast_);
2290
112
                stack.newFrame(FRAME_OBJECT_COMP_ARRAY, ast_);
2291
112
                ast_ = ast.array;
2292
112
                goto recurse;
2293
3.76M
            } break;
2294
2295
2.84M
            case AST_SELF: {
2296
2.84M
                scratch.t = Value::OBJECT;
2297
2.84M
                HeapObject *self;
2298
2.84M
                unsigned offset;
2299
2.84M
                stack.getSelfBinding(self, offset);
2300
2.84M
                scratch.v.h = self;
2301
2.84M
            } break;
2302
2303
3.70M
            case AST_SUPER_INDEX: {
2304
3.70M
                const auto &ast = *static_cast<const SuperIndex *>(ast_);
2305
3.70M
                stack.newFrame(FRAME_SUPER_INDEX, ast_);
2306
3.70M
                ast_ = ast.index;
2307
3.70M
                goto recurse;
2308
3.76M
            } break;
2309
2310
4.11M
            case AST_UNARY: {
2311
4.11M
                const auto &ast = *static_cast<const Unary *>(ast_);
2312
4.11M
                stack.newFrame(FRAME_UNARY, ast_);
2313
4.11M
                ast_ = ast.expr;
2314
4.11M
                goto recurse;
2315
3.76M
            } break;
2316
2317
37.5M
            case AST_VAR: {
2318
37.5M
                const auto &ast = *static_cast<const Var *>(ast_);
2319
37.5M
                auto *thunk = stack.lookUpVar(ast.id);
2320
37.5M
                if (thunk == nullptr) {
2321
0
                    std::cerr << "INTERNAL ERROR: Could not bind variable: "
2322
0
                              << encode_utf8(ast.id->name) << " at "
2323
0
                              << ast.location << std::endl;
2324
0
                    std::abort();
2325
0
                }
2326
37.5M
                if (thunk->filled) {
2327
28.3M
                    scratch = thunk->content;
2328
28.3M
                } else {
2329
9.15M
                    stack.newCall(ast.location, thunk, thunk->self, thunk->offset, thunk->upValues);
2330
9.15M
                    ast_ = thunk->body;
2331
9.15M
                    goto recurse;
2332
9.15M
                }
2333
37.5M
            } break;
2334
2335
28.3M
            default:
2336
0
                std::cerr << "INTERNAL ERROR: Unknown AST: " << ast_->type << std::endl;
2337
0
                std::abort();
2338
188M
        }
2339
2340
        // To evaluate another AST, set ast to it, then goto recurse.
2341
        // To pop, exit the switch or goto popframe
2342
        // To change the frame and re-enter the switch, goto replaceframe
2343
213M
        while (stack.size() > initial_stack_size) {
2344
203M
            Frame &f = stack.top();
2345
203M
            switch (f.kind) {
2346
14.9M
                case FRAME_APPLY_TARGET: {
2347
14.9M
                    const auto &ast = *static_cast<const Apply *>(f.ast);
2348
14.9M
                    if (scratch.t != Value::FUNCTION) {
2349
21
                        throw makeError(ast.location,
2350
21
                                        "only functions can be called, got " + type_str(scratch));
2351
21
                    }
2352
14.9M
                    auto *func = static_cast<HeapClosure *>(scratch.v.h);
2353
2354
14.9M
                    std::set<const Identifier *> params_needed;
2355
25.2M
                    for (const auto &param : func->params) {
2356
25.2M
                        params_needed.insert(param.id);
2357
25.2M
                    }
2358
2359
                    // Create thunks for arguments.
2360
14.9M
                    std::vector<HeapThunk *> positional_args;
2361
14.9M
                    BindingFrame args;
2362
14.9M
                    bool got_named = false;
2363
40.2M
                    for (std::size_t i = 0; i < ast.args.size(); ++i) {
2364
25.2M
                        const auto &arg = ast.args[i];
2365
2366
25.2M
                        const Identifier *name;
2367
25.2M
                        if (arg.id != nullptr) {
2368
0
                            got_named = true;
2369
0
                            name = arg.id;
2370
25.2M
                        } else {
2371
25.2M
                            if (got_named) {
2372
0
                                std::stringstream ss;
2373
0
                                ss << "internal error: got positional param after named at index "
2374
0
                                   << i;
2375
0
                                throw makeError(ast.location, ss.str());
2376
0
                            }
2377
25.2M
                            if (i >= func->params.size()) {
2378
0
                                std::stringstream ss;
2379
0
                                ss << "too many args, function has " << func->params.size()
2380
0
                                   << " parameter(s)";
2381
0
                                throw makeError(ast.location, ss.str());
2382
0
                            }
2383
25.2M
                            name = func->params[i].id;
2384
25.2M
                        }
2385
                        // Special case for builtin functions -- leave identifier blank for
2386
                        // them in the thunk.  This removes the thunk frame from the stacktrace.
2387
25.2M
                        const Identifier *name_ = func->isBuiltin() ? nullptr : name;
2388
25.2M
                        HeapObject *self;
2389
25.2M
                        unsigned offset;
2390
25.2M
                        stack.getSelfBinding(self, offset);
2391
25.2M
                        auto *thunk = makeHeap<HeapThunk>(name_, self, offset, arg.expr);
2392
25.2M
                        thunk->upValues = capture(arg.expr->freeVariables);
2393
                        // While making the thunks, keep them in a frame to avoid premature garbage
2394
                        // collection.
2395
25.2M
                        f.thunks.push_back(thunk);
2396
25.2M
                        if (args.find(name) != args.end()) {
2397
0
                            std::stringstream ss;
2398
0
                            ss << "binding parameter a second time: " << encode_utf8(name->name);
2399
0
                            throw makeError(ast.location, ss.str());
2400
0
                        }
2401
25.2M
                        args[name] = thunk;
2402
25.2M
                        if (params_needed.find(name) == params_needed.end()) {
2403
0
                            std::stringstream ss;
2404
0
                            ss << "function has no parameter " << encode_utf8(name->name);
2405
0
                            throw makeError(ast.location, ss.str());
2406
0
                        }
2407
25.2M
                    }
2408
2409
                    // For any func params for which there was no arg, create a thunk for those and
2410
                    // bind the default argument.  Allow default thunks to see other params.  If no
2411
                    // default argument than raise an error.
2412
2413
                    // Raise errors for unbound params, create thunks (but don't fill in upvalues).
2414
                    // This is a subset of f.thunks, so will not get garbage collected.
2415
14.9M
                    std::vector<HeapThunk *> def_arg_thunks;
2416
25.2M
                    for (const auto &param : func->params) {
2417
25.2M
                        if (args.find(param.id) != args.end())
2418
25.2M
                            continue;
2419
131
                        if (param.def == nullptr) {
2420
16
                            std::stringstream ss;
2421
16
                            ss << "function parameter " << encode_utf8(param.id->name)
2422
16
                               << " not bound in call.";
2423
16
                            throw makeError(ast.location, ss.str());
2424
16
                        }
2425
2426
                        // Special case for builtin functions -- leave identifier blank for
2427
                        // them in the thunk.  This removes the thunk frame from the stacktrace.
2428
115
                        const Identifier *name_ = func->isBuiltin() ? nullptr : param.id;
2429
115
                        auto *thunk =
2430
115
                            makeHeap<HeapThunk>(name_, func->self, func->offset, param.def);
2431
115
                        f.thunks.push_back(thunk);
2432
115
                        def_arg_thunks.push_back(thunk);
2433
115
                        args[param.id] = thunk;
2434
115
                    }
2435
2436
14.9M
                    BindingFrame up_values = func->upValues;
2437
14.9M
                    up_values.insert(args.begin(), args.end());
2438
2439
                    // Fill in upvalues
2440
14.9M
                    for (HeapThunk *thunk : def_arg_thunks) {
2441
88
                        thunk->upValues = up_values;
2442
88
                    }
2443
2444
                    // Cache these, because pop will invalidate them.
2445
14.9M
                    std::vector<HeapThunk *> thunks_copy = f.thunks;
2446
2447
14.9M
                    const AST *f_ast = f.ast;
2448
14.9M
                    stack.pop();
2449
2450
14.9M
                    if (func->isBuiltin()) {
2451
                        // Built-in function.
2452
                        // Give nullptr for self because no one looking at this frame will
2453
                        // attempt to bind to self (it's native code).
2454
11.6M
                        stack.newFrame(FRAME_BUILTIN_FORCE_THUNKS, f_ast);
2455
11.6M
                        assert(func->upValues.empty());
2456
11.6M
                        stack.top().bindings = up_values;
2457
11.6M
                        stack.top().thunks = thunks_copy;
2458
11.6M
                        stack.top().val = scratch;
2459
11.6M
                        goto replaceframe;
2460
11.6M
                    } else {
2461
                        // User defined function.
2462
3.29M
                        stack.newCall(ast.location, func, func->self, func->offset, up_values);
2463
3.29M
                        if (ast.tailstrict) {
2464
2.24M
                            stack.top().tailCall = true;
2465
2.24M
                            if (thunks_copy.size() == 0) {
2466
                                // No need to force thunks, proceed straight to body.
2467
0
                                ast_ = func->body;
2468
0
                                goto recurse;
2469
2.24M
                            } else {
2470
                                // The check for args.size() > 0
2471
2.24M
                                stack.top().thunks = thunks_copy;
2472
2.24M
                                stack.top().val = scratch;
2473
2.24M
                                goto replaceframe;
2474
2.24M
                            }
2475
2.24M
                        } else {
2476
1.05M
                            ast_ = func->body;
2477
1.05M
                            goto recurse;
2478
1.05M
                        }
2479
3.29M
                    }
2480
14.9M
                } break;
2481
2482
8.17M
                case FRAME_BINARY_LEFT: {
2483
8.17M
                    const auto &ast = *static_cast<const Binary *>(f.ast);
2484
8.17M
                    const Value &lhs = scratch;
2485
8.17M
                    if (lhs.t == Value::BOOLEAN) {
2486
                        // Handle short-cut semantics
2487
275k
                        switch (ast.op) {
2488
88.6k
                            case BOP_AND: {
2489
88.6k
                                if (!lhs.v.b) {
2490
10.4k
                                    scratch = makeBoolean(false);
2491
10.4k
                                    goto popframe;
2492
10.4k
                                }
2493
88.6k
                            } break;
2494
2495
183k
                            case BOP_OR: {
2496
183k
                                if (lhs.v.b) {
2497
3.98k
                                    scratch = makeBoolean(true);
2498
3.98k
                                    goto popframe;
2499
3.98k
                                }
2500
183k
                            } break;
2501
2502
179k
                            default:;
2503
275k
                        }
2504
275k
                    }
2505
8.15M
                    stack.top().kind = FRAME_BINARY_RIGHT;
2506
8.15M
                    stack.top().val = lhs;
2507
8.15M
                    ast_ = ast.right;
2508
8.15M
                    goto recurse;
2509
8.17M
                } break;
2510
2511
8.14M
                case FRAME_BINARY_RIGHT: {
2512
8.14M
                    stack.top().val2 = scratch;
2513
8.14M
                    stack.top().kind = FRAME_BINARY_OP;
2514
8.14M
                }
2515
8.14M
                [[fallthrough]];
2516
8.14M
                case FRAME_BINARY_OP: {
2517
8.14M
                    const auto &ast = *static_cast<const Binary *>(f.ast);
2518
8.14M
                    const Value &lhs = stack.top().val;
2519
8.14M
                    const Value &rhs = stack.top().val2;
2520
2521
                    // Handle cases where the LHS and RHS are not the same type.
2522
8.14M
                    if (lhs.t == Value::STRING || rhs.t == Value::STRING) {
2523
2.19M
                        if (ast.op == BOP_PLUS) {
2524
                            // Handle co-ercions for string processing.
2525
2.03M
                            stack.top().kind = FRAME_STRING_CONCAT;
2526
2.03M
                            stack.top().val2 = rhs;
2527
2.03M
                            goto replaceframe;
2528
2.03M
                        }
2529
2.19M
                    }
2530
6.11M
                    switch (ast.op) {
2531
                        // Equality can be used when the types don't match.
2532
0
                        case BOP_MANIFEST_EQUAL:
2533
0
                            std::cerr << "INTERNAL ERROR: Equals not desugared" << std::endl;
2534
0
                            abort();
2535
2536
                        // Equality can be used when the types don't match.
2537
0
                        case BOP_MANIFEST_UNEQUAL:
2538
0
                            std::cerr << "INTERNAL ERROR: Notequals not desugared" << std::endl;
2539
0
                            abort();
2540
2541
                        // e in e
2542
8.39k
                        case BOP_IN: {
2543
8.39k
                            if (lhs.t != Value::STRING) {
2544
6
                                throw makeError(ast.location,
2545
6
                                                "the left hand side of the 'in' operator should be "
2546
6
                                                "a string,  got " +
2547
6
                                                    type_str(lhs));
2548
6
                            }
2549
8.39k
                            auto *field = static_cast<HeapString *>(lhs.v.h);
2550
8.39k
                            switch (rhs.t) {
2551
8.38k
                                case Value::OBJECT: {
2552
8.38k
                                    auto *obj = static_cast<HeapObject *>(rhs.v.h);
2553
8.38k
                                    auto *fid = alloc->makeIdentifier(field->value);
2554
8.38k
                                    unsigned unused_found_at = 0;
2555
8.38k
                                    bool in = findObject(fid, obj, 0, unused_found_at);
2556
8.38k
                                    scratch = makeBoolean(in);
2557
8.38k
                                } break;
2558
2559
4
                                default:
2560
4
                                    throw makeError(
2561
4
                                        ast.location,
2562
4
                                        "the right hand side of the 'in' operator should be"
2563
4
                                        " an object, got " +
2564
4
                                            type_str(rhs));
2565
8.39k
                            }
2566
8.38k
                            goto popframe;
2567
8.39k
                        }
2568
2569
6.10M
                        default:;
2570
6.11M
                    }
2571
                    // Everything else requires matching types.
2572
6.10M
                    if (lhs.t != rhs.t) {
2573
185
                        throw makeError(ast.location,
2574
185
                                        "binary operator " + bop_string(ast.op) +
2575
185
                                            " requires "
2576
185
                                            "matching types, got " +
2577
185
                                            type_str(lhs) + " and " + type_str(rhs) + ".");
2578
185
                    }
2579
6.10M
                    switch (lhs.t) {
2580
1.40M
                        case Value::ARRAY:
2581
1.40M
                            if (ast.op == BOP_PLUS) {
2582
1.38M
                                auto *arr_l = static_cast<HeapArray *>(lhs.v.h);
2583
1.38M
                                auto *arr_r = static_cast<HeapArray *>(rhs.v.h);
2584
1.38M
                                std::vector<HeapThunk *> elements;
2585
1.38M
                                for (auto *el : arr_l->elements)
2586
84.5M
                                    elements.push_back(el);
2587
1.38M
                                for (auto *el : arr_r->elements)
2588
2.71M
                                    elements.push_back(el);
2589
1.38M
                                scratch = makeArray(elements);
2590
1.38M
                            } else if (ast.op == BOP_LESS || ast.op == BOP_LESS_EQ || ast.op == BOP_GREATER || ast.op == BOP_GREATER_EQ) {
2591
28.6k
                                HeapThunk *func;
2592
28.6k
                                switch(ast.op) {
2593
9.90k
                                case BOP_LESS:
2594
9.90k
                                    func = sourceVals["__array_less"];
2595
9.90k
                                    break;
2596
8.40k
                                case BOP_LESS_EQ:
2597
8.40k
                                    func = sourceVals["__array_less_or_equal"];
2598
8.40k
                                    break;
2599
3.28k
                                case BOP_GREATER:
2600
3.28k
                                    func = sourceVals["__array_greater"];
2601
3.28k
                                    break;
2602
7.03k
                                case BOP_GREATER_EQ:
2603
7.03k
                                    func = sourceVals["__array_greater_or_equal"];
2604
7.03k
                                    break;
2605
0
                                default:
2606
0
                                    JSONNET_UNREACHABLE();
2607
28.6k
                                }
2608
28.6k
                                if (!func->filled) {
2609
300
                                    stack.newCall(ast.location, func, func->self, func->offset, func->upValues);
2610
300
                                    ast_ = func->body;
2611
300
                                    goto recurse;
2612
300
                                }
2613
28.3k
                                auto *lhs_th = makeHeap<HeapThunk>(idInternal, f.self, f.offset, ast.left);
2614
28.3k
                                lhs_th->fill(lhs);
2615
28.3k
                                f.thunks.push_back(lhs_th);
2616
28.3k
                                auto *rhs_th = makeHeap<HeapThunk>(idInternal, f.self, f.offset, ast.right);
2617
28.3k
                                rhs_th->fill(rhs);
2618
28.3k
                                f.thunks.push_back(rhs_th);
2619
28.3k
                                const AST *orig_ast = ast_;
2620
28.3k
                                stack.pop();
2621
28.3k
                                ast_ = callSourceVal(orig_ast, func, {lhs_th, rhs_th});
2622
28.3k
                                goto recurse;
2623
28.6k
                            } else {
2624
12
                                throw makeError(ast.location,
2625
12
                                                "binary operator " + bop_string(ast.op) +
2626
12
                                                    " does not operate on arrays.");
2627
12
                            }
2628
1.38M
                            break;
2629
2630
1.38M
                        case Value::BOOLEAN:
2631
258k
                            switch (ast.op) {
2632
78.1k
                                case BOP_AND: scratch = makeBoolean(lhs.v.b && rhs.v.b); break;
2633
2634
179k
                                case BOP_OR: scratch = makeBoolean(lhs.v.b || rhs.v.b); break;
2635
2636
45
                                default:
2637
45
                                    throw makeError(ast.location,
2638
45
                                                    "binary operator " + bop_string(ast.op) +
2639
45
                                                        " does not operate on booleans.");
2640
258k
                            }
2641
258k
                            break;
2642
2643
1.59M
                        case Value::NUMBER:
2644
1.59M
                            switch (ast.op) {
2645
436k
                                case BOP_PLUS:
2646
436k
                                    scratch = makeNumberCheck(ast.location, lhs.v.d + rhs.v.d);
2647
436k
                                    break;
2648
2649
124k
                                case BOP_MINUS:
2650
124k
                                    scratch = makeNumberCheck(ast.location, lhs.v.d - rhs.v.d);
2651
124k
                                    break;
2652
2653
6.09k
                                case BOP_MULT:
2654
6.09k
                                    scratch = makeNumberCheck(ast.location, lhs.v.d * rhs.v.d);
2655
6.09k
                                    break;
2656
2657
62.4k
                                case BOP_DIV:
2658
62.4k
                                    if (rhs.v.d == 0)
2659
7
                                        throw makeError(ast.location, "division by zero.");
2660
62.4k
                                    scratch = makeNumberCheck(ast.location, lhs.v.d / rhs.v.d);
2661
62.4k
                                    break;
2662
2663
                                    // No need to check doubles made from longs
2664
2665
1.39k
                                case BOP_SHIFT_L: {
2666
1.39k
                                    if (rhs.v.d < 0)
2667
2
                                        throw makeError(ast.location, "shift by negative exponent.");
2668
2669
1.39k
                                    int64_t long_l = safeDoubleToInt64(lhs.v.d, ast.location);
2670
1.39k
                                    int64_t long_r = safeDoubleToInt64(rhs.v.d, ast.location);
2671
1.39k
                                    long_r = long_r % 64;
2672
2673
                                    // Additional safety check for left shifts to prevent undefined behavior.
2674
                                    // Left-shift that would move the highest set bit into the sign bit position is undefined.
2675
1.39k
                                    if (long_r >= 1 && abs(long_l) >= (1LL << (63 - long_r))) {
2676
62
                                        throw makeError(ast.location,
2677
62
                                            "numeric value outside safe integer range for bitwise operation.");
2678
62
                                    }
2679
2680
                                    // Left-shift of a negative number is undefined until C++20.
2681
                                    // Perform the shift on unsigned int to avoid that.
2682
1.32k
                                    scratch = makeNumber(static_cast<int64_t>(static_cast<uint64_t>(long_l) << long_r));
2683
1.32k
                                } break;
2684
2685
87
                                case BOP_SHIFT_R: {
2686
87
                                    if (rhs.v.d < 0)
2687
2
                                        throw makeError(ast.location, "shift by negative exponent.");
2688
2689
85
                                    int64_t long_l = safeDoubleToInt64(lhs.v.d, ast.location);
2690
85
                                    int64_t long_r = safeDoubleToInt64(rhs.v.d, ast.location);
2691
85
                                    long_r = long_r % 64;
2692
85
                                    scratch = makeNumber(long_l >> long_r);
2693
85
                                } break;
2694
2695
26.9k
                                case BOP_BITWISE_AND: {
2696
26.9k
                                    int64_t long_l = safeDoubleToInt64(lhs.v.d, ast.location);
2697
26.9k
                                    int64_t long_r = safeDoubleToInt64(rhs.v.d, ast.location);
2698
26.9k
                                    scratch = makeNumber(long_l & long_r);
2699
26.9k
                                } break;
2700
2701
13.6k
                                case BOP_BITWISE_XOR: {
2702
13.6k
                                    int64_t long_l = safeDoubleToInt64(lhs.v.d, ast.location);
2703
13.6k
                                    int64_t long_r = safeDoubleToInt64(rhs.v.d, ast.location);
2704
13.6k
                                    scratch = makeNumber(long_l ^ long_r);
2705
13.6k
                                } break;
2706
2707
7.03k
                                case BOP_BITWISE_OR: {
2708
7.03k
                                    int64_t long_l = safeDoubleToInt64(lhs.v.d, ast.location);
2709
7.03k
                                    int64_t long_r = safeDoubleToInt64(rhs.v.d, ast.location);
2710
7.03k
                                    scratch = makeNumber(long_l | long_r);
2711
7.03k
                                } break;
2712
2713
45.2k
                                case BOP_LESS_EQ: scratch = makeBoolean(lhs.v.d <= rhs.v.d); break;
2714
2715
681k
                                case BOP_GREATER_EQ:
2716
681k
                                    scratch = makeBoolean(lhs.v.d >= rhs.v.d);
2717
681k
                                    break;
2718
2719
161k
                                case BOP_LESS: scratch = makeBoolean(lhs.v.d < rhs.v.d); break;
2720
2721
26.6k
                                case BOP_GREATER: scratch = makeBoolean(lhs.v.d > rhs.v.d); break;
2722
2723
8
                                default:
2724
8
                                    throw makeError(ast.location,
2725
8
                                                    "binary operator " + bop_string(ast.op) +
2726
8
                                                        " does not operate on numbers.");
2727
1.59M
                            }
2728
1.59M
                            break;
2729
2730
1.59M
                        case Value::FUNCTION:
2731
2
                            throw makeError(ast.location,
2732
2
                                            "binary operator " + bop_string(ast.op) +
2733
2
                                                " does not operate on functions.");
2734
2735
3
                        case Value::NULL_TYPE:
2736
3
                            throw makeError(ast.location,
2737
3
                                            "binary operator " + bop_string(ast.op) +
2738
3
                                                " does not operate on null.");
2739
2740
2.69M
                        case Value::OBJECT: {
2741
2.69M
                            if (ast.op != BOP_PLUS) {
2742
16
                                throw makeError(ast.location,
2743
16
                                                "binary operator " + bop_string(ast.op) +
2744
16
                                                    " does not operate on objects.");
2745
16
                            }
2746
2.69M
                            auto *lhs_obj = static_cast<HeapObject *>(lhs.v.h);
2747
2.69M
                            auto *rhs_obj = static_cast<HeapObject *>(rhs.v.h);
2748
2.69M
                            scratch = makeObject<HeapExtendedObject>(lhs_obj, rhs_obj);
2749
2.69M
                        } break;
2750
2751
153k
                        case Value::STRING: {
2752
153k
                            const UString &lhs_str = static_cast<HeapString *>(lhs.v.h)->value;
2753
153k
                            const UString &rhs_str = static_cast<HeapString *>(rhs.v.h)->value;
2754
153k
                            switch (ast.op) {
2755
0
                                case BOP_PLUS: scratch = makeString(lhs_str + rhs_str); break;
2756
2757
23.1k
                                case BOP_LESS_EQ: scratch = makeBoolean(lhs_str <= rhs_str); break;
2758
2759
109k
                                case BOP_GREATER_EQ:
2760
109k
                                    scratch = makeBoolean(lhs_str >= rhs_str);
2761
109k
                                    break;
2762
2763
10.5k
                                case BOP_LESS: scratch = makeBoolean(lhs_str < rhs_str); break;
2764
2765
10.6k
                                case BOP_GREATER: scratch = makeBoolean(lhs_str > rhs_str); break;
2766
2767
46
                                default:
2768
46
                                    throw makeError(ast.location,
2769
46
                                                    "binary operator " + bop_string(ast.op) +
2770
46
                                                        " does not operate on strings.");
2771
153k
                            }
2772
153k
                        } break;
2773
6.10M
                    }
2774
6.10M
                } break;
2775
2776
6.07M
                case FRAME_BUILTIN_FILTER: {
2777
0
                    const auto &ast = *static_cast<const Apply *>(f.ast);
2778
0
                    auto *func = static_cast<HeapClosure *>(f.val.v.h);
2779
0
                    auto *arr = static_cast<HeapArray *>(f.val2.v.h);
2780
0
                    if (scratch.t != Value::BOOLEAN) {
2781
0
                        throw makeError(
2782
0
                            ast.location,
2783
0
                            "filter function must return boolean, got: " + type_str(scratch));
2784
0
                    }
2785
0
                    if (scratch.v.b)
2786
0
                        f.thunks.push_back(arr->elements[f.elementId]);
2787
0
                    f.elementId++;
2788
                    // Iterate through arr, calling the function on each.
2789
0
                    if (f.elementId == arr->elements.size()) {
2790
0
                        scratch = makeArray(f.thunks);
2791
0
                    } else {
2792
0
                        auto *thunk = arr->elements[f.elementId];
2793
0
                        BindingFrame bindings = func->upValues;
2794
0
                        bindings[func->params[0].id] = thunk;
2795
0
                        stack.newCall(ast.location, func, func->self, func->offset, bindings);
2796
0
                        ast_ = func->body;
2797
0
                        goto recurse;
2798
0
                    }
2799
0
                } break;
2800
2801
30.0M
                case FRAME_BUILTIN_FORCE_THUNKS: {
2802
30.0M
                    const auto &location = f.location;
2803
30.0M
                    auto *func = static_cast<HeapClosure *>(f.val.v.h);
2804
30.0M
                    if (f.elementId == f.thunks.size()) {
2805
                        // All thunks forced, now the builtin implementations.
2806
11.6M
                        const std::string &builtin_name = func->builtinName;
2807
11.6M
                        std::vector<Value> args;
2808
18.3M
                        for (const auto &p : func->params) {
2809
18.3M
                            const auto it = f.bindings.find(p.id);
2810
18.3M
                            if (it != f.bindings.end()) {
2811
18.3M
                                assert(it->second->filled);
2812
18.3M
                                args.push_back(it->second->content);
2813
18.3M
                            } else {
2814
0
                                std::stringstream ss;
2815
0
                                ss << "function parameter " << encode_utf8(p.id->name)
2816
0
                                    << " not bound in call.";
2817
0
                                throw makeError(location, ss.str());
2818
0
                            }
2819
18.3M
                        }
2820
2821
11.6M
                        BuiltinMap::const_iterator bit = builtins.find(builtin_name);
2822
11.6M
                        if (bit != builtins.end()) {
2823
11.6M
                            const AST *new_ast = (this->*bit->second)(location, args);
2824
11.6M
                            if (new_ast != nullptr) {
2825
0
                                ast_ = new_ast;
2826
0
                                goto recurse;
2827
0
                            }
2828
11.6M
                            break;
2829
11.6M
                        }
2830
0
                        VmNativeCallbackMap::const_iterator nit =
2831
0
                            nativeCallbacks.find(builtin_name);
2832
                        // TODO(dcunnin): Support arrays.
2833
                        // TODO(dcunnin): Support objects.
2834
0
                        std::vector<JsonnetJsonValue> args2;
2835
0
                        for (const Value &arg : args) {
2836
0
                            switch (arg.t) {
2837
0
                                case Value::STRING:
2838
0
                                    args2.emplace_back(
2839
0
                                        JsonnetJsonValue::STRING,
2840
0
                                        encode_utf8(static_cast<HeapString *>(arg.v.h)->value),
2841
0
                                        0);
2842
0
                                    break;
2843
2844
0
                                case Value::BOOLEAN:
2845
0
                                    args2.emplace_back(
2846
0
                                        JsonnetJsonValue::BOOL, "", arg.v.b ? 1.0 : 0.0);
2847
0
                                    break;
2848
2849
0
                                case Value::NUMBER:
2850
0
                                    args2.emplace_back(JsonnetJsonValue::NUMBER, "", arg.v.d);
2851
0
                                    break;
2852
2853
0
                                case Value::NULL_TYPE:
2854
0
                                    args2.emplace_back(JsonnetJsonValue::NULL_KIND, "", 0);
2855
0
                                    break;
2856
2857
0
                                default:
2858
0
                                    throw makeError(location,
2859
0
                                                    "native extensions can only take primitives.");
2860
0
                            }
2861
0
                        }
2862
0
                        std::vector<const JsonnetJsonValue *> args3;
2863
0
                        for (size_t i = 0; i < args2.size(); ++i) {
2864
0
                            args3.push_back(&args2[i]);
2865
0
                        }
2866
0
                        if (nit == nativeCallbacks.end()) {
2867
0
                            throw makeError(location,
2868
0
                                            "unrecognized builtin name: " + builtin_name);
2869
0
                        }
2870
0
                        const VmNativeCallback &cb = nit->second;
2871
2872
0
                        int succ;
2873
0
                        std::unique_ptr<JsonnetJsonValue> r(cb.cb(cb.ctx, args3.data(), &succ));
2874
2875
0
                        if (succ) {
2876
0
                            bool unused;
2877
0
                            jsonToHeap(r, unused, scratch);
2878
0
                        } else {
2879
0
                            if (r->kind != JsonnetJsonValue::STRING) {
2880
0
                                throw makeError(
2881
0
                                    location,
2882
0
                                    "native extension returned an error that was not a string.");
2883
0
                            }
2884
0
                            std::string rs = r->string;
2885
0
                            throw makeError(location, rs);
2886
0
                        }
2887
2888
18.4M
                    } else {
2889
                        // Not all arguments forced yet.
2890
18.4M
                        HeapThunk *th = f.thunks[f.elementId++];
2891
18.4M
                        if (!th->filled) {
2892
18.4M
                            stack.newCall(location, th, th->self, th->offset, th->upValues);
2893
18.4M
                            ast_ = th->body;
2894
18.4M
                            goto recurse;
2895
18.4M
                        } else {
2896
                            // Otherwise loop back around to force the next thunk.
2897
0
                            goto replaceframe;
2898
0
                        }
2899
18.4M
                    }
2900
30.0M
                } break;
2901
2902
61.2M
                case FRAME_CALL: {
2903
61.2M
                    if (auto *thunk = dynamic_cast<HeapThunk *>(f.context)) {
2904
                        // If we called a thunk, cache result.
2905
32.7M
                        thunk->fill(scratch);
2906
32.7M
                    } else if (auto *closure = dynamic_cast<HeapClosure *>(f.context)) {
2907
9.92M
                        if (f.elementId < f.thunks.size()) {
2908
                            // If tailstrict, force thunks
2909
4.97M
                            HeapThunk *th = f.thunks[f.elementId++];
2910
4.97M
                            if (!th->filled) {
2911
4.97M
                                stack.newCall(f.location, th, th->self, th->offset, th->upValues);
2912
4.97M
                                ast_ = th->body;
2913
4.97M
                                goto recurse;
2914
4.97M
                            }
2915
4.97M
                        } else if (f.thunks.size() == 0) {
2916
                            // Body has now been executed
2917
2.72M
                        } else {
2918
                            // Execute the body
2919
2.22M
                            f.thunks.clear();
2920
2.22M
                            f.elementId = 0;
2921
2.22M
                            ast_ = closure->body;
2922
2.22M
                            goto recurse;
2923
2.22M
                        }
2924
9.92M
                    }
2925
                    // Result of call is in scratch, just pop.
2926
61.2M
                } break;
2927
2928
54.0M
                case FRAME_ERROR: {
2929
4.20k
                    const auto &ast = *static_cast<const Error *>(f.ast);
2930
4.20k
                    UString msg;
2931
4.20k
                    if (scratch.t == Value::STRING) {
2932
611
                        msg = static_cast<HeapString *>(scratch.v.h)->value;
2933
3.59k
                    } else {
2934
3.59k
                        msg = toString(ast.location);
2935
3.59k
                    }
2936
4.20k
                    throw makeError(ast.location, encode_utf8(msg));
2937
61.2M
                } break;
2938
2939
11.3M
                case FRAME_IF: {
2940
11.3M
                    const auto &ast = *static_cast<const Conditional *>(f.ast);
2941
11.3M
                    if (scratch.t != Value::BOOLEAN) {
2942
104
                        throw makeError(
2943
104
                            ast.location,
2944
104
                            "condition must be boolean, got " + type_str(scratch) + ".");
2945
104
                    }
2946
11.3M
                    ast_ = scratch.v.b ? ast.branchTrue : ast.branchFalse;
2947
11.3M
                    stack.pop();
2948
11.3M
                    goto recurse;
2949
11.3M
                } break;
2950
2951
3.70M
                case FRAME_SUPER_INDEX: {
2952
3.70M
                    const auto &ast = *static_cast<const SuperIndex *>(f.ast);
2953
3.70M
                    HeapObject *self;
2954
3.70M
                    unsigned offset;
2955
3.70M
                    stack.getSelfBinding(self, offset);
2956
3.70M
                    offset++;
2957
3.70M
                    if (offset >= countLeaves(self)) {
2958
2
                        throw makeError(ast.location,
2959
2
                                        "attempt to use super when there is no super class.");
2960
2
                    }
2961
3.70M
                    if (scratch.t != Value::STRING) {
2962
0
                        throw makeError(
2963
0
                            ast.location,
2964
0
                            "super index must be string, got " + type_str(scratch) + ".");
2965
0
                    }
2966
2967
3.70M
                    const UString &index_name = static_cast<HeapString *>(scratch.v.h)->value;
2968
3.70M
                    auto *fid = alloc->makeIdentifier(index_name);
2969
3.70M
                    stack.pop();
2970
3.70M
                    ast_ = objectIndex(ast.location, self, fid, offset);
2971
3.70M
                    goto recurse;
2972
3.70M
                } break;
2973
2974
4.13M
                case FRAME_IN_SUPER_ELEMENT: {
2975
4.13M
                    const auto &ast = *static_cast<const InSuper *>(f.ast);
2976
4.13M
                    HeapObject *self;
2977
4.13M
                    unsigned offset;
2978
4.13M
                    stack.getSelfBinding(self, offset);
2979
4.13M
                    offset++;
2980
4.13M
                    if (scratch.t != Value::STRING) {
2981
1
                        throw makeError(ast.location,
2982
1
                                        "left hand side of e in super must be string, got " +
2983
1
                                            type_str(scratch) + ".");
2984
1
                    }
2985
4.13M
                    if (offset >= countLeaves(self)) {
2986
                        // There is no super object.
2987
209k
                        scratch = makeBoolean(false);
2988
3.92M
                    } else {
2989
3.92M
                        const UString &element_name = static_cast<HeapString *>(scratch.v.h)->value;
2990
3.92M
                        auto *fid = alloc->makeIdentifier(element_name);
2991
3.92M
                        unsigned unused_found_at = 0;
2992
3.92M
                        bool in = findObject(fid, self, offset, unused_found_at);
2993
3.92M
                        scratch = makeBoolean(in);
2994
3.92M
                    }
2995
4.13M
                } break;
2996
2997
15.6M
                case FRAME_INDEX_INDEX: {
2998
15.6M
                    const auto &ast = *static_cast<const Index *>(f.ast);
2999
15.6M
                    const Value &target = f.val;
3000
15.6M
                    if (target.t == Value::ARRAY) {
3001
337k
                        const auto *array = static_cast<HeapArray *>(target.v.h);
3002
337k
                        if (scratch.t == Value::STRING) {
3003
114
                            const UString &str = static_cast<HeapString *>(scratch.v.h)->value;
3004
114
                            throw makeError(
3005
114
                                ast.location,
3006
114
                                "attempted index an array with string \""
3007
114
                                + encode_utf8(jsonnet_string_escape(str, false)) + "\".");
3008
114
                        }
3009
337k
                        if (scratch.t != Value::NUMBER) {
3010
21
                            throw makeError(
3011
21
                                ast.location,
3012
21
                                "array index must be number, got " + type_str(scratch) + ".");
3013
21
                        }
3014
337k
                        double index = ::floor(scratch.v.d);
3015
337k
                        long sz = array->elements.size();
3016
337k
                        if (index < 0 || index >= sz) {
3017
16
                            std::stringstream ss;
3018
16
                            ss << "array bounds error: " << index << " not within [0, " << sz
3019
16
                               << ")";
3020
16
                            throw makeError(ast.location, ss.str());
3021
16
                        }
3022
337k
                        if (scratch.v.d != index) {
3023
13
                            std::stringstream ss;
3024
13
                            ss << "array index was not integer: " << scratch.v.d;
3025
13
                            throw makeError(ast.location, ss.str());
3026
13
                        }
3027
                        // index < sz <= SIZE_T_MAX
3028
337k
                        auto *thunk = array->elements[size_t(index)];
3029
337k
                        if (thunk->filled) {
3030
148k
                            scratch = thunk->content;
3031
189k
                        } else {
3032
189k
                            stack.pop();
3033
189k
                            stack.newCall(
3034
189k
                                ast.location, thunk, thunk->self, thunk->offset, thunk->upValues);
3035
189k
                            ast_ = thunk->body;
3036
189k
                            goto recurse;
3037
189k
                        }
3038
15.3M
                    } else if (target.t == Value::OBJECT) {
3039
15.0M
                        auto *obj = static_cast<HeapObject *>(target.v.h);
3040
15.0M
                        assert(obj != nullptr);
3041
15.0M
                        if (scratch.t != Value::STRING) {
3042
14
                            throw makeError(
3043
14
                                ast.location,
3044
14
                                "object index must be string, got " + type_str(scratch) + ".");
3045
14
                        }
3046
15.0M
                        const UString &index_name = static_cast<HeapString *>(scratch.v.h)->value;
3047
15.0M
                        auto *fid = alloc->makeIdentifier(index_name);
3048
15.0M
                        stack.pop();
3049
15.0M
                        ast_ = objectIndex(ast.location, obj, fid, 0);
3050
15.0M
                        goto recurse;
3051
15.0M
                    } else if (target.t == Value::STRING) {
3052
301k
                        auto *obj = static_cast<HeapString *>(target.v.h);
3053
301k
                        assert(obj != nullptr);
3054
301k
                        if (scratch.t != Value::NUMBER) {
3055
6
                            throw makeError(
3056
6
                                ast.location,
3057
6
                                "string index must be a number, got " + type_str(scratch) + ".");
3058
6
                        }
3059
301k
                        long sz = obj->value.length();
3060
301k
                        long i = (long)scratch.v.d;
3061
301k
                        if (i < 0 || i >= sz) {
3062
189
                            std::stringstream ss;
3063
189
                            ss << "string bounds error: " << i << " not within [0, " << sz << ")";
3064
189
                            throw makeError(ast.location, ss.str());
3065
189
                        }
3066
301k
                        char32_t ch[] = {obj->value[i], U'\0'};
3067
301k
                        scratch = makeString(ch);
3068
301k
                    } else {
3069
0
                        std::cerr << "INTERNAL ERROR: not object / array / string." << std::endl;
3070
0
                        abort();
3071
0
                    }
3072
15.6M
                } break;
3073
3074
15.6M
                case FRAME_INDEX_TARGET: {
3075
15.6M
                    const auto &ast = *static_cast<const Index *>(f.ast);
3076
15.6M
                    if (scratch.t != Value::ARRAY && scratch.t != Value::OBJECT &&
3077
301k
                        scratch.t != Value::STRING) {
3078
26
                        throw makeError(ast.location,
3079
26
                                        "can only index objects, strings, and arrays, got " +
3080
26
                                            type_str(scratch) + ".");
3081
26
                    }
3082
15.6M
                    f.val = scratch;
3083
15.6M
                    f.kind = FRAME_INDEX_INDEX;
3084
15.6M
                    if (scratch.t == Value::OBJECT) {
3085
15.0M
                        auto *self = static_cast<HeapObject *>(scratch.v.h);
3086
15.0M
                        if (!stack.alreadyExecutingInvariants(self)) {
3087
14.9M
                            stack.newFrame(FRAME_INVARIANTS, ast.location);
3088
14.9M
                            Frame &f2 = stack.top();
3089
14.9M
                            f2.self = self;
3090
14.9M
                            unsigned counter = 0;
3091
14.9M
                            objectInvariants(self, self, counter, f2.thunks);
3092
14.9M
                            if (f2.thunks.size() > 0) {
3093
1.85k
                                auto *thunk = f2.thunks[0];
3094
1.85k
                                f2.elementId = 1;
3095
1.85k
                                stack.newCall(ast.location,
3096
1.85k
                                              thunk,
3097
1.85k
                                              thunk->self,
3098
1.85k
                                              thunk->offset,
3099
1.85k
                                              thunk->upValues);
3100
1.85k
                                ast_ = thunk->body;
3101
1.85k
                                goto recurse;
3102
1.85k
                            }
3103
14.9M
                        }
3104
15.0M
                    }
3105
15.6M
                    ast_ = ast.index;
3106
15.6M
                    goto recurse;
3107
15.6M
                } break;
3108
3109
15.0M
                case FRAME_INVARIANTS: {
3110
15.0M
                    if (f.elementId >= f.thunks.size()) {
3111
14.9M
                        if (stack.size() == initial_stack_size + 1) {
3112
                            // Just pop, evaluate was invoked by runInvariants.
3113
18.1k
                            break;
3114
18.1k
                        }
3115
14.9M
                        stack.pop();
3116
14.9M
                        Frame &f2 = stack.top();
3117
14.9M
                        const auto &ast = *static_cast<const Index *>(f2.ast);
3118
14.9M
                        ast_ = ast.index;
3119
14.9M
                        goto recurse;
3120
14.9M
                    }
3121
31.6k
                    auto *thunk = f.thunks[f.elementId++];
3122
31.6k
                    stack.newCall(f.location, thunk, thunk->self, thunk->offset, thunk->upValues);
3123
31.6k
                    ast_ = thunk->body;
3124
31.6k
                    goto recurse;
3125
15.0M
                } break;
3126
3127
6.89M
                case FRAME_LOCAL: {
3128
                    // Result of execution is in scratch already.
3129
6.89M
                } break;
3130
3131
3.69M
                case FRAME_OBJECT: {
3132
3.69M
                    const auto &ast = *static_cast<const DesugaredObject *>(f.ast);
3133
3.69M
                    if (scratch.t != Value::NULL_TYPE) {
3134
3.69M
                        if (scratch.t != Value::STRING) {
3135
23
                            throw makeError(ast.location, "field name was not a string.");
3136
23
                        }
3137
3.69M
                        const auto &fname = static_cast<const HeapString *>(scratch.v.h)->value;
3138
3.69M
                        const Identifier *fid = alloc->makeIdentifier(fname);
3139
3.69M
                        if (f.objectFields.find(fid) != f.objectFields.end()) {
3140
3
                            std::string msg =
3141
3
                                "duplicate field name: \"" + encode_utf8(fname) + "\"";
3142
3
                            throw makeError(ast.location, msg);
3143
3
                        }
3144
3.69M
                        f.objectFields[fid].hide = f.fit->hide;
3145
3.69M
                        f.objectFields[fid].body = f.fit->body;
3146
3.69M
                    }
3147
3.69M
                    f.fit++;
3148
3.69M
                    if (f.fit != ast.fields.end()) {
3149
2.29M
                        ast_ = f.fit->name;
3150
2.29M
                        goto recurse;
3151
2.29M
                    } else {
3152
1.40M
                        auto env = capture(ast.freeVariables);
3153
1.40M
                        scratch = makeObject<HeapSimpleObject>(env, f.objectFields, ast.asserts);
3154
1.40M
                    }
3155
3.69M
                } break;
3156
3157
1.40M
                case FRAME_OBJECT_COMP_ARRAY: {
3158
0
                    const auto &ast = *static_cast<const ObjectComprehensionSimple *>(f.ast);
3159
0
                    const Value &arr_v = scratch;
3160
0
                    if (scratch.t != Value::ARRAY) {
3161
0
                        throw makeError(ast.location,
3162
0
                                        "object comprehension needs array, got " + type_str(arr_v));
3163
0
                    }
3164
0
                    const auto *arr = static_cast<const HeapArray *>(arr_v.v.h);
3165
0
                    if (arr->elements.size() == 0) {
3166
                        // Degenerate case.  Just create the object now.
3167
0
                        scratch = makeObject<HeapComprehensionObject>(
3168
0
                            BindingFrame{}, ast.value, ast.id, BindingFrame{});
3169
0
                    } else {
3170
0
                        f.kind = FRAME_OBJECT_COMP_ELEMENT;
3171
0
                        f.val = scratch;
3172
0
                        f.bindings[ast.id] = arr->elements[0];
3173
0
                        f.elementId = 0;
3174
0
                        ast_ = ast.field;
3175
0
                        goto recurse;
3176
0
                    }
3177
0
                } break;
3178
3179
0
                case FRAME_OBJECT_COMP_ELEMENT: {
3180
0
                    const auto &ast = *static_cast<const ObjectComprehensionSimple *>(f.ast);
3181
0
                    const auto *arr = static_cast<const HeapArray *>(f.val.v.h);
3182
0
                    if (scratch.t != Value::NULL_TYPE) {
3183
0
                        if (scratch.t != Value::STRING) {
3184
0
                            std::stringstream ss;
3185
0
                            ss << "field must be string, got: " << type_str(scratch);
3186
0
                            throw makeError(ast.location, ss.str());
3187
0
                        }
3188
0
                        const auto &fname = static_cast<const HeapString *>(scratch.v.h)->value;
3189
0
                        const Identifier *fid = alloc->makeIdentifier(fname);
3190
0
                        if (f.elements.find(fid) != f.elements.end()) {
3191
0
                            throw makeError(ast.location,
3192
0
                                            "duplicate field name: \"" + encode_utf8(fname) + "\"");
3193
0
                        }
3194
0
                        f.elements[fid] = arr->elements[f.elementId];
3195
0
                    }
3196
0
                    f.elementId++;
3197
3198
0
                    if (f.elementId == arr->elements.size()) {
3199
0
                        auto env = capture(ast.freeVariables);
3200
0
                        scratch =
3201
0
                            makeObject<HeapComprehensionObject>(env, ast.value, ast.id, f.elements);
3202
0
                    } else {
3203
0
                        f.bindings[ast.id] = arr->elements[f.elementId];
3204
0
                        ast_ = ast.field;
3205
0
                        goto recurse;
3206
0
                    }
3207
0
                } break;
3208
3209
2.03M
                case FRAME_STRING_CONCAT: {
3210
2.03M
                    const auto &ast = *static_cast<const Binary *>(f.ast);
3211
2.03M
                    const Value &lhs = stack.top().val;
3212
2.03M
                    UString output;
3213
2.03M
                    if (lhs.t == Value::STRING) {
3214
1.80M
                        output.append(static_cast<const HeapString *>(lhs.v.h)->value);
3215
1.80M
                    } else {
3216
226k
                        scratch = lhs;
3217
226k
                        output.append(toString(ast.left->location));
3218
226k
                    }
3219
2.03M
                    const Value &rhs = stack.top().val2;
3220
2.03M
                    if (rhs.t == Value::STRING) {
3221
1.75M
                        output.append(static_cast<const HeapString *>(rhs.v.h)->value);
3222
1.75M
                    } else {
3223
281k
                        scratch = rhs;
3224
281k
                        output.append(toString(ast.right->location));
3225
281k
                    }
3226
2.03M
                    scratch = makeString(output);
3227
2.03M
                } break;
3228
3229
2.79M
                case FRAME_UNARY: {
3230
2.79M
                    const auto &ast = *static_cast<const Unary *>(f.ast);
3231
2.79M
                    switch (scratch.t) {
3232
2.62M
                        case Value::BOOLEAN:
3233
2.62M
                            if (ast.op == UOP_NOT) {
3234
2.62M
                                scratch = makeBoolean(!scratch.v.b);
3235
2.62M
                            } else {
3236
3
                                throw makeError(ast.location,
3237
3
                                                "unary operator " + uop_string(ast.op) +
3238
3
                                                    " does not operate on booleans.");
3239
3
                            }
3240
2.62M
                            break;
3241
3242
2.62M
                        case Value::NUMBER:
3243
171k
                            switch (ast.op) {
3244
13.6k
                                case UOP_PLUS: break;
3245
3246
145k
                                case UOP_MINUS: scratch = makeNumber(-scratch.v.d); break;
3247
3248
12.0k
                                case UOP_BITWISE_NOT:
3249
12.0k
                                    scratch = makeNumber(~(long)(scratch.v.d));
3250
12.0k
                                    break;
3251
3252
29
                                default:
3253
29
                                    throw makeError(ast.location,
3254
29
                                                    "unary operator " + uop_string(ast.op) +
3255
29
                                                        " does not operate on numbers.");
3256
171k
                            }
3257
171k
                            break;
3258
3259
171k
                        default:
3260
174
                            throw makeError(ast.location,
3261
174
                                            "unary operator " + uop_string(ast.op) +
3262
174
                                                " does not operate on type " + type_str(scratch));
3263
2.79M
                    }
3264
2.79M
                } break;
3265
3266
2.79M
                case FRAME_BUILTIN_JOIN_STRINGS: {
3267
0
                    joinString(f.first, f.str, f.val, f.elementId, scratch);
3268
0
                    f.elementId++;
3269
0
                    auto *ast = joinStrings();
3270
0
                    if (ast != nullptr) {
3271
0
                        ast_ = ast;
3272
0
                        goto recurse;
3273
0
                    }
3274
0
                } break;
3275
3276
0
                case FRAME_BUILTIN_JOIN_ARRAYS: {
3277
0
                    joinArray(f.first, f.thunks, f.val, f.elementId, scratch);
3278
0
                    f.elementId++;
3279
0
                    auto *ast = joinArrays();
3280
0
                    if (ast != nullptr) {
3281
0
                        ast_ = ast;
3282
0
                        goto recurse;
3283
0
                    }
3284
0
                } break;
3285
3286
0
                 case FRAME_BUILTIN_DECODE_UTF8: {
3287
0
                    auto *ast = decodeUTF8();
3288
0
                    if (ast != nullptr) {
3289
0
                        ast_ = ast;
3290
0
                        goto recurse;
3291
0
                    }
3292
0
                } break;
3293
3294
0
                default:
3295
0
                    std::cerr << "INTERNAL ERROR: Unknown FrameKind:  " << f.kind << std::endl;
3296
0
                    std::abort();
3297
203M
            }
3298
3299
89.5M
        popframe:;
3300
3301
89.5M
            stack.pop();
3302
3303
105M
        replaceframe:;
3304
105M
        }
3305
107M
    }
3306
3307
    /** Manifest the scratch value by evaluating any remaining fields, and then convert to JSON.
3308
     *
3309
     * This can trigger a garbage collection cycle.  Be sure to stash any objects that aren't
3310
     * reachable via the stack or heap.
3311
     *
3312
     * \param multiline If true, will print objects and arrays in an indented fashion.
3313
     */
3314
    UString manifestJson(const LocationRange &loc, bool multiline, const UString &indent)
3315
10.2M
    {
3316
        // Printing fields means evaluating and binding them, which can trigger
3317
        // garbage collection.
3318
3319
10.2M
        UStringStream ss;
3320
10.2M
        switch (scratch.t) {
3321
412k
            case Value::ARRAY: {
3322
412k
                HeapArray *arr = static_cast<HeapArray *>(scratch.v.h);
3323
412k
                if (arr->elements.size() == 0) {
3324
103k
                    ss << U"[ ]";
3325
308k
                } else {
3326
308k
                    const char32_t *prefix = multiline ? U"[\n" : U"[";
3327
308k
                    UString indent2 = multiline ? indent + U"   " : indent;
3328
7.57M
                    for (auto *thunk : arr->elements) {
3329
7.57M
                        LocationRange tloc = thunk->body == nullptr ? loc : thunk->body->location;
3330
7.57M
                        if (thunk->filled) {
3331
0
                            stack.newCall(loc, thunk, nullptr, 0, BindingFrame{});
3332
                            // Keep arr alive when scratch is overwritten
3333
0
                            stack.top().val = scratch;
3334
0
                            scratch = thunk->content;
3335
7.57M
                        } else {
3336
7.57M
                            stack.newCall(loc, thunk, thunk->self, thunk->offset, thunk->upValues);
3337
                            // Keep arr alive when scratch is overwritten
3338
7.57M
                            stack.top().val = scratch;
3339
7.57M
                            evaluate(thunk->body, stack.size());
3340
7.57M
                        }
3341
7.57M
                        auto element = manifestJson(tloc, multiline, indent2);
3342
                        // Restore scratch
3343
7.57M
                        scratch = stack.top().val;
3344
7.57M
                        stack.pop();
3345
7.57M
                        ss << prefix << indent2 << element;
3346
7.57M
                        prefix = multiline ? U",\n" : U", ";
3347
7.57M
                    }
3348
308k
                    ss << (multiline ? U"\n" : U"") << indent << U"]";
3349
308k
                }
3350
412k
            } break;
3351
3352
137k
            case Value::BOOLEAN: ss << (scratch.v.b ? U"true" : U"false"); break;
3353
3354
8.39M
            case Value::NUMBER: ss << decode_utf8(jsonnet_unparse_number(scratch.v.d)); break;
3355
3356
26
            case Value::FUNCTION:
3357
26
                throw makeError(loc, "couldn't manifest function in JSON output.");
3358
3359
5.06k
            case Value::NULL_TYPE: ss << U"null"; break;
3360
3361
1.16M
            case Value::OBJECT: {
3362
1.16M
                auto *obj = static_cast<HeapObject *>(scratch.v.h);
3363
1.16M
                runInvariants(loc, obj);
3364
                // Using std::map has the useful side-effect of ordering the fields
3365
                // alphabetically.
3366
1.16M
                std::map<UString, const Identifier *> fields;
3367
1.56M
                for (const auto &f : objectFields(obj, true)) {
3368
1.56M
                    fields[f->name] = f;
3369
1.56M
                }
3370
1.16M
                if (fields.size() == 0) {
3371
322k
                    ss << U"{ }";
3372
843k
                } else {
3373
843k
                    UString indent2 = multiline ? indent + U"   " : indent;
3374
843k
                    const char32_t *prefix = multiline ? U"{\n" : U"{";
3375
1.45M
                    for (const auto &f : fields) {
3376
                        // pushes FRAME_CALL
3377
1.45M
                        const AST *body = objectIndex(loc, obj, f.second, 0);
3378
1.45M
                        stack.top().val = scratch;
3379
1.45M
                        evaluate(body, stack.size());
3380
1.45M
                        auto vstr = manifestJson(body->location, multiline, indent2);
3381
                        // Reset scratch so that the object we're manifesting doesn't
3382
                        // get GC'd.
3383
1.45M
                        scratch = stack.top().val;
3384
1.45M
                        stack.pop();
3385
1.45M
                        ss << prefix << indent2 << jsonnet_string_unparse(f.first, false) << U": "
3386
1.45M
                           << vstr;
3387
1.45M
                        prefix = multiline ? U",\n" : U", ";
3388
1.45M
                    }
3389
843k
                    ss << (multiline ? U"\n" : U"") << indent << U"}";
3390
843k
                }
3391
1.16M
            } break;
3392
3393
128k
            case Value::STRING: {
3394
128k
                const UString &str = static_cast<HeapString *>(scratch.v.h)->value;
3395
128k
                ss << jsonnet_string_unparse(str, false);
3396
128k
            } break;
3397
10.2M
        }
3398
9.90M
        return ss.str();
3399
10.2M
    }
3400
3401
    UString manifestString(const LocationRange &loc)
3402
0
    {
3403
0
        if (scratch.t != Value::STRING) {
3404
0
            std::stringstream ss;
3405
0
            ss << "expected string result, got: " << type_str(scratch.t);
3406
0
            throw makeError(loc, ss.str());
3407
0
        }
3408
0
        return static_cast<HeapString *>(scratch.v.h)->value;
3409
0
    }
3410
3411
    StrMap manifestMulti(bool string)
3412
1.79k
    {
3413
1.79k
        StrMap r;
3414
1.79k
        LocationRange loc("During manifestation");
3415
1.79k
        if (scratch.t != Value::OBJECT) {
3416
736
            std::stringstream ss;
3417
736
            ss << "multi mode: top-level object was a " << type_str(scratch.t) << ", "
3418
736
               << "should be an object whose keys are filenames and values hold "
3419
736
               << "the JSON for that file.";
3420
736
            throw makeError(loc, ss.str());
3421
736
        }
3422
1.06k
        auto *obj = static_cast<HeapObject *>(scratch.v.h);
3423
1.06k
        runInvariants(loc, obj);
3424
1.06k
        std::map<UString, const Identifier *> fields;
3425
4.62k
        for (const auto &f : objectFields(obj, true)) {
3426
4.62k
            fields[f->name] = f;
3427
4.62k
        }
3428
4.36k
        for (const auto &f : fields) {
3429
            // pushes FRAME_CALL
3430
4.36k
            const AST *body = objectIndex(loc, obj, f.second, 0);
3431
4.36k
            stack.top().val = scratch;
3432
4.36k
            evaluate(body, stack.size());
3433
4.36k
            auto vstr =
3434
4.36k
                string ? manifestString(body->location) : manifestJson(body->location, true, U"");
3435
            // Reset scratch so that the object we're manifesting doesn't
3436
            // get GC'd.
3437
4.36k
            scratch = stack.top().val;
3438
4.36k
            stack.pop();
3439
4.36k
            r[encode_utf8(f.first)] = encode_utf8(vstr);
3440
4.36k
        }
3441
1.06k
        return r;
3442
1.79k
    }
3443
3444
    std::vector<std::string> manifestStream(bool string)
3445
1.49k
    {
3446
1.49k
        std::vector<std::string> r;
3447
1.49k
        LocationRange loc("During manifestation");
3448
1.49k
        if (scratch.t != Value::ARRAY) {
3449
929
            std::stringstream ss;
3450
929
            ss << "stream mode: top-level object was a " << type_str(scratch.t) << ", "
3451
929
               << "should be an array whose elements hold "
3452
929
               << "the JSON for each document in the stream.";
3453
929
            throw makeError(loc, ss.str());
3454
929
        }
3455
561
        auto *arr = static_cast<HeapArray *>(scratch.v.h);
3456
736k
        for (auto *thunk : arr->elements) {
3457
736k
            LocationRange tloc = thunk->body == nullptr ? loc : thunk->body->location;
3458
736k
            if (thunk->filled) {
3459
0
                stack.newCall(loc, thunk, nullptr, 0, BindingFrame{});
3460
                // Keep arr alive when scratch is overwritten
3461
0
                stack.top().val = scratch;
3462
0
                scratch = thunk->content;
3463
736k
            } else {
3464
736k
                stack.newCall(loc, thunk, thunk->self, thunk->offset, thunk->upValues);
3465
                // Keep arr alive when scratch is overwritten
3466
736k
                stack.top().val = scratch;
3467
736k
                evaluate(thunk->body, stack.size());
3468
736k
            }
3469
736k
            UString element = string ? manifestString(tloc) : manifestJson(tloc, true, U"");
3470
736k
            scratch = stack.top().val;
3471
736k
            stack.pop();
3472
736k
            r.push_back(encode_utf8(element));
3473
736k
        }
3474
561
        return r;
3475
1.49k
    }
3476
3477
    static void handleRapidYamlError(const char* inner_msg, size_t length, ryml::Location loc, void * /* unused: userdata */)
3478
0
    {
3479
0
        std::ostringstream msg;
3480
0
        msg << "YAML error: " << loc.line << ":";
3481
0
        if (loc.col) {
3482
0
            msg << loc.col << ":";
3483
0
        } else if (loc.offset) {
3484
0
            msg << loc.offset << ":";
3485
0
        }
3486
0
        msg << " " << std::string_view(inner_msg, length);
3487
0
        throw RapidYamlError(msg.str());
3488
0
    }
3489
};
3490
3491
}  // namespace
3492
3493
std::string jsonnet_vm_execute(Allocator *alloc, const AST *ast, const ExtMap &ext_vars,
3494
                               unsigned max_stack, double gc_min_objects, double gc_growth_trigger,
3495
                               const VmNativeCallbackMap &natives,
3496
                               JsonnetImportCallback *import_callback, void *ctx,
3497
                               bool string_output)
3498
0
{
3499
0
    Interpreter vm(alloc,
3500
0
                   ext_vars,
3501
0
                   max_stack,
3502
0
                   gc_min_objects,
3503
0
                   gc_growth_trigger,
3504
0
                   natives,
3505
0
                   import_callback,
3506
0
                   ctx);
3507
0
    vm.evaluate(ast, 0);
3508
0
    if (string_output) {
3509
0
        return encode_utf8(vm.manifestString(LocationRange("During manifestation")));
3510
0
    } else {
3511
0
        return encode_utf8(vm.manifestJson(LocationRange("During manifestation"), true, U""));
3512
0
    }
3513
0
}
3514
3515
StrMap jsonnet_vm_execute_multi(Allocator *alloc, const AST *ast, const ExtMap &ext_vars,
3516
                                unsigned max_stack, double gc_min_objects, double gc_growth_trigger,
3517
                                const VmNativeCallbackMap &natives,
3518
                                JsonnetImportCallback *import_callback, void *ctx,
3519
                                bool string_output)
3520
2.86k
{
3521
2.86k
    Interpreter vm(alloc,
3522
2.86k
                   ext_vars,
3523
2.86k
                   max_stack,
3524
2.86k
                   gc_min_objects,
3525
2.86k
                   gc_growth_trigger,
3526
2.86k
                   natives,
3527
2.86k
                   import_callback,
3528
2.86k
                   ctx);
3529
2.86k
    vm.evaluate(ast, 0);
3530
2.86k
    return vm.manifestMulti(string_output);
3531
2.86k
}
3532
3533
std::vector<std::string> jsonnet_vm_execute_stream(Allocator *alloc, const AST *ast,
3534
                                                   const ExtMap &ext_vars, unsigned max_stack,
3535
                                                   double gc_min_objects, double gc_growth_trigger,
3536
                                                   const VmNativeCallbackMap &natives,
3537
                                                   JsonnetImportCallback *import_callback,
3538
                                                   void *ctx, bool string_output)
3539
3.03k
{
3540
3.03k
    Interpreter vm(alloc,
3541
3.03k
                   ext_vars,
3542
3.03k
                   max_stack,
3543
3.03k
                   gc_min_objects,
3544
3.03k
                   gc_growth_trigger,
3545
3.03k
                   natives,
3546
3.03k
                   import_callback,
3547
3.03k
                   ctx);
3548
3.03k
    vm.evaluate(ast, 0);
3549
3.03k
    return vm.manifestStream(string_output);
3550
3.03k
}
3551
3552
}  // namespace jsonnet::internal