Merge remote-tracking branch 'origin/topic/timw/4553-combine-call-stacks'
Some checks are pending
pre-commit / pre-commit (push) Waiting to run

* origin/topic/timw/4553-combine-call-stacks:
  Replace g_frame_stack with call_stack used by ScriptProfiler
  Clean up initialization in Func.h
This commit is contained in:
Tim Wojtulewicz 2025-10-13 09:49:10 -07:00
commit 5227e7ebb9
13 changed files with 135 additions and 116 deletions

18
CHANGES
View file

@ -1,3 +1,21 @@
8.1.0-dev.676 | 2025-10-13 09:54:47 -0700
* Replace g_frame_stack with call_stack used by ScriptProfiler (Tim Wojtulewicz, Corelight)
* Clean up initialization in Func.h (Tim Wojtulewicz, Corelight)
* Bump spicy-format (Benjamin Bannier, Corelight)
This brings in two changes:
- proper indention for blocks starting with multiple comments
- switch the hook from `language: rust` to `language: python` to avoid
nonsense due to pre-commit ignore Rust lock files,
https://github.com/pre-commit/pre-commit/issues/3162.
The second one should help avoid future instances of the hook randomly
breaking as its dependencies accidentially break semver.
8.1.0-dev.671 | 2025-10-11 12:38:21 +0200
* ZValElement: s to o renaming (Arne Welzel, Corelight)

8
NEWS
View file

@ -26,6 +26,14 @@ Breaking Changes
to live in the ``auxil`` directory, after having moved there from an external plugin.
It is now built as part of main Zeek build whenever building on Linux.
- The global ``g_frame_stack`` C++ variable was removed. This variable was used to track
the current stack of script-function frames, mostly for the built-in script debugger. We
realized that we were also tracking this information in the global ``call_stack``
variable, used by the script profiler. These two have now been combined together into
``call_stack``. Plugins using ``g_frame_stack`` will need to be adapted to use the other
variable. We opted to not use a deprecation cycle for this due to the complication with
managing the data across both variables.
New Functionality
-----------------

View file

@ -1 +1 @@
8.1.0-dev.671
8.1.0-dev.676

View file

@ -303,17 +303,16 @@ void DbgBreakpoint::PrintHitMsg() {
case BP_FUNC:
case BP_LINE: {
ODesc d;
Frame* f = g_frame_stack.back();
const ScriptFunc* func = f->GetFunction();
const auto& f = call_stack.back().frame;
if ( func )
if ( const Func* func = f->GetFunction() )
func->DescribeDebug(&d, f->GetFuncArgs());
const Location* loc = at_stmt->GetLocationInfo();
debug_msg("Breakpoint %d, %s at %s:%d\n", GetID(), d.Description(), loc->FileName(), loc->FirstLine());
}
return;
}
case BP_TIME: assert(false);

View file

@ -143,16 +143,14 @@ int TraceState::LogTrace(const char* fmt, ...) {
const Stmt* stmt;
Location loc;
if ( g_frame_stack.size() > 0 && g_frame_stack.back() ) {
stmt = g_frame_stack.back()->GetNextStmt();
if ( ! call_stack.empty() ) {
const auto& frame = call_stack.back().frame;
stmt = frame->GetNextStmt();
if ( stmt )
loc = *stmt->GetLocationInfo();
else {
const ScriptFunc* f = g_frame_stack.back()->GetFunction();
if ( f )
else if ( const Func* f = frame->GetFunction() )
loc = *f->GetLocationInfo();
}
}
if ( ! loc.FileName() ) {
static constexpr const char str[] = "<no filename>";
@ -163,7 +161,7 @@ int TraceState::LogTrace(const char* fmt, ...) {
fprintf(trace_file, "%s:%d", loc.FileName(), loc.LastLine());
// Each stack frame is indented.
for ( int i = 0; i < int(g_frame_stack.size()); ++i )
for ( const auto& call : call_stack )
fprintf(trace_file, "\t");
retval = vfprintf(trace_file, fmt, args);
@ -589,16 +587,18 @@ static int dbg_dispatch_cmd(DebugCmd cmd_code, const vector<string>& args) {
case dcQuit: debug_msg("Program Terminating\n"); exit(0);
case dcNext:
g_frame_stack.back()->BreakBeforeNextStmt(true);
case dcNext: {
Frame* frame = call_stack.back().frame;
frame->BreakBeforeNextStmt(true);
step_or_next_pending = true;
last_frame = g_frame_stack.back();
last_frame = frame;
break;
}
case dcStep:
g_debugger_state.BreakBeforeNextStmt(true);
step_or_next_pending = true;
last_frame = g_frame_stack.back();
last_frame = call_stack.back().frame;
break;
case dcContinue:
@ -607,7 +607,7 @@ static int dbg_dispatch_cmd(DebugCmd cmd_code, const vector<string>& args) {
break;
case dcFinish:
g_frame_stack.back()->BreakOnReturn(true);
call_stack.back().frame->BreakOnReturn(true);
g_debugger_state.BreakBeforeNextStmt(false);
break;
@ -663,7 +663,7 @@ static char* get_prompt(bool reset_counter = false) {
string get_context_description(const Stmt* stmt, const Frame* frame) {
ODesc d;
const ScriptFunc* func = frame ? frame->GetFunction() : nullptr;
const Func* func = frame ? frame->GetFunction() : nullptr;
if ( func )
func->DescribeDebug(&d, frame->GetFuncArgs());
@ -698,9 +698,8 @@ int dbg_handle_debug_input() {
g_debugger_state.BreakFromSignal(false);
}
Frame* curr_frame = g_frame_stack.back();
const ScriptFunc* func = curr_frame->GetFunction();
if ( func )
auto curr_frame = call_stack.back().frame;
if ( const Func* func = curr_frame->GetFunction() )
current_module = extract_module_name(func->GetName().c_str());
else
current_module = GLOBAL_MODULE_NAME;
@ -711,8 +710,8 @@ int dbg_handle_debug_input() {
const Location loc = *stmt->GetLocationInfo();
if ( ! step_or_next_pending || g_frame_stack.back() != last_frame ) {
string context = get_context_description(stmt, g_frame_stack.back());
if ( ! step_or_next_pending || call_stack.back().frame != last_frame ) {
string context = get_context_description(stmt, call_stack.back().frame);
debug_msg("%s\n", context.c_str());
}
@ -848,16 +847,16 @@ ValPtr dbg_eval_expr(const char* expr) {
// Push the current frame's associated scope.
// Note: g_debugger_state.curr_frame_idx is the user-visible number,
// while the array index goes in the opposite direction
int frame_idx = (g_frame_stack.size() - 1) - g_debugger_state.curr_frame_idx;
int frame_idx = (call_stack.size() - 1) - g_debugger_state.curr_frame_idx;
if ( ! (frame_idx >= 0 && (unsigned)frame_idx < g_frame_stack.size()) )
reporter->InternalError("Assertion failed: frame_idx >= 0 && (unsigned) frame_idx < g_frame_stack.size()");
if ( ! (frame_idx >= 0 && static_cast<size_t>(frame_idx) < call_stack.size()) )
reporter->InternalError("Assertion failed: frame_idx >= 0 && (unsigned) frame_idx < call_stack.size()");
Frame* frame = g_frame_stack[frame_idx];
if ( ! (frame) )
const auto& frame = call_stack[frame_idx].frame;
if ( ! frame )
reporter->InternalError("Assertion failed: frame");
const ScriptFunc* func = frame->GetFunction();
const Func* func = frame->GetFunction();
if ( func )
push_existing_scope(func->GetScope());

View file

@ -173,7 +173,7 @@ int find_all_matching_cmds(const string& prefix, const char* array_of_matches[])
// Start, end bounds of which frame numbers to print
static int dbg_backtrace_internal(int start, int end) {
if ( start < 0 || end < 0 || (unsigned)start >= g_frame_stack.size() || (unsigned)end >= g_frame_stack.size() )
if ( start < 0 || end < 0 || (unsigned)start >= call_stack.size() || (unsigned)end >= call_stack.size() )
reporter->InternalError("Invalid stack frame index in DbgBacktraceInternal\n");
if ( start < end ) {
@ -183,11 +183,11 @@ static int dbg_backtrace_internal(int start, int end) {
}
for ( int i = start; i >= end; --i ) {
const Frame* f = g_frame_stack[i];
const auto& f = call_stack[i].frame;
const Stmt* stmt = f ? f->GetNextStmt() : nullptr;
string context = get_context_description(stmt, f);
debug_msg("#%d %s\n", int(g_frame_stack.size() - 1 - i), context.c_str());
debug_msg("#%d %s\n", int(call_stack.size() - 1 - i), context.c_str());
};
return 1;
@ -196,7 +196,7 @@ static int dbg_backtrace_internal(int start, int end) {
// Returns 0 for illegal arguments, or 1 on success.
int dbg_cmd_backtrace(DebugCmd cmd, const vector<string>& args) {
assert(cmd == dcBacktrace);
assert(g_frame_stack.size() > 0);
assert(! call_stack.empty());
unsigned int start_iter;
int end_iter;
@ -210,20 +210,20 @@ int dbg_cmd_backtrace(DebugCmd cmd, const vector<string>& args) {
}
if ( how_many > 0 ) { // innermost N frames
start_iter = g_frame_stack.size() - 1;
start_iter = call_stack.size() - 1;
end_iter = start_iter - how_many + 1;
if ( end_iter < 0 )
end_iter = 0;
}
else { // outermost N frames
start_iter = how_many - 1;
if ( start_iter + 1 > g_frame_stack.size() )
start_iter = g_frame_stack.size() - 1;
if ( start_iter + 1 > call_stack.size() )
start_iter = call_stack.size() - 1;
end_iter = 0;
}
}
else {
start_iter = g_frame_stack.size() - 1;
start_iter = call_stack.size() - 1;
end_iter = 0;
}
@ -248,7 +248,7 @@ int dbg_cmd_frame(DebugCmd cmd, const vector<string>& args) {
return 0;
}
if ( idx < 0 || (unsigned int)idx >= g_frame_stack.size() ) {
if ( idx < 0 || (unsigned int)idx >= call_stack.size() ) {
debug_msg("No frame %d", idx);
return 0;
}
@ -267,7 +267,7 @@ int dbg_cmd_frame(DebugCmd cmd, const vector<string>& args) {
}
else if ( cmd == dcUp ) {
if ( (unsigned int)(g_debugger_state.curr_frame_idx + 1) == g_frame_stack.size() ) {
if ( (unsigned int)(g_debugger_state.curr_frame_idx + 1) == call_stack.size() ) {
debug_msg("Outermost frame already selected\n");
return 0;
}
@ -275,11 +275,11 @@ int dbg_cmd_frame(DebugCmd cmd, const vector<string>& args) {
++g_debugger_state.curr_frame_idx;
}
int user_frame_number = g_frame_stack.size() - 1 - g_debugger_state.curr_frame_idx;
int user_frame_number = call_stack.size() - 1 - g_debugger_state.curr_frame_idx;
// Set the current location to the new frame being looked at
// for 'list', 'break', etc.
const Stmt* stmt = g_frame_stack[user_frame_number]->GetNextStmt();
const Stmt* stmt = call_stack[user_frame_number].frame->GetNextStmt();
if ( ! stmt )
reporter->InternalError("Assertion failed: stmt is null");
@ -310,9 +310,9 @@ int dbg_cmd_break(DebugCmd cmd, const vector<string>& args) {
int cond_index = -1; // at which argument pos. does bp condition start?
if ( args.empty() || args[0] == "if" ) { // break on next stmt
int user_frame_number = g_frame_stack.size() - 1 - g_debugger_state.curr_frame_idx;
int user_frame_number = call_stack.size() - 1 - g_debugger_state.curr_frame_idx;
Stmt* stmt = g_frame_stack[user_frame_number]->GetNextStmt();
Stmt* stmt = call_stack[user_frame_number].frame->GetNextStmt();
if ( ! stmt )
reporter->InternalError("Assertion failed: stmt is null");

View file

@ -11,31 +11,25 @@
#include "zeek/Val.h"
#include "zeek/broker/Data.h"
std::vector<zeek::detail::Frame*> g_frame_stack;
namespace zeek::detail {
Frame::Frame(int arg_size, const ScriptFunc* func, const zeek::Args* fn_args) {
Frame::Frame(int arg_size, const Func* func, const zeek::Args* fn_args) {
size = arg_size;
if ( size > 0 )
frame = std::make_unique<Element[]>(size);
function = func;
func_args = fn_args;
next_stmt = nullptr;
break_before_next_stmt = false;
break_on_return = false;
call = nullptr;
delayed = false;
// We could Ref()/Unref() the captures frame, but there's really
// no need because by definition this current frame exists to
// enable execution of the function, and its captures frame won't
// go away until the function itself goes away, which can only be
// after this frame does.
captures = function ? function->GetCapturesFrame() : nullptr;
captures_offset_map = function ? function->GetCapturesOffsetMap() : nullptr;
current_offset = 0;
if ( function && function->GetKind() == Func::SCRIPT_FUNC ) {
auto* sf = static_cast<const ScriptFunc*>(function);
captures = sf->GetCapturesFrame();
captures_offset_map = sf->GetCapturesOffsetMap();
}
}
void Frame::SetElement(int n, ValPtr v) {

View file

@ -20,11 +20,11 @@ using ValPtr = IntrusivePtr<Val>;
class BrokerListView;
class BrokerData;
class Func;
namespace detail {
class CallExpr;
class ScriptFunc;
using IDPtr = IntrusivePtr<ID>;
namespace trigger {
@ -47,7 +47,7 @@ public:
* @param func the function that is creating this frame
* @param fn_args the arguments being passed to that function.
*/
Frame(int size, const ScriptFunc* func, const zeek::Args* fn_args);
Frame(int size, const Func* func, const zeek::Args* fn_args);
/**
* Returns the size of the frame.
@ -117,7 +117,7 @@ public:
/**
* @return the function that the frame is associated with.
*/
const ScriptFunc* GetFunction() const { return function; }
const Func* GetFunction() const { return function; }
/**
* @return the arguments passed to the function that this frame
@ -130,7 +130,7 @@ public:
*
* @param func the function for the frame to be associated with.
*/
void SetFunction(ScriptFunc* func) { function = func; }
void SetFunction(Func* func) { function = func; }
/**
* Sets the next statement to be executed in the context of the frame.
@ -216,9 +216,9 @@ private:
/** The number of vals that can be stored in this frame. */
int size;
bool break_before_next_stmt;
bool break_on_return;
bool delayed;
bool break_before_next_stmt = false;
bool break_on_return = false;
bool delayed = false;
/** Associates ID's offsets with values. */
std::unique_ptr<Element[]> frame;
@ -228,25 +228,25 @@ private:
* This is how we support inlined functions without having to
* alter the offsets associated with their local variables.
*/
int current_offset;
int current_offset = 0;
/** Frame used for lambda/when captures. */
Frame* captures;
Frame* captures = nullptr;
/** Maps IDs to offsets into the "captures" frame. If the ID
* isn't present, then it's not a capture.
*/
const OffsetMap* captures_offset_map;
const OffsetMap* captures_offset_map = nullptr;
/** The function this frame is associated with. */
const ScriptFunc* function;
const Func* function = nullptr;
// The following is only needed for the debugger.
/** The arguments to the function that this Frame is associated with. */
const zeek::Args* func_args;
const zeek::Args* func_args = nullptr;
/** The next statement to be evaluated in the context of this frame. */
Stmt* next_stmt;
Stmt* next_stmt = nullptr;
trigger::TriggerPtr trigger;
const CallExpr* call = nullptr;
@ -266,4 +266,4 @@ private:
* DebugFrame which provides the information that the debugger uses. See:
* https://stackoverflow.com/a/16211097
*/
extern std::vector<zeek::detail::Frame*> g_frame_stack;
// extern std::vector<zeek::detail::Frame*> g_frame_stack;

View file

@ -93,10 +93,11 @@ std::string render_call_stack() {
if ( lvl > 0 )
rval += " | ";
const auto& name = ci.func->GetName();
const auto& name = ci.frame->GetFunction()->GetName();
std::string arg_desc;
for ( const auto& arg : ci.args ) {
const auto& args = ci.frame->GetFuncArgs();
for ( const auto& arg : *args ) {
ODesc d;
d.SetShort();
arg->Describe(&d);
@ -331,17 +332,16 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
return Flavor() == FUNC_FLAVOR_HOOK ? val_mgr->True() : nullptr;
}
auto f = make_intrusive<Frame>(frame_size, this, args);
Frame f{static_cast<int>(frame_size), this, args};
// Hand down any trigger.
if ( parent ) {
f->SetTrigger({NewRef{}, parent->GetTrigger()});
f->SetTriggerAssoc(parent->GetTriggerAssoc());
f.SetTrigger({NewRef{}, parent->GetTrigger()});
f.SetTriggerAssoc(parent->GetTriggerAssoc());
}
g_frame_stack.push_back(f.get()); // used for backtracing
const CallExpr* call_expr = parent ? parent->GetCall() : nullptr;
call_stack.emplace_back(CallInfo{call_expr, this, *args});
call_stack.emplace_back(call_expr, &f);
// If a script function is ever invoked with more arguments than it has
// parameters log an error and return. Most likely a "variadic function"
@ -374,24 +374,23 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
for ( auto j = 0u; j < args->size(); ++j ) {
const auto& arg = (*args)[j];
if ( f->GetElement(j) != arg )
if ( f.GetElement(j) != arg )
// Either not yet set, or somebody reassigned the frame slot.
f->SetElement(j, arg);
f.SetElement(j, arg);
}
if ( spm )
spm->StartInvocation(this, body.stmts);
f->Reset(args->size());
f.Reset(args->size());
try {
result = body.stmts->Exec(f.get(), flow);
result = body.stmts->Exec(&f, flow);
}
catch ( InterpreterException& e ) {
// Already reported, but now determine whether to unwind further.
if ( Flavor() == FUNC_FLAVOR_FUNCTION ) {
g_frame_stack.pop_back();
call_stack.pop_back();
// Result not set b/c exception was thrown
throw;
@ -404,7 +403,7 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
if ( spm )
spm->EndInvocation();
if ( f->HasDelayed() ) {
if ( f.HasDelayed() ) {
assert(! result);
assert(parent);
parent->SetDelayed();
@ -424,8 +423,6 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
}
}
call_stack.pop_back();
if ( Flavor() == FUNC_FLAVOR_HOOK ) {
if ( ! result )
result = val_mgr->True();
@ -438,7 +435,7 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
// the function without an explicit return, or without a value.
else if ( GetType()->Yield() && GetType()->Yield()->Tag() != TYPE_VOID && ! GetType()->ExpressionlessReturnOkay() &&
(flow != FLOW_RETURN /* we fell off the end */ || ! result /* explicit return with no result */) &&
! f->HasDelayed() )
! f.HasDelayed() )
reporter->Warning("non-void function returning without a value: %s", GetName().c_str());
if ( result && g_trace_state.DoTrace() ) {
@ -448,7 +445,7 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const {
g_trace_state.LogTrace("Function return: %s\n", d.Description());
}
g_frame_stack.pop_back();
call_stack.pop_back();
return result;
}
@ -733,8 +730,10 @@ ValPtr BuiltinFunc::Invoke(Args* args, Frame* parent) const {
g_trace_state.LogTrace("\tBuiltin Function called: %s\n", d.Description());
}
Frame f{0, this, args};
const CallExpr* call_expr = parent ? parent->GetCall() : nullptr;
call_stack.emplace_back(CallInfo{call_expr, this, *args});
call_stack.emplace_back(call_expr, &f);
auto result = func(parent, args);
call_stack.pop_back();
@ -937,15 +936,15 @@ zeek::VectorValPtr get_current_script_backtrace() {
auto cs_copy = zeek::detail::call_stack;
for ( const auto& ci : std::ranges::reverse_view(cs_copy) ) {
if ( ! ci.func )
// This happens for compiled code.
if ( ! ci.frame->GetFunction() )
// This happens for compiled code and BIFs.
continue;
const auto& params = ci.func->GetType()->Params();
auto args = MakeCallArgumentVector(ci.args, params);
const auto& params = ci.frame->GetFunction()->GetType()->Params();
auto args = MakeCallArgumentVector(*ci.frame->GetFuncArgs(), params);
auto elem =
make_backtrace_element(ci.func->GetName(), std::move(args), ci.call ? ci.call->GetLocationInfo() : nullptr);
auto elem = make_backtrace_element(ci.frame->GetFunction()->GetName(), std::move(args),
ci.call ? ci.call->GetLocationInfo() : nullptr);
rval->Append(std::move(elem));
}
@ -990,7 +989,7 @@ static void emit_builtin_error_common(const char* msg, Obj* arg, bool unwind) {
return;
}
auto last_call = call_stack.back();
const auto& last_call = call_stack.back();
if ( call_stack.size() < 2 ) {
// Don't need to check for wrapper function like "<module>::__<func>"
@ -998,10 +997,13 @@ static void emit_builtin_error_common(const char* msg, Obj* arg, bool unwind) {
return;
}
if ( ! last_call.frame->GetFunction() )
return;
auto starts_with_double_underscore = [](const std::string& name) -> bool {
return name.size() > 2 && name[0] == '_' && name[1] == '_';
};
const std::string& last_func = last_call.func->GetName();
const std::string& last_func = last_call.frame->GetFunction()->GetName();
auto pos = last_func.find_first_of("::");
std::string wrapper_func;
@ -1027,7 +1029,10 @@ static void emit_builtin_error_common(const char* msg, Obj* arg, bool unwind) {
}
auto parent_call = call_stack[call_stack.size() - 2];
const auto& parent_func = parent_call.func->GetName();
std::string parent_func;
if ( const auto* pcf = parent_call.frame->GetFunction() )
parent_func = pcf->GetName();
if ( wrapper_func == parent_func )
emit(parent_call.call);

View file

@ -339,21 +339,16 @@ public:
void Describe(ODesc* d) const override;
protected:
BuiltinFunc() {
func = nullptr;
is_pure = false;
}
built_in_func func;
bool is_pure;
BuiltinFunc() = default;
built_in_func func = nullptr;
bool is_pure = false;
};
extern bool check_built_in_call(BuiltinFunc* f, CallExpr* call);
struct CallInfo {
const CallExpr* call;
const Func* func;
const zeek::Args& args;
const CallExpr* call = nullptr;
Frame* frame = nullptr;
};
// Class that collects all the specifics defining a Func.

View file

@ -7,6 +7,7 @@
#include "zeek/Desc.h"
#include "zeek/Expr.h"
#include "zeek/Frame.h"
#include "zeek/Func.h"
#include "zeek/IntrusivePtr.h"
#include "zeek/Reporter.h"
@ -136,7 +137,7 @@ std::string_view determine_script_location() {
ssize_t sidx = static_cast<ssize_t>(zeek::detail::call_stack.size()) - 1;
while ( sidx >= 0 ) {
const auto* func = zeek::detail::call_stack[sidx].func;
const auto* func = zeek::detail::call_stack[sidx].frame->GetFunction();
const auto* ce = zeek::detail::call_stack[sidx].call;
// Cached?

View file

@ -41,12 +41,12 @@ eval auto& v = $$;
internal-op Load-Capture
class Vi
eval $$ = Z_FRAME->GetFunction()->GetCapturesVec()[$1];
eval $$ = static_cast<const ScriptFunc*>(Z_FRAME->GetFunction())->GetCapturesVec()[$1];
internal-op Load-Managed-Capture
class Vi
eval auto& lhs = $$;
auto& rhs = Z_FRAME->GetFunction()->GetCapturesVec()[$1];
auto& rhs = static_cast<const ScriptFunc*>(Z_FRAME->GetFunction())->GetCapturesVec()[$1];
zeek::Ref(rhs.ManagedVal());
ZVal::DeleteManagedType(lhs);
lhs = rhs;
@ -62,12 +62,12 @@ eval GlobalID($1)->SetVal(GlobalVal($1).ToVal(Z_TYPE));
internal-op Store-Capture
op1-read
class Vi
eval Z_FRAME->GetFunction()->GetCapturesVec()[$2] = $1;
eval static_cast<const ScriptFunc*>(Z_FRAME->GetFunction())->GetCapturesVec()[$2] = $1;
internal-op Store-Managed-Capture
op1-read
class Vi
eval auto& lhs = Z_FRAME->GetFunction()->GetCapturesVec()[$2];
eval auto& lhs = static_cast<const ScriptFunc*>(Z_FRAME->GetFunction())->GetCapturesVec()[$2];
auto& rhs = $1;
zeek::Ref(rhs.ManagedVal());
ZVal::DeleteManagedType(lhs);

View file

@ -1054,7 +1054,7 @@ SetupResult setup(int argc, char** argv, Options* zopts) {
auto [body, scope] = get_global_stmts();
StmtFlowType flow;
Frame f(scope->Length(), nullptr, nullptr);
g_frame_stack.push_back(&f);
call_stack.emplace_back(nullptr, &f);
try {
body->Exec(&f, flow);
@ -1062,7 +1062,7 @@ SetupResult setup(int argc, char** argv, Options* zopts) {
reporter->FatalError("failed to execute script statements at top-level scope");
}
g_frame_stack.pop_back();
call_stack.pop_back();
}
clear_script_analysis();