From b40139433554783cf6d54b87f5faa4e98fb97551 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 9 Mar 2024 14:13:40 +0000 Subject: [PATCH 1/9] Rewrite manifestJson to execute "stacklessly" inside evaluate() This gets rid of the mutual recursion between manifestJson() and evaluate(), turning the manifestJson processing into a part of the interpreter's main loop. It should remove one avenue for native stack overflows during execution. The Interpreter needed to be able to convert things to JSON anyway, as this is the behaviour of string coercion (for string concatenation), and also the behaviour when using the 'error' operator/statement. --- core/vm.cpp | 292 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 191 insertions(+), 101 deletions(-) diff --git a/core/vm.cpp b/core/vm.cpp index 21418c475..6cfe2f0a0 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -94,6 +94,11 @@ enum FrameKind { FRAME_BUILTIN_JOIN_STRINGS, // When executing std.join over strings, used to hold intermediate state. FRAME_BUILTIN_JOIN_ARRAYS, // When executing std.join over arrays, used to hold intermediate state. FRAME_BUILTIN_DECODE_UTF8, // When executing std.decodeUTF8, used to hold intermediate state. + + FRAME_TO_STRING, // Coerce scratch to a string (no-op if it's already a string). + FRAME_TO_JSON, // Manifest scratch as JSON (ie, convert to string). + FRAME_ARRAY_TO_JSON, // Manifest an array (Frame.val) to JSON. + FRAME_OBJECT_TO_JSON, // Manifest an object (Frame.val) to JSON. }; /** A frame on the stack. @@ -156,6 +161,12 @@ struct Frame { UString str; bool first; + /** Used for manifesting an object to JSON. */ + std::map manifestFields; + + /** Used for manifesting an objects and arrays to JSON. 0 = single-line. */ + int indentLevel; + /** Used for accumulating bytes */ std::string bytes; @@ -191,6 +202,8 @@ struct Frame { location(ast->location), tailCall(false), elementId(0), + first(true), + indentLevel(0), context(NULL), self(NULL), offset(0) @@ -205,6 +218,8 @@ struct Frame { location(location), tailCall(false), elementId(0), + first(true), + indentLevel(0), context(NULL), self(NULL), offset(0) @@ -1881,11 +1896,6 @@ class Interpreter { } } - UString toString(const LocationRange &loc) - { - return manifestJson(loc, false, U""); - } - /** Recursively collect an object's invariants. * * \param curr @@ -1998,7 +2008,16 @@ class Interpreter { */ void evaluate(const AST *ast_, unsigned initial_stack_size) { + // In some cases (when manifesting JSON), we want to run the JSON-conversion continuations, + // but we already have a value to operate on not an AST, so for this purpose we just + // skip the AST part entirely. + // This allows "extra" computation to be done by the VM machinery, without needing to + // describe that computation in terms of Jsonnet code (an AST). + if (ast_ == nullptr) { + goto exec_continuations; + } recurse: + assert(ast_ != nullptr); switch (ast_->type) { case AST_APPLY: { @@ -2050,6 +2069,7 @@ class Interpreter { case AST_ERROR: { const auto &ast = *static_cast(ast_); stack.newFrame(FRAME_ERROR, ast_); + stack.newFrame(FRAME_TO_STRING, ast_); ast_ = ast.expr; goto recurse; } break; @@ -2223,6 +2243,7 @@ class Interpreter { std::abort(); } + exec_continuations: // To evaluate another AST, set ast to it, then goto recurse. // To pop, exit the switch or goto popframe // To change the frame and re-enter the switch, goto replaceframe @@ -2407,8 +2428,17 @@ class Interpreter { if (ast.op == BOP_PLUS) { // Handle co-ercions for string processing. stack.top().kind = FRAME_STRING_CONCAT; - stack.top().val2 = rhs; - goto replaceframe; + if (lhs.t != Value::STRING) { + ast_ = ast.left; + stack.newFrame(FRAME_TO_JSON, stack.top().location); + goto recurse; + } else if (rhs.t != Value::STRING) { + ast_ = ast.right; + stack.newFrame(FRAME_TO_JSON, stack.top().location); + goto recurse; + } else { + goto replaceframe; + } } } switch (ast.op) { @@ -2791,7 +2821,8 @@ class Interpreter { if (scratch.t == Value::STRING) { msg = static_cast(scratch.v.h)->value; } else { - msg = toString(ast.location); + std::cerr << "INTERNAL ERROR: value for FRAME_ERROR was not coerced to string" << std::endl; + std::abort(); } throw makeError(ast.location, encode_utf8(msg)); } break; @@ -3067,21 +3098,21 @@ class Interpreter { } break; case FRAME_STRING_CONCAT: { - const auto &ast = *static_cast(f.ast); const Value &lhs = stack.top().val; + const Value &rhs = stack.top().val2; + const HeapString *coerced = (lhs.t != Value::STRING || rhs.t != Value::STRING) + ? static_cast(scratch.v.h) + : nullptr; UString output; if (lhs.t == Value::STRING) { output.append(static_cast(lhs.v.h)->value); } else { - scratch = lhs; - output.append(toString(ast.left->location)); + output.append(coerced->value); } - const Value &rhs = stack.top().val2; if (rhs.t == Value::STRING) { output.append(static_cast(rhs.v.h)->value); } else { - scratch = rhs; - output.append(toString(ast.right->location)); + output.append(coerced->value); } scratch = makeString(output); } break; @@ -3151,6 +3182,143 @@ class Interpreter { } } break; + case FRAME_TO_STRING: { + if (scratch.t == Value::STRING) { + break; + } + } // Falls through. + case FRAME_TO_JSON: { + switch (scratch.t) { + case Value::NULL_TYPE: scratch = makeString(U"null"); break; + case Value::BOOLEAN: scratch = makeString(scratch.v.b ? U"true" : U"false"); break; + case Value::NUMBER: + scratch = makeString(decode_utf8(jsonnet_unparse_number(scratch.v.d))); + break; + case Value::STRING: + scratch = makeString(jsonnet_string_unparse(static_cast(scratch.v.h)->value, false)); + break; + case Value::FUNCTION: + throw makeError(stack.top().location, "couldn't manifest function in JSON output."); + case Value::ARRAY: { + const auto arr = static_cast(scratch.v.h); + if (arr->elements.empty()) { + scratch = makeString(U"[ ]"); + } else { + f.kind = FRAME_ARRAY_TO_JSON; + f.first = true; + f.elementId = 0; + f.val = scratch; + f.str.clear(); + goto replaceframe; + } + } break; + case Value::OBJECT: + const auto obj = static_cast(scratch.v.h); + // TODO: Do this by constructing a FRAME_INVARIANT here? + runInvariants(f.location, obj); + std::map fields; + for (const auto &field : objectFields(obj, true)) { + fields[field->name] = field; + } + if (fields.empty()) { + scratch = makeString(U"{ }"); + } else { + f.kind = FRAME_OBJECT_TO_JSON; + f.first = true; + f.val = scratch; + f.str.clear(); + std::swap(f.manifestFields, fields); // Swap instead of deep copy. + goto replaceframe; + } + break; + } + } break; + case FRAME_ARRAY_TO_JSON: { + assert(f.val.t == Value::ARRAY); + const auto arr = static_cast(stack.top().val.v.h); + if (!f.first) { + // We should have got here by coercing an array element to a string, + // leaving the JSON representation in scratch. + assert(scratch.t == Value::STRING); + f.str.append(f.str.empty() ? U"[" : (f.indentLevel ? U"," : U", ")); + if (f.indentLevel) { + f.str.append(U"\n"); + for (int i = 0; i < f.indentLevel; ++i) { + f.str.append(U" "); + } + } + f.str.append(static_cast(scratch.v.h)->value); + ++f.elementId; + } + f.first = false; + if (f.elementId < arr->elements.size()) { + const auto thunk = arr->elements[f.elementId]; + const auto loc = f.location; + const int indentLevel = (f.indentLevel == 0) ? 0 : f.indentLevel + 1; + stack.newFrame(FRAME_TO_JSON, loc); + stack.top().indentLevel = indentLevel; + if (thunk->filled) { + scratch = thunk->content; + goto replaceframe; + } else { + stack.newCall(loc, thunk, thunk->self, thunk->offset, thunk->upValues); + ast_ = thunk->body; + goto recurse; + } + } else { + if (f.indentLevel) { + f.str.append(U"\n"); + for (int i = 0; i < f.indentLevel - 1; ++i) { + f.str.append(U" "); + } + } + f.str.append(U"]"); + scratch = makeString(f.str); + } + } break; + case FRAME_OBJECT_TO_JSON: { + assert(f.val.t == Value::OBJECT); + const auto obj = static_cast(f.val.v.h); + if (!f.first) { + // We should have got here by coercing an object field value to a string, + // leaving the JSON representation in scratch. + assert(scratch.t == Value::STRING); + const auto it = f.manifestFields.begin(); + f.str.append(f.str.empty() ? U"{" : (f.indentLevel ? U"," : U", ")); + if (f.indentLevel) { + f.str.append(U"\n"); + for (int i = 0; i < f.indentLevel; ++i) { + f.str.append(U" "); + } + } + f.str.append(jsonnet_string_unparse(it->first, false)); + f.str.append(U": "); + f.str.append(static_cast(scratch.v.h)->value); + f.manifestFields.erase(it); + } + f.first = false; + if (!f.manifestFields.empty()) { + const Identifier *ident = f.manifestFields.begin()->second; + const auto loc = f.location; + const int indentLevel = (f.indentLevel == 0) ? 0 : f.indentLevel + 1; + stack.newFrame(FRAME_TO_JSON, loc); + stack.top().indentLevel = indentLevel; + // pushes FRAME_CALL + const AST *body = objectIndex(loc, obj, ident, 0); + ast_ = body; + goto recurse; + } else { + if (f.indentLevel) { + f.str.append(U"\n"); + for (int i = 0; i < f.indentLevel - 1; ++i) { + f.str.append(U" "); + } + } + f.str.append(U"}"); + scratch = makeString(f.str); + } + } break; + default: std::cerr << "INTERNAL ERROR: Unknown FrameKind: " << f.kind << std::endl; std::abort(); @@ -3171,91 +3339,13 @@ class Interpreter { * * \param multiline If true, will print objects and arrays in an indented fashion. */ - UString manifestJson(const LocationRange &loc, bool multiline, const UString &indent) + UString manifestJson(const LocationRange &loc, bool multiline) { - // Printing fields means evaluating and binding them, which can trigger - // garbage collection. - - UStringStream ss; - switch (scratch.t) { - case Value::ARRAY: { - HeapArray *arr = static_cast(scratch.v.h); - if (arr->elements.size() == 0) { - ss << U"[ ]"; - } else { - const char32_t *prefix = multiline ? U"[\n" : U"["; - UString indent2 = multiline ? indent + U" " : indent; - for (auto *thunk : arr->elements) { - LocationRange tloc = thunk->body == nullptr ? loc : thunk->body->location; - if (thunk->filled) { - stack.newCall(loc, thunk, nullptr, 0, BindingFrame{}); - // Keep arr alive when scratch is overwritten - stack.top().val = scratch; - scratch = thunk->content; - } else { - stack.newCall(loc, thunk, thunk->self, thunk->offset, thunk->upValues); - // Keep arr alive when scratch is overwritten - stack.top().val = scratch; - evaluate(thunk->body, stack.size()); - } - auto element = manifestJson(tloc, multiline, indent2); - // Restore scratch - scratch = stack.top().val; - stack.pop(); - ss << prefix << indent2 << element; - prefix = multiline ? U",\n" : U", "; - } - ss << (multiline ? U"\n" : U"") << indent << U"]"; - } - } break; - - case Value::BOOLEAN: ss << (scratch.v.b ? U"true" : U"false"); break; - - case Value::NUMBER: ss << decode_utf8(jsonnet_unparse_number(scratch.v.d)); break; - - case Value::FUNCTION: - throw makeError(loc, "couldn't manifest function in JSON output."); - - case Value::NULL_TYPE: ss << U"null"; break; - - case Value::OBJECT: { - auto *obj = static_cast(scratch.v.h); - runInvariants(loc, obj); - // Using std::map has the useful side-effect of ordering the fields - // alphabetically. - std::map fields; - for (const auto &f : objectFields(obj, true)) { - fields[f->name] = f; - } - if (fields.size() == 0) { - ss << U"{ }"; - } else { - UString indent2 = multiline ? indent + U" " : indent; - const char32_t *prefix = multiline ? U"{\n" : U"{"; - for (const auto &f : fields) { - // pushes FRAME_CALL - const AST *body = objectIndex(loc, obj, f.second, 0); - stack.top().val = scratch; - evaluate(body, stack.size()); - auto vstr = manifestJson(body->location, multiline, indent2); - // Reset scratch so that the object we're manifesting doesn't - // get GC'd. - scratch = stack.top().val; - stack.pop(); - ss << prefix << indent2 << jsonnet_string_unparse(f.first, false) << U": " - << vstr; - prefix = multiline ? U",\n" : U", "; - } - ss << (multiline ? U"\n" : U"") << indent << U"}"; - } - } break; - - case Value::STRING: { - const UString &str = static_cast(scratch.v.h)->value; - ss << jsonnet_string_unparse(str, false); - } break; - } - return ss.str(); + const unsigned initial_size = stack.size(); + stack.newFrame(FRAME_TO_JSON, loc); + stack.top().indentLevel = multiline ? 1 : 0; + evaluate(nullptr, initial_size); + return manifestString(loc); } UString manifestString(const LocationRange &loc) @@ -3291,7 +3381,7 @@ class Interpreter { stack.top().val = scratch; evaluate(body, stack.size()); auto vstr = - string ? manifestString(body->location) : manifestJson(body->location, true, U""); + string ? manifestString(body->location) : manifestJson(body->location, true); // Reset scratch so that the object we're manifesting doesn't // get GC'd. scratch = stack.top().val; @@ -3326,7 +3416,7 @@ class Interpreter { stack.top().val = scratch; evaluate(thunk->body, stack.size()); } - UString element = string ? manifestString(tloc) : manifestJson(tloc, true, U""); + UString element = string ? manifestString(tloc) : manifestJson(tloc, true); scratch = stack.top().val; stack.pop(); r.push_back(encode_utf8(element)); @@ -3355,7 +3445,7 @@ std::string jsonnet_vm_execute(Allocator *alloc, const AST *ast, const ExtMap &e if (string_output) { return encode_utf8(vm.manifestString(LocationRange("During manifestation"))); } else { - return encode_utf8(vm.manifestJson(LocationRange("During manifestation"), true, U"")); + return encode_utf8(vm.manifestJson(LocationRange("During manifestation"), true)); } } From b4ee10b2fd2700d20b197f8795b8ee540efe979f Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 9 Mar 2024 15:18:10 +0000 Subject: [PATCH 2/9] fix incorrect stack manipulation in FRAME_TO_JSON for objects --- core/vm.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/core/vm.cpp b/core/vm.cpp index 6cfe2f0a0..42703d6fd 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -3214,20 +3214,31 @@ class Interpreter { } break; case Value::OBJECT: const auto obj = static_cast(scratch.v.h); - // TODO: Do this by constructing a FRAME_INVARIANT here? - runInvariants(f.location, obj); + const auto loc = f.location; + + f.kind = FRAME_OBJECT_TO_JSON; + f.first = true; + f.val = scratch; + f.str.clear(); std::map fields; for (const auto &field : objectFields(obj, true)) { fields[field->name] = field; } - if (fields.empty()) { + std::swap(f.manifestFields, fields); // Swap instead of deep copy. + + // runInvariants re-enters evaluate() so it messes with the stack. + // Hence we need to make sure that the FRAME_OBJECT_TO_JSON is set up _first_, + // even if the object is "empty" (no fields to manifest). + // TODO: Do this by constructing a FRAME_INVARIANT here? + runInvariants(loc, obj); + + // fields was already cleared above, and `f` may have been invalidated + // by the stack-manipulation inside runInvariants. So we need to explicitly + // look at stack.top().manifestFields. + if (stack.top().manifestFields.empty()) { scratch = makeString(U"{ }"); } else { - f.kind = FRAME_OBJECT_TO_JSON; - f.first = true; - f.val = scratch; - f.str.clear(); - std::swap(f.manifestFields, fields); // Swap instead of deep copy. + assert(stack.top().kind == FRAME_OBJECT_TO_JSON); goto replaceframe; } break; From e4f534b548c8d96211bc584f9adc0307e36d4e2c Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 9 Mar 2024 16:11:26 +0000 Subject: [PATCH 3/9] create a FRAME_CALL for nested objects being manifested (to apply stack limits) --- core/vm.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/vm.cpp b/core/vm.cpp index 42703d6fd..294be27d5 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -3246,7 +3246,7 @@ class Interpreter { } break; case FRAME_ARRAY_TO_JSON: { assert(f.val.t == Value::ARRAY); - const auto arr = static_cast(stack.top().val.v.h); + const auto arr = static_cast(stack.top().val.v.h); if (!f.first) { // We should have got here by coercing an array element to a string, // leaving the JSON representation in scratch. @@ -3266,6 +3266,8 @@ class Interpreter { const auto thunk = arr->elements[f.elementId]; const auto loc = f.location; const int indentLevel = (f.indentLevel == 0) ? 0 : f.indentLevel + 1; + // Add a call frame for the JSON conversion, used to apply depth limit. + stack.newCall(thunk->body->location, arr, nullptr, 0, BindingFrame{}); stack.newFrame(FRAME_TO_JSON, loc); stack.top().indentLevel = indentLevel; if (thunk->filled) { @@ -3312,6 +3314,8 @@ class Interpreter { const Identifier *ident = f.manifestFields.begin()->second; const auto loc = f.location; const int indentLevel = (f.indentLevel == 0) ? 0 : f.indentLevel + 1; + // Add a call frame for the JSON conversion, used to apply depth limit. + stack.newCall(loc, obj, nullptr, 0, BindingFrame{}); stack.newFrame(FRAME_TO_JSON, loc); stack.top().indentLevel = indentLevel; // pushes FRAME_CALL From cbec0793c686767b143619c3a8a89dff37b31d73 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 9 Mar 2024 16:29:37 +0000 Subject: [PATCH 4/9] fix a couple of crash bugs with stackless JSON conversion --- core/vm.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/vm.cpp b/core/vm.cpp index 294be27d5..c5ce43bff 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -339,8 +339,11 @@ class Stack { if (name == "") name = "anonymous"; + if (dynamic_cast(e)) { return "object <" + name + ">"; + } else if (dynamic_cast(e)) { + return "array <" + name + ">"; } else if (auto *thunk = dynamic_cast(e)) { if (thunk->name == nullptr) { return ""; // Argument of builtin, or root (since top level functions). @@ -348,6 +351,7 @@ class Stack { return "thunk <" + encode_utf8(thunk->name->name) + ">"; } } else { + assert(dynamic_cast(e)); const auto *func = static_cast(e); if (func->body == nullptr) { return "builtin function <" + func->builtinName + ">"; @@ -3267,7 +3271,7 @@ class Interpreter { const auto loc = f.location; const int indentLevel = (f.indentLevel == 0) ? 0 : f.indentLevel + 1; // Add a call frame for the JSON conversion, used to apply depth limit. - stack.newCall(thunk->body->location, arr, nullptr, 0, BindingFrame{}); + stack.newCall(thunk->body ? thunk->body->location : LocationRange("unknown"), arr, nullptr, 0, BindingFrame{}); stack.newFrame(FRAME_TO_JSON, loc); stack.top().indentLevel = indentLevel; if (thunk->filled) { From 2f47ccc5bacafe0b63e6d65107333ccb43434d67 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 9 Mar 2024 16:54:32 +0000 Subject: [PATCH 5/9] change the stack handling in FRAME_OBJECT_TO_JSON to get better stack traces --- core/vm.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/vm.cpp b/core/vm.cpp index c5ce43bff..081f42a04 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -3318,12 +3318,12 @@ class Interpreter { const Identifier *ident = f.manifestFields.begin()->second; const auto loc = f.location; const int indentLevel = (f.indentLevel == 0) ? 0 : f.indentLevel + 1; - // Add a call frame for the JSON conversion, used to apply depth limit. - stack.newCall(loc, obj, nullptr, 0, BindingFrame{}); - stack.newFrame(FRAME_TO_JSON, loc); - stack.top().indentLevel = indentLevel; - // pushes FRAME_CALL + // pushes FRAME_CALL (note this also applies the stack depth limit) const AST *body = objectIndex(loc, obj, ident, 0); + // Before returning from the objectIndex call, convert the result to a JSON string value. + stack.newFrame(FRAME_TO_JSON, body->location); + stack.top().indentLevel = indentLevel; + // Replace the location up the stack for better traces. ast_ = body; goto recurse; } else { From 39dfb41bbe433c57bc2746924980d1847f9105c8 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 9 Mar 2024 17:20:11 +0000 Subject: [PATCH 6/9] adjust location tracking when manifesting arrays to JSON --- core/vm.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/vm.cpp b/core/vm.cpp index 081f42a04..876f2125a 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -3270,15 +3270,16 @@ class Interpreter { const auto thunk = arr->elements[f.elementId]; const auto loc = f.location; const int indentLevel = (f.indentLevel == 0) ? 0 : f.indentLevel + 1; - // Add a call frame for the JSON conversion, used to apply depth limit. - stack.newCall(thunk->body ? thunk->body->location : LocationRange("unknown"), arr, nullptr, 0, BindingFrame{}); - stack.newFrame(FRAME_TO_JSON, loc); + const LocationRange tloc = thunk ? thunk->body->location : loc; + // Add an explicit call frame for the JSON conversion, used to apply depth limit. + stack.newCall(tloc, arr, nullptr, 0, BindingFrame{}); + stack.newFrame(FRAME_TO_JSON, tloc); stack.top().indentLevel = indentLevel; if (thunk->filled) { scratch = thunk->content; goto replaceframe; } else { - stack.newCall(loc, thunk, thunk->self, thunk->offset, thunk->upValues); + stack.newCall(tloc, thunk, thunk->self, thunk->offset, thunk->upValues); ast_ = thunk->body; goto recurse; } From 5ce04d3edbc302b80e7052cd1edab35334800d1a Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 9 Mar 2024 17:29:31 +0000 Subject: [PATCH 7/9] fix crash bug in handling of LocationRange in array to json --- core/vm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm.cpp b/core/vm.cpp index 876f2125a..52c59a696 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -3270,7 +3270,7 @@ class Interpreter { const auto thunk = arr->elements[f.elementId]; const auto loc = f.location; const int indentLevel = (f.indentLevel == 0) ? 0 : f.indentLevel + 1; - const LocationRange tloc = thunk ? thunk->body->location : loc; + const LocationRange tloc = thunk->body ? thunk->body->location : loc; // Add an explicit call frame for the JSON conversion, used to apply depth limit. stack.newCall(tloc, arr, nullptr, 0, BindingFrame{}); stack.newFrame(FRAME_TO_JSON, tloc); From c16d4a331738129e7295acfd48f21128e9f8168f Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 9 Mar 2024 17:39:53 +0000 Subject: [PATCH 8/9] try to match previous error traces fairly closely --- core/vm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/vm.cpp b/core/vm.cpp index 52c59a696..c26346329 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -2434,11 +2434,11 @@ class Interpreter { stack.top().kind = FRAME_STRING_CONCAT; if (lhs.t != Value::STRING) { ast_ = ast.left; - stack.newFrame(FRAME_TO_JSON, stack.top().location); + stack.newFrame(FRAME_TO_JSON, ast_->location); goto recurse; } else if (rhs.t != Value::STRING) { ast_ = ast.right; - stack.newFrame(FRAME_TO_JSON, stack.top().location); + stack.newFrame(FRAME_TO_JSON, ast_->location); goto recurse; } else { goto replaceframe; From becdd8713e3f8db71fcfebbe6cbe5b1257036644 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 9 Mar 2024 17:40:02 +0000 Subject: [PATCH 9/9] refresh error trace goldens --- ...or.array_recursive_manifest.jsonnet.golden | 40 +++++++++---------- ...error.inside_tostring_array.jsonnet.golden | 3 +- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/test_suite/error.array_recursive_manifest.jsonnet.golden b/test_suite/error.array_recursive_manifest.jsonnet.golden index c07d62b0a..0670eccd6 100644 --- a/test_suite/error.array_recursive_manifest.jsonnet.golden +++ b/test_suite/error.array_recursive_manifest.jsonnet.golden @@ -1,22 +1,22 @@ RUNTIME ERROR: max stack frames exceeded. - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array ... - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - error.array_recursive_manifest.jsonnet:17:12 thunk - During manifestation + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 array + error.array_recursive_manifest.jsonnet:17:12 diff --git a/test_suite/error.inside_tostring_array.jsonnet.golden b/test_suite/error.inside_tostring_array.jsonnet.golden index ca6384bcf..80fd66df2 100644 --- a/test_suite/error.inside_tostring_array.jsonnet.golden +++ b/test_suite/error.inside_tostring_array.jsonnet.golden @@ -1,3 +1,4 @@ RUNTIME ERROR: foobar error.inside_tostring_array.jsonnet:17:8-22 thunk - error.inside_tostring_array.jsonnet:17:1-23 + error.inside_tostring_array.jsonnet:17:8-22 array + error.inside_tostring_array.jsonnet:17:8-22