Coverage Report

Created: 2022-03-15 07:22

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