mirror of
https://github.com/zeek/zeek.git
synced 2025-10-03 23:28:20 +00:00
Add lambda expressions with closures to Zeek.
This allows anonymous functions in Zeek to capture their closures. they do so by creating a copy of their enclosing frame and joining that with their own frame. There is no way to specify what specific items to capture from the closure like C++, nor is there a nonlocal keyword like Python. Attemptying to declare a local variable that has already been caught by the closure will error nicely. At the worst this is an inconvenience for people who are using lambdas which use the same variable names as their closures. As a result of functions copying their enclosing frames there is no way for a function with a closure to reach back up and modify the state of the frame that it was created in. This lets functions that generate functions work as expected. The function can reach back and modify its copy of the frame that it is captured in though. Implementation wise this is done by creating two new subclasses in Zeek. The first is a LambdaExpression which can be thought of as a function generator. It gathers all of the ingredients for a function at parse time, and then when evaluated creats a new version of that function with the frame it is being evaluated in as a closure. The second subclass is a ClosureFrame. This acts for most intents and purposes like a regular Frame, but it routes lookups of values to its closure as needed.
This commit is contained in:
parent
eef669f048
commit
a3001f1b2b
17 changed files with 636 additions and 52 deletions
79
src/Frame.h
79
src/Frame.h
|
@ -4,25 +4,31 @@
|
|||
#define frame_h
|
||||
|
||||
#include <vector>
|
||||
using namespace std;
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
|
||||
#include "Val.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class BroFunc;
|
||||
class Trigger;
|
||||
class CallExpr;
|
||||
class Val;
|
||||
|
||||
class Frame : public BroObj {
|
||||
public:
|
||||
Frame(int size, const BroFunc* func, const val_list *fn_args);
|
||||
// The constructed frame becomes a view of the input frame. No copying is done.
|
||||
Frame(const Frame* other);
|
||||
~Frame() override;
|
||||
|
||||
Val* NthElement(int n) { return frame[n]; }
|
||||
void SetElement(int n, Val* v)
|
||||
{
|
||||
Unref(frame[n]);
|
||||
frame[n] = v;
|
||||
}
|
||||
Val* NthElement(int n) const { return frame[n]; }
|
||||
void SetElement(int n, Val* v);
|
||||
virtual void SetElement(const ID* id, Val* v);
|
||||
|
||||
virtual Val* GetElement(ID* id) const;
|
||||
void AddElement(ID* id, Val* v);
|
||||
|
||||
void Reset(int startIdx);
|
||||
void Release();
|
||||
|
@ -49,7 +55,9 @@ public:
|
|||
bool BreakOnReturn() const { return break_on_return; }
|
||||
|
||||
// Deep-copies values.
|
||||
Frame* Clone();
|
||||
virtual Frame* Clone();
|
||||
// Only deep-copies values corresponding to requested IDs.
|
||||
Frame* SelectiveClone(id_list* selection);
|
||||
|
||||
// If the frame is run in the context of a trigger condition evaluation,
|
||||
// the trigger needs to be registered.
|
||||
|
@ -82,6 +90,61 @@ protected:
|
|||
bool delayed;
|
||||
};
|
||||
|
||||
/*
|
||||
Class that allows for lookups in both a closure frame and a regular frame
|
||||
according to a list of IDs passed into the constructor.
|
||||
|
||||
Facts:
|
||||
- A ClosureFrame is created from two frames: a closure and a regular frame.
|
||||
- ALL operations except GetElement operations operate on the regular frame.
|
||||
- A ClosureFrame requires a list of outside ID's captured by the closure.
|
||||
- Get operations on those IDs will be performed on the closure frame.
|
||||
|
||||
ClosureFrame allows functions that generate functions to be passed between
|
||||
different sized frames and still properly capture their closures. It also allows for
|
||||
cleaner handling of closures.
|
||||
*/
|
||||
class ClosureFrame : public Frame {
|
||||
public:
|
||||
ClosureFrame(Frame* closure, Frame* body,
|
||||
std::shared_ptr<id_list> outer_ids);
|
||||
~ClosureFrame() override;
|
||||
Val* GetElement(ID* id) const override;
|
||||
void SetElement(const ID* id, Val* v) override;
|
||||
Frame* Clone() override;
|
||||
|
||||
private:
|
||||
Frame* closure;
|
||||
Frame* body;
|
||||
|
||||
// Searches this frame and all sub-frame's closures for a value corresponding
|
||||
// to the id.
|
||||
static Val* GatherFromClosure(const Frame* start, const ID* id);
|
||||
// Moves through the closure frames and associates val with id.
|
||||
static void SetInClosure(Frame* start, const ID* id, Val* val);
|
||||
|
||||
// Hashes c style strings. The strings need to be null-terminated.
|
||||
struct const_char_hasher {
|
||||
size_t operator()(const char* in) const
|
||||
{
|
||||
// http://www.cse.yorku.ca/~oz/hash.html
|
||||
size_t h = 5381;
|
||||
int c;
|
||||
|
||||
while ((c = *in++))
|
||||
h = ((h << 5) + h) + c;
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
// NOTE: In a perfect world this would be best done with a trie or bloom
|
||||
// filter. We only need to check if things are NOT in the closure.
|
||||
// In reality though the size of a closure is small enough that operatons are
|
||||
// fairly quick anyway.
|
||||
std::unordered_set<const char*, const_char_hasher> closure_elements;
|
||||
};
|
||||
|
||||
extern vector<Frame*> g_frame_stack;
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue