Coverage Report

Created: 2023-09-25 07:10

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