diff --git a/src/Frame.cc b/src/Frame.cc index 6569e2a184..81b96c73ae 100644 --- a/src/Frame.cc +++ b/src/Frame.cc @@ -302,24 +302,25 @@ Frame* Frame::SelectiveClone(const IDPList& selection, ScriptFunc* func) const return other; } -broker::expected Frame::Serialize(const Frame* target, const IDPList& selection) +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; - std::unordered_map new_map; - if ( target->offset_map ) - new_map = *(target->offset_map); + OffsetMap new_map; + if ( offset_map ) + new_map = *offset_map; - for (const auto& we : selection) + for ( const auto& we : selection ) { - if ( target->IsOuterID(we) ) + if ( IsOuterID(we) ) them.append(we); else { @@ -330,18 +331,18 @@ broker::expected Frame::Serialize(const Frame* target, const IDPLi if ( them.length() ) { - if ( ! target->closure ) + if ( ! closure ) reporter->InternalError("Attempting to serialize values from a frame that does not exist."); rval.emplace_back(std::string("ClosureFrame")); - auto ids = SerializeIDList(target->outer_ids); + auto ids = SerializeIDList(outer_ids); if ( ! ids ) return broker::ec::invalid_data; rval.emplace_back(*ids); - auto serialized = Frame::Serialize(target->closure, them); + auto serialized = closure->SerializeClosureFrame(them); if ( ! serialized ) return broker::ec::invalid_data; @@ -358,7 +359,7 @@ broker::expected Frame::Serialize(const Frame* target, const IDPLi broker::vector body; - for ( int i = 0; i < target->size; ++i ) + for ( int i = 0; i < size; ++i ) body.emplace_back(broker::none()); for ( const auto& id : us ) @@ -366,10 +367,10 @@ broker::expected Frame::Serialize(const Frame* target, const IDPLi int location = id->Offset(); auto where = new_map.find(std::string(id->Name())); - if (where != new_map.end()) + if ( where != new_map.end() ) location = where->second; - const auto& val = target->frame[location].val; + const auto& val = frame[location].val; TypeTag tag = val->GetType()->Tag(); @@ -386,15 +387,38 @@ broker::expected Frame::Serialize(const Frame* target, const IDPLi return {std::move(rval)}; } -std::pair Frame::Unserialize(const broker::vector& data) +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(broker::none()); + body[i] = val_tuple; + } + + rval.emplace_back(body); + + return {std::move(rval)}; + } + +std::pair Frame::Unserialize(const broker::vector& data, + const std::vector* captures) { if ( data.size() == 0 ) return std::make_pair(true, nullptr); - IDPList outer_ids; - OffsetMap offset_map; - FramePtr closure; - auto where = data.begin(); auto has_name = broker::get_if(*where); @@ -403,6 +427,54 @@ std::pair Frame::Unserialize(const broker::vector& data) std::advance(where, 1); + if ( captures || *has_name == "CopyFrame" ) + { + ASSERT(captures && *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); @@ -428,7 +500,7 @@ std::pair Frame::Unserialize(const broker::vector& data) std::advance(where, 1); - auto closure_pair = Frame::Unserialize(*has_vec); + auto closure_pair = Frame::Unserialize(*has_vec, nullptr); if ( ! closure_pair.first ) { for ( auto& i : outer_ids ) diff --git a/src/Frame.h b/src/Frame.h index 7f5abd0e4c..33104ba78c 100644 --- a/src/Frame.h +++ b/src/Frame.h @@ -13,6 +13,7 @@ #include "zeek/ZeekList.h" // for typedef val_list #include "zeek/Obj.h" +#include "zeek/Type.h" #include "zeek/IntrusivePtr.h" #include "zeek/ZeekArgs.h" @@ -178,29 +179,42 @@ public: Frame* SelectiveClone(const IDPList& selection, ScriptFunc* func) const; /** - * Serializes the Frame into a Broker representation. - * - * Serializing a frame can be fairly non-trivial. If the frame has no - * closure the serialized frame is just a vector: + * Serializes the frame in the context of supporting the (deprecated) + * reference semantics for closures. This can be fairly non-trivial. + * If the frame itself has no closure then the serialized frame + * is a vector: * * [ "Frame", [offset_map] [serialized_values] ] * - * Where serialized_values are two element vectors. A serialized_value + * where serialized_values are two-element vectors. A serialized_value * has the result of calling broker::data_to_val on the value in the * first index, and an integer representing that value's type in the * second index. offset_map is a serialized version of the frame's * offset_map. * - * A Frame with a closure needs to serialize a little more information. - * It is serialized as: + * A reference-semantics frame with its own closure needs to + * (recursively) serialize more information: * * [ "ClosureFrame", [outer_ids], Serialize(closure), [offset_map], * [serialized_values] ] * - * @return the broker representaton, or an error if the serialization + * @return the broker representation, or an error if the serialization * failed. */ - static broker::expected Serialize(const Frame* target, const IDPList& selection); + broker::expected SerializeClosureFrame(const IDPList& selection); + + /** + * Serializes the frame in the context of supporting copy semantics + * for lambdas: + * + * [ "CopyFrame", serialized_values ] + * + * where serialized_values are two-element vectors. A serialized_value + * has the result of calling broker::data_to_val on the value in the + * first index, and an integer representing that value's type in the + * second index. + */ + broker::expected SerializeCopyFrame(); /** * Instantiates a Frame from a serialized one. @@ -208,8 +222,13 @@ public: * @return a pair in which the first item is the status of the serialization; * and the second is the unserialized frame with reference count +1, or * null if the serialization wasn't successful. + * + * The *captures* argument, if non-nil, specifies that the frame + * reflects captures with copy-semantics rather than deprecated + * reference semantics. */ - static std::pair Unserialize(const broker::vector& data); + static std::pair Unserialize(const broker::vector& data, + const std::vector* captures); /** * Sets the IDs that the frame knows offsets for. These offsets will diff --git a/src/Func.cc b/src/Func.cc index 9aff1dee42..cd19355f8d 100644 --- a/src/Func.cc +++ b/src/Func.cc @@ -596,7 +596,7 @@ void ScriptFunc::SetClosureFrame(Frame* f) bool ScriptFunc::UpdateClosure(const broker::vector& data) { - auto result = Frame::Unserialize(data); + auto result = Frame::Unserialize(data, nullptr); if ( ! result.first ) return false; @@ -615,6 +615,17 @@ bool ScriptFunc::UpdateClosure(const broker::vector& data) return true; } +bool ScriptFunc::DeserializeCaptures(const broker::vector& data) + { + auto result = Frame::Unserialize(data, GetType()->GetCaptures()); + +ASSERT(result.first); + + SetCaptures(result.second.release()); + + return true; + } + FuncPtr ScriptFunc::DoClone() { @@ -629,12 +640,22 @@ FuncPtr ScriptFunc::DoClone() other->weak_closure_ref = false; other->outer_ids = outer_ids; + if ( captures_frame ) + { + other->captures_frame = captures_frame->Clone(); + other->captures_offset_mapping = new OffsetMap; + *other->captures_offset_mapping = *captures_offset_mapping; + } + return other; } broker::expected ScriptFunc::SerializeClosure() const { - return Frame::Serialize(closure, outer_ids); + if ( captures_frame ) + return captures_frame->SerializeCopyFrame(); + else + return closure->SerializeClosureFrame(outer_ids); } void ScriptFunc::Describe(ODesc* d) const diff --git a/src/Func.h b/src/Func.h index 3338b4de71..6f5397730d 100644 --- a/src/Func.h +++ b/src/Func.h @@ -215,12 +215,19 @@ public: bool StrengthenClosureReference(Frame* f); /** - * Serializes this function's closure. + * Serializes this function's closure or capture frame. * - * @return a serialized version of the function's closure. + * @return a serialized version of the function's closure/capture frame. */ broker::expected SerializeClosure() const; + /** + * Sets the captures frame to one built from *data*. + * + * @param data a serialized frame + */ + bool DeserializeCaptures(const broker::vector& data); + void AddBody(StmtPtr new_body, const std::vector& new_inits, size_t new_frame_size, int priority) override; diff --git a/src/broker/Data.cc b/src/broker/Data.cc index ab65355ba4..15c3ec5607 100644 --- a/src/broker/Data.cc +++ b/src/broker/Data.cc @@ -395,7 +395,7 @@ struct val_converter { if ( t->Tag() != TYPE_FUNC ) return nullptr; - if ( a.size() == 2 ) // We have a closure. + if ( a.size() == 2 ) // we have a closure/capture frame { auto frame = broker::get_if(a[1]); if ( ! frame ) @@ -405,8 +405,20 @@ struct val_converter { if ( ! b ) return nullptr; - if ( ! b->UpdateClosure(*frame) ) - return nullptr; + auto copy_semantics = + b->GetType()->GetCaptures() != nullptr; + + if ( copy_semantics ) + { + if ( ! b->DeserializeCaptures(*frame) ) + return nullptr; + } + else + { + // Support for deprecated serialization. + if ( ! b->UpdateClosure(*frame) ) + return nullptr; + } } return rval;