// See the file "COPYING" in the main distribution directory for copyright. #include "zeek/Frame.h" #include #include "zeek/Desc.h" #include "zeek/Func.h" #include "zeek/ID.h" #include "zeek/Trigger.h" #include "zeek/Val.h" #include "zeek/broker/Data.h" std::vector g_frame_stack; namespace zeek::detail { Frame::Frame(int arg_size, const ScriptFunc* func, const zeek::Args* fn_args) { size = arg_size; frame = std::make_unique(size); function = func; func_args = fn_args; next_stmt = nullptr; break_before_next_stmt = false; break_on_return = false; call = nullptr; delayed = false; closure = nullptr; // 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; } Frame::~Frame() { if ( functions_with_closure_frame_reference ) { for ( auto& func : *functions_with_closure_frame_reference ) { func->StrengthenClosureReference(this); Unref(func); } } if ( ! weak_closure_ref ) Unref(closure); for ( auto& i : outer_ids ) Unref(i); for ( int i = 0; i < size; ++i ) ClearElement(i); } void Frame::AddFunctionWithClosureRef(ScriptFunc* func) { Ref(func); if ( ! functions_with_closure_frame_reference ) functions_with_closure_frame_reference = std::make_unique>(); functions_with_closure_frame_reference->emplace_back(func); } void Frame::SetElement(int n, ValPtr v) { n += current_offset; ClearElement(n); frame[n] = {std::move(v), false}; } void Frame::SetElementWeak(int n, Val* v) { n += current_offset; ClearElement(n); frame[n] = {{AdoptRef{}, v}, true}; } void Frame::SetElement(const ID* id, ValPtr v) { if ( closure && IsOuterID(id) ) { closure->SetElement(id, std::move(v)); return; } if ( captures ) { auto cap_off = captures_offset_map->find(id->Name()); if ( cap_off != captures_offset_map->end() ) { captures->SetElement(cap_off->second, std::move(v)); return; } } // do we have an offset for it? if ( offset_map && ! offset_map->empty() ) { auto where = offset_map->find(std::string(id->Name())); if ( where != offset_map->end() ) { // Need to add a Ref to 'v' since the SetElement() for // id->Offset() below is otherwise responsible for keeping track // of the implied reference count of the passed-in 'v' argument. // i.e. if we end up storing it twice, we need an addition Ref. SetElement(where->second, v); } } SetElement(id->Offset(), std::move(v)); } const ValPtr& Frame::GetElementByID(const ID* id) const { if ( closure && IsOuterID(id) ) return closure->GetElementByID(id); if ( captures ) { auto cap_off = captures_offset_map->find(id->Name()); if ( cap_off != captures_offset_map->end() ) return captures->GetElement(cap_off->second); } // do we have an offset for it? if ( offset_map && ! offset_map->empty() ) { auto where = offset_map->find(std::string(id->Name())); if ( where != offset_map->end() ) return frame[where->second + current_offset].val; } return frame[id->Offset() + current_offset].val; } void Frame::Reset(int startIdx) { if ( functions_with_closure_frame_reference ) { for ( auto& func : *functions_with_closure_frame_reference ) { // A lambda could be escaping its enclosing Frame at this point so // it needs to claim some ownership (or copy) of the Frame in // order to be of any further use. func->StrengthenClosureReference(this); Unref(func); } functions_with_closure_frame_reference.reset(); } for ( int i = startIdx + current_offset; i < size; ++i ) ClearElement(i); } void Frame::Describe(ODesc* d) const { if ( ! d->IsBinary() ) d->AddSP("frame"); if ( ! d->IsReadable() ) { d->Add(size); for ( int i = 0; i < size; ++i ) { d->Add(frame[i].val != nullptr); d->SP(); } } for ( int i = 0; i < size; ++i ) if ( frame[i].val ) frame[i].val->Describe(d); else if ( d->IsReadable() ) d->Add(""); } Frame* Frame::Clone() const { Frame* other = new Frame(size, function, func_args); if ( offset_map ) other->offset_map = std::make_unique(*offset_map); other->CaptureClosure(closure, outer_ids); other->call = call; other->trigger = trigger; for ( int i = 0; i < size; i++ ) if ( frame[i].val ) other->frame[i].val = frame[i].val->Clone(); // Note, there's no need to clone "captures" or "captures_offset_map" // since those get created fresh when constructing "other". return other; } static bool val_is_func(const ValPtr& v, ScriptFunc* func) { if ( v->GetType()->Tag() != TYPE_FUNC ) return false; return v->AsFunc() == func; } void Frame::CloneNonFuncElement(int offset, ScriptFunc* func, Frame* other) const { const auto& v = frame[offset].val; if ( ! v ) return; if ( val_is_func(v, func) ) { other->SetElementWeak(offset, v.get()); return; } auto rval = v->Clone(); other->SetElement(offset, std::move(rval)); } Frame* Frame::SelectiveClone(const IDPList& selection, ScriptFunc* func) const { if ( selection.length() == 0 ) return nullptr; IDPList us; // and IDPList them; for ( const auto& we : selection ) { if ( ! IsOuterID(we) ) us.append(we); else them.append(we); } Frame* other = new Frame(size, function, func_args); for ( const auto& id : us ) { if ( offset_map && ! offset_map->empty() ) { auto where = offset_map->find(std::string(id->Name())); if ( where != offset_map->end() ) { CloneNonFuncElement(where->second, func, other); continue; } } if ( ! frame[id->Offset() + current_offset].val ) reporter->InternalError("Attempted to clone an id ('%s') with no associated value.", id->Name()); CloneNonFuncElement(id->Offset(), func, other); } /** * What to do here depends on what the expected behavior of a copy * operation on a function with a closure is. As we let functions * mutate their closures, it seems reasonable that when cloned, the * clone should continue to mutate the same closure as the function * doesn't **own** the closure. Uncommenting the below if statement * will change that behavior such that the function also copies the * closure frame. */ // if ( closure ) // other->closure = closure->SelectiveClone(them); // other->outer_ids = outer_ids; if ( closure ) other->CaptureClosure(closure, outer_ids); if ( offset_map ) { if ( ! other->offset_map ) other->offset_map = std::make_unique(*offset_map); else *(other->offset_map) = *offset_map; } else other->offset_map.reset(); return other; } broker::expected Frame::SerializeClosureFrame(const IDPList& selection) { broker::vector rval; if ( selection.length() == 0 ) // Easy - no captures, so frame is irrelvant. return {std::move(rval)}; IDPList us; // and IDPList them; OffsetMap new_map; if ( offset_map ) new_map = *offset_map; for ( const auto& we : selection ) { if ( IsOuterID(we) ) them.append(we); else { us.append(we); new_map.insert(std::make_pair(std::string(we->Name()), we->Offset())); } } if ( them.length() ) { if ( ! closure ) reporter->InternalError( "Attempting to serialize values from a frame that does not exist."); rval.emplace_back(std::string("ClosureFrame")); auto ids = SerializeIDList(outer_ids); if ( ! ids ) return broker::ec::invalid_data; rval.emplace_back(*ids); auto serialized = closure->SerializeClosureFrame(them); if ( ! serialized ) return broker::ec::invalid_data; rval.emplace_back(*serialized); } else rval.emplace_back(std::string("Frame")); auto map = SerializeOffsetMap(new_map); if ( ! map ) return broker::ec::invalid_data; rval.emplace_back(*map); broker::vector body; for ( int i = 0; i < size; ++i ) body.emplace_back(broker::none()); for ( const auto& id : us ) { int location = id->Offset(); auto where = new_map.find(std::string(id->Name())); if ( where != new_map.end() ) location = where->second; const auto& val = frame[location].val; TypeTag tag = val->GetType()->Tag(); auto expected = Broker::detail::val_to_data(val.get()); if ( ! expected ) return broker::ec::invalid_data; broker::vector val_tuple{std::move(*expected), static_cast(tag)}; body[location] = val_tuple; } rval.emplace_back(body); return {std::move(rval)}; } broker::expected Frame::SerializeCopyFrame() { broker::vector rval; rval.emplace_back(std::string("CopyFrame")); broker::vector body; for ( int i = 0; i < size; ++i ) { const auto& val = frame[i].val; auto expected = Broker::detail::val_to_data(val.get()); if ( ! expected ) return broker::ec::invalid_data; TypeTag tag = val->GetType()->Tag(); broker::vector val_tuple{std::move(*expected), static_cast(tag)}; body.emplace_back(std::move(val_tuple)); } rval.emplace_back(std::move(body)); return {std::move(rval)}; } std::pair Frame::Unserialize(const broker::vector& data, const std::optional& captures) { if ( data.size() == 0 ) return std::make_pair(true, nullptr); auto where = data.begin(); auto has_name = broker::get_if(*where); if ( ! has_name ) return std::make_pair(false, nullptr); std::advance(where, 1); if ( captures || *has_name == "CopyFrame" ) { if ( captures ) ASSERT(*has_name == "CopyFrame"); auto has_body = broker::get_if(*where); if ( ! has_body ) return std::make_pair(false, nullptr); broker::vector body = *has_body; int frame_size = body.size(); auto rf = make_intrusive(frame_size, nullptr, nullptr); rf->closure = nullptr; for ( int i = 0; i < frame_size; ++i ) { auto has_vec = broker::get_if(body[i]); if ( ! has_vec ) continue; broker::vector val_tuple = *has_vec; if ( val_tuple.size() != 2 ) return std::make_pair(false, nullptr); auto has_type = broker::get_if(val_tuple[1]); if ( ! has_type ) return std::make_pair(false, nullptr); broker::integer g = *has_type; Type t(static_cast(g)); auto val = Broker::detail::data_to_val(std::move(val_tuple[0]), &t); if ( ! val ) return std::make_pair(false, nullptr); rf->frame[i].val = std::move(val); } return std::make_pair(true, std::move(rf)); } // Code to support deprecated semantics: IDPList outer_ids; OffsetMap offset_map; FramePtr closure; if ( *has_name == "ClosureFrame" ) { auto has_vec = broker::get_if(*where); if ( ! has_vec ) return std::make_pair(false, nullptr); std::advance(where, 1); auto list_pair = UnserializeIDList(*has_vec); if ( ! list_pair.first ) return std::make_pair(false, nullptr); outer_ids = std::move(list_pair.second); has_vec = broker::get_if(*where); if ( ! has_vec ) { for ( auto& i : outer_ids ) Unref(i); return std::make_pair(false, nullptr); } std::advance(where, 1); auto closure_pair = Frame::Unserialize(*has_vec, {}); if ( ! closure_pair.first ) { for ( auto& i : outer_ids ) Unref(i); return std::make_pair(false, nullptr); } closure = std::move(closure_pair.second); } auto has_vec = broker::get_if(*where); if ( ! has_vec ) { for ( auto& i : outer_ids ) Unref(i); return std::make_pair(false, nullptr); } std::advance(where, 1); auto map_pair = UnserializeOffsetMap(*has_vec); if ( ! map_pair.first ) { for ( auto& i : outer_ids ) Unref(i); return std::make_pair(false, nullptr); } offset_map = std::move(map_pair.second); auto has_body = broker::get_if(*where); if ( ! has_body ) { for ( auto& i : outer_ids ) Unref(i); return std::make_pair(false, nullptr); } broker::vector body = *has_body; int frame_size = body.size(); // We'll associate this frame with a function later. auto rf = make_intrusive(frame_size, nullptr, nullptr); rf->offset_map = std::make_unique(std::move(offset_map)); // Frame takes ownership of unref'ing elements in outer_ids rf->outer_ids = std::move(outer_ids); rf->closure = closure.release(); rf->weak_closure_ref = false; for ( int i = 0; i < frame_size; ++i ) { auto has_vec = broker::get_if(body[i]); if ( ! has_vec ) continue; broker::vector val_tuple = *has_vec; if ( val_tuple.size() != 2 ) return std::make_pair(false, nullptr); auto has_type = broker::get_if(val_tuple[1]); if ( ! has_type ) return std::make_pair(false, nullptr); broker::integer g = *has_type; Type t(static_cast(g)); auto val = Broker::detail::data_to_val(std::move(val_tuple[0]), &t); if ( ! val ) return std::make_pair(false, nullptr); rf->frame[i].val = std::move(val); } return std::make_pair(true, std::move(rf)); } void Frame::AddKnownOffsets(const IDPList& ids) { if ( ! offset_map ) offset_map = std::make_unique(); std::transform(ids.begin(), ids.end(), std::inserter(*offset_map, offset_map->end()), [](const ID* id) -> std::pair { return std::make_pair(std::string(id->Name()), id->Offset()); }); } void Frame::CaptureClosure(Frame* c, IDPList arg_outer_ids) { if ( closure || outer_ids.length() ) reporter->InternalError("Attempted to override a closure."); outer_ids = std::move(arg_outer_ids); for ( auto& i : outer_ids ) Ref(i); closure = c; if ( closure ) weak_closure_ref = true; /** * Want to capture closures by copy? * You'll also need to remove the Unref in the destructor. */ // if (c) closure = c->SelectiveClone(outer_ids); } const detail::Location* Frame::GetCallLocation() const { return call ? call->GetLocationInfo() : call_loc; } void Frame::SetTrigger(trigger::TriggerPtr arg_trigger) { trigger = std::move(arg_trigger); } void Frame::ClearTrigger() { trigger = nullptr; } void Frame::ClearElement(int n) { if ( frame[n].weak_ref ) frame[n].val.release(); else frame[n] = {nullptr, false}; } bool Frame::IsOuterID(const ID* in) const { return std::any_of(outer_ids.begin(), outer_ids.end(), [&in](ID* id) -> bool { return strcmp(id->Name(), in->Name()) == 0; }); } broker::expected Frame::SerializeIDList(const IDPList& in) { broker::vector rval; for ( const auto& id : in ) { // name rval.emplace_back(std::string(id->Name())); // offset rval.emplace_back(id->Offset()); } return {std::move(rval)}; } broker::expected Frame::SerializeOffsetMap(const OffsetMap& in) { broker::vector rval; std::for_each(in.begin(), in.end(), [&rval](const std::pair& e) { rval.emplace_back(e.first); rval.emplace_back(e.second); }); return {std::move(rval)}; } std::pair Frame::UnserializeIDList(const broker::vector& data) { IDPList rval; if ( data.size() % 2 != 0 ) return std::make_pair(false, std::move(rval)); auto where = data.begin(); while ( where < data.end() ) { auto has_name = broker::get_if(*where); if ( ! has_name ) { for ( auto& i : rval ) Unref(i); rval = IDPList{}; return std::make_pair(false, std::move(rval)); } std::advance(where, 1); auto has_offset = broker::get_if(*where); if ( ! has_offset ) { for ( auto& i : rval ) Unref(i); rval = IDPList{}; return std::make_pair(false, std::move(rval)); } auto* id = new ID(has_name->c_str(), SCOPE_FUNCTION, false); id->SetOffset(*has_offset); rval.push_back(id); std::advance(where, 1); } return std::make_pair(true, std::move(rval)); } std::pair> Frame::UnserializeOffsetMap(const broker::vector& data) { OffsetMap rval; for ( broker::vector::size_type i = 0; i < data.size(); i += 2 ) { auto key = broker::get_if(data[i]); if ( ! key ) return std::make_pair(false, std::move(rval)); auto offset = broker::get_if(data[i + 1]); if ( ! offset ) return std::make_pair(false, std::move(rval)); rval.insert({std::move(*key), std::move(*offset)}); } return std::make_pair(true, std::move(rval)); } }