mirror of
https://github.com/zeek/zeek.git
synced 2025-10-04 07:38:19 +00:00
changes to ScriptFunc's to track captures, and LambdaExpr's to check/manage them
This commit is contained in:
parent
955384291d
commit
627fb8616e
4 changed files with 177 additions and 3 deletions
67
src/Expr.cc
67
src/Expr.cc
|
@ -4329,6 +4329,8 @@ LambdaExpr::LambdaExpr(std::unique_ptr<function_ingredients> arg_ing,
|
||||||
|
|
||||||
SetType(ingredients->id->GetType());
|
SetType(ingredients->id->GetType());
|
||||||
|
|
||||||
|
CheckCaptures();
|
||||||
|
|
||||||
// Install a dummy version of the function globally for use only
|
// Install a dummy version of the function globally for use only
|
||||||
// when broker provides a closure.
|
// when broker provides a closure.
|
||||||
auto dummy_func = make_intrusive<ScriptFunc>(
|
auto dummy_func = make_intrusive<ScriptFunc>(
|
||||||
|
@ -4375,6 +4377,66 @@ LambdaExpr::LambdaExpr(std::unique_ptr<function_ingredients> arg_ing,
|
||||||
id->SetConst();
|
id->SetConst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LambdaExpr::CheckCaptures()
|
||||||
|
{
|
||||||
|
auto ft = type->AsFuncType();
|
||||||
|
auto captures = ft->GetCaptures();
|
||||||
|
|
||||||
|
capture_by_ref = false;
|
||||||
|
|
||||||
|
if ( ! captures )
|
||||||
|
{
|
||||||
|
if ( outer_ids.size() > 0 )
|
||||||
|
{
|
||||||
|
reporter->Warning("use of outer identifiers in lambdas without [] captures is deprecated: %s%s",
|
||||||
|
outer_ids.size() > 1 ? "e.g., " : "",
|
||||||
|
outer_ids[0]->Name());
|
||||||
|
capture_by_ref = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<const ID*> outer_is_matched;
|
||||||
|
std::set<const ID*> capture_is_matched;
|
||||||
|
|
||||||
|
for ( auto c : *captures )
|
||||||
|
{
|
||||||
|
auto cid = c->id.get();
|
||||||
|
|
||||||
|
if ( ! cid )
|
||||||
|
// This happens for undefined/inappropriate
|
||||||
|
// identifiers listed in captures. There's
|
||||||
|
// already been an error message.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ( capture_is_matched.count(cid) > 0 )
|
||||||
|
{
|
||||||
|
ExprError(util::fmt("%s listed multiple times in capture", cid->Name()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( auto id : outer_ids )
|
||||||
|
if ( cid == id )
|
||||||
|
{
|
||||||
|
outer_is_matched.insert(id);
|
||||||
|
capture_is_matched.insert(cid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( auto id : outer_ids )
|
||||||
|
if ( outer_is_matched.count(id) == 0 )
|
||||||
|
ExprError(util::fmt("%s is used inside lambda but not captured", id->Name()));
|
||||||
|
|
||||||
|
for ( auto c : *captures )
|
||||||
|
{
|
||||||
|
auto cid = c->id.get();
|
||||||
|
if ( cid && capture_is_matched.count(cid) == 0 )
|
||||||
|
ExprError(util::fmt("%s is captured but not used inside lambda", cid->Name()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scope* LambdaExpr::GetScope() const
|
Scope* LambdaExpr::GetScope() const
|
||||||
{
|
{
|
||||||
return ingredients->scope.get();
|
return ingredients->scope.get();
|
||||||
|
@ -4389,7 +4451,10 @@ ValPtr LambdaExpr::Eval(Frame* f) const
|
||||||
ingredients->frame_size,
|
ingredients->frame_size,
|
||||||
ingredients->priority);
|
ingredients->priority);
|
||||||
|
|
||||||
|
if ( capture_by_ref )
|
||||||
lamb->AddClosure(outer_ids, f);
|
lamb->AddClosure(outer_ids, f);
|
||||||
|
else
|
||||||
|
lamb->CreateCaptures(f);
|
||||||
|
|
||||||
// Set name to corresponding dummy func.
|
// Set name to corresponding dummy func.
|
||||||
// Allows for lookups by the receiver.
|
// Allows for lookups by the receiver.
|
||||||
|
@ -5060,7 +5125,9 @@ ExprPtr check_and_promote_expr(Expr* const e, zeek::Type* t)
|
||||||
IntrusivePtr{NewRef{}, e},
|
IntrusivePtr{NewRef{}, e},
|
||||||
IntrusivePtr{NewRef{}, t->AsVectorType()});
|
IntrusivePtr{NewRef{}, t->AsVectorType()});
|
||||||
|
|
||||||
|
if ( t->Tag() != TYPE_ERROR && et->Tag() != TYPE_ERROR )
|
||||||
t->Error("type clash", e);
|
t->Error("type clash", e);
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -847,9 +847,13 @@ protected:
|
||||||
void ExprDescribe(ODesc* d) const override;
|
void ExprDescribe(ODesc* d) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void CheckCaptures();
|
||||||
|
|
||||||
std::unique_ptr<function_ingredients> ingredients;
|
std::unique_ptr<function_ingredients> ingredients;
|
||||||
|
|
||||||
IDPList outer_ids;
|
IDPList outer_ids;
|
||||||
|
bool capture_by_ref; // if true, use deprecated reference semantics
|
||||||
|
|
||||||
std::string my_name;
|
std::string my_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
53
src/Func.cc
53
src/Func.cc
|
@ -319,6 +319,9 @@ ScriptFunc::~ScriptFunc()
|
||||||
{
|
{
|
||||||
if ( ! weak_closure_ref )
|
if ( ! weak_closure_ref )
|
||||||
Unref(closure);
|
Unref(closure);
|
||||||
|
|
||||||
|
delete captures_frame;
|
||||||
|
delete captures_offset_mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScriptFunc::IsPure() const
|
bool ScriptFunc::IsPure() const
|
||||||
|
@ -472,6 +475,56 @@ ValPtr ScriptFunc::Invoke(zeek::Args* args, Frame* parent) const
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptFunc::CreateCaptures(Frame* f)
|
||||||
|
{
|
||||||
|
auto captures = type->GetCaptures();
|
||||||
|
|
||||||
|
if ( ! captures )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Create a private Frame to hold the values of captured variables,
|
||||||
|
// and a mapping from those variables to their offsets in the Frame.
|
||||||
|
captures_frame = new Frame(captures->size(), this, nullptr);
|
||||||
|
captures_offset_mapping = new OffsetMap;
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
for ( auto c : *captures )
|
||||||
|
{
|
||||||
|
auto cid = c->id;
|
||||||
|
auto v = f->GetElementByID(cid);
|
||||||
|
|
||||||
|
if ( v )
|
||||||
|
{
|
||||||
|
if ( c->deep_copy || ! v->Modifiable() )
|
||||||
|
v = v->Clone();
|
||||||
|
else
|
||||||
|
v->Ref();
|
||||||
|
|
||||||
|
captures_frame->SetElement(offset, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*captures_offset_mapping)[cid->Name()] = offset;
|
||||||
|
++offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptFunc::SetCaptures(Frame* f)
|
||||||
|
{
|
||||||
|
auto captures = type->GetCaptures();
|
||||||
|
ASSERT(captures);
|
||||||
|
|
||||||
|
captures_frame = f;
|
||||||
|
captures_offset_mapping = new OffsetMap;
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
for ( auto c : *captures )
|
||||||
|
{
|
||||||
|
auto cid = c->id;
|
||||||
|
(*captures_offset_mapping)[cid->Name()] = offset;
|
||||||
|
++offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptFunc::AddBody(StmtPtr new_body,
|
void ScriptFunc::AddBody(StmtPtr new_body,
|
||||||
const std::vector<IDPtr>& new_inits,
|
const std::vector<IDPtr>& new_inits,
|
||||||
size_t new_frame_size, int priority)
|
size_t new_frame_size, int priority)
|
||||||
|
|
52
src/Func.h
52
src/Func.h
|
@ -161,7 +161,39 @@ public:
|
||||||
ValPtr Invoke(zeek::Args* args, Frame* parent) const override;
|
ValPtr Invoke(zeek::Args* args, Frame* parent) const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds adds a closure to the function. Closures are cloned and
|
* Creates a separate frame for captures and initializes its
|
||||||
|
* elements. The list of captures comes from the ScriptFunc's
|
||||||
|
* type, so doesn't need to be passed in, just the frame to
|
||||||
|
* use in evaluating the identifiers.
|
||||||
|
*
|
||||||
|
* @param f the frame used for evaluating the captured identifiers
|
||||||
|
*/
|
||||||
|
void CreateCaptures(Frame* f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the frame associated with this function for tracking
|
||||||
|
* captures, or nil if there isn't one.
|
||||||
|
*
|
||||||
|
* @return internal frame kept by the function for persisting captures
|
||||||
|
*/
|
||||||
|
Frame* GetCapturesFrame() const { return captures_frame; }
|
||||||
|
|
||||||
|
// Same definition as in Frame.h.
|
||||||
|
using OffsetMap = std::unordered_map<std::string, int>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the mapping of captures to slots in the captures frame.
|
||||||
|
*
|
||||||
|
* @return pointer to mapping of captures to slots
|
||||||
|
*/
|
||||||
|
const OffsetMap* GetCapturesOffsetMap() const
|
||||||
|
{ return captures_offset_mapping; }
|
||||||
|
|
||||||
|
// The following "Closure" methods implement the deprecated
|
||||||
|
// capture-by-reference functionality.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a closure to the function. Closures are cloned and
|
||||||
* future calls to ScriptFunc methods will not modify *f*.
|
* future calls to ScriptFunc methods will not modify *f*.
|
||||||
*
|
*
|
||||||
* @param ids IDs that are captured by the closure.
|
* @param ids IDs that are captured by the closure.
|
||||||
|
@ -218,14 +250,32 @@ protected:
|
||||||
*/
|
*/
|
||||||
void SetClosureFrame(Frame* f);
|
void SetClosureFrame(Frame* f);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the given frame for captures, and generates the
|
||||||
|
* mapping from captured variables to offsets in the frame.
|
||||||
|
*
|
||||||
|
* @param f the frame holding the values of capture variables
|
||||||
|
*/
|
||||||
|
void SetCaptures(Frame* f);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t frame_size;
|
size_t frame_size;
|
||||||
|
|
||||||
// List of the outer IDs used in the function.
|
// List of the outer IDs used in the function.
|
||||||
IDPList outer_ids;
|
IDPList outer_ids;
|
||||||
|
|
||||||
|
// The following is used for deprecated capture-by-reference
|
||||||
|
// closures:
|
||||||
// The frame the ScriptFunc was initialized in.
|
// The frame the ScriptFunc was initialized in.
|
||||||
Frame* closure = nullptr;
|
Frame* closure = nullptr;
|
||||||
bool weak_closure_ref = false;
|
bool weak_closure_ref = false;
|
||||||
|
|
||||||
|
// Used for capture-by-copy closures. These persist over the
|
||||||
|
// function's lifetime, providing quasi-globals that maintain
|
||||||
|
// state across individual calls to the function.
|
||||||
|
Frame* captures_frame = nullptr;
|
||||||
|
|
||||||
|
OffsetMap* captures_offset_mapping = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
using built_in_func = BifReturnVal (*)(Frame* frame, const Args* args);
|
using built_in_func = BifReturnVal (*)(Frame* frame, const Args* args);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue