mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
Merge remote-tracking branch 'origin/topic/vern/remove-uu'
* origin/topic/vern/remove-uu: fix up for linking w/ doc update documentation update script simplification that removes an unnecessary &is_assigned removing -uu functionality and associated script analysis now no longer needed
This commit is contained in:
commit
e5b163290d
25 changed files with 12 additions and 2622 deletions
5
CHANGES
5
CHANGES
|
@ -1,3 +1,8 @@
|
|||
4.2.0-dev.214 | 2021-09-24 10:31:34 -0700
|
||||
|
||||
* script simplification that removes an unnecessary &is_assigned (Vern Paxson, Corelight)
|
||||
|
||||
* removing -uu functionality and associated script analysis now no longer needed (Vern Paxson, Corelight)
|
||||
|
||||
4.2.0-dev.208 | 2021-09-23 17:48:13 +0200
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
4.2.0-dev.208
|
||||
4.2.0-dev.214
|
||||
|
|
2
doc
2
doc
|
@ -1 +1 @@
|
|||
Subproject commit 41fbcde395e804c1e0938b5d7022215cebd50758
|
||||
Subproject commit f562d75aef2e7c401913de122e4f888d42ae5f8e
|
|
@ -239,10 +239,7 @@ function determine_service(c: connection): string
|
|||
function set_conn(c: connection, eoc: bool)
|
||||
{
|
||||
if ( ! c?$conn )
|
||||
{
|
||||
local tmp: Info &is_assigned;
|
||||
c$conn = tmp;
|
||||
}
|
||||
c$conn = Info();
|
||||
|
||||
c$conn$ts=c$start_time;
|
||||
c$conn$uid=c$uid;
|
||||
|
|
|
@ -412,15 +412,11 @@ set(MAIN_SRCS
|
|||
|
||||
${_gen_zeek_script_cpp}
|
||||
|
||||
script_opt/DefItem.cc
|
||||
script_opt/DefSetsMgr.cc
|
||||
script_opt/Expr.cc
|
||||
script_opt/GenRDs.cc
|
||||
script_opt/GenIDDefs.cc
|
||||
script_opt/IDOptInfo.cc
|
||||
script_opt/Inline.cc
|
||||
script_opt/ProfileFunc.cc
|
||||
script_opt/ReachingDefs.cc
|
||||
script_opt/Reduce.cc
|
||||
script_opt/ScriptOpt.cc
|
||||
script_opt/Stmt.cc
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "zeek/script_opt/DefItem.h"
|
||||
|
||||
#include "zeek/Expr.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
||||
DefinitionItem::DefinitionItem(const ID* _id) : name(_id->Name())
|
||||
{
|
||||
is_id = true;
|
||||
id = _id;
|
||||
di = nullptr;
|
||||
field_name = nullptr;
|
||||
|
||||
t = id->GetType();
|
||||
|
||||
CheckForRecord();
|
||||
}
|
||||
|
||||
DefinitionItem::DefinitionItem(const DefinitionItem* _di, const char* _field_name, TypePtr _t)
|
||||
{
|
||||
is_id = false;
|
||||
id = nullptr;
|
||||
di = _di;
|
||||
field_name = _field_name;
|
||||
|
||||
t = std::move(_t);
|
||||
|
||||
name += di->Name();
|
||||
name += '$';
|
||||
name += field_name;
|
||||
|
||||
CheckForRecord();
|
||||
}
|
||||
|
||||
std::shared_ptr<DefinitionItem> DefinitionItem::FindField(const char* field) const
|
||||
{
|
||||
if ( ! IsRecord() )
|
||||
return nullptr;
|
||||
|
||||
auto offset = rt->FieldOffset(field);
|
||||
|
||||
return FindField(offset);
|
||||
}
|
||||
|
||||
std::shared_ptr<DefinitionItem> DefinitionItem::FindField(int offset) const
|
||||
{
|
||||
if ( ! IsRecord() )
|
||||
return nullptr;
|
||||
|
||||
return (*fields)[offset];
|
||||
}
|
||||
|
||||
std::shared_ptr<DefinitionItem> DefinitionItem::CreateField(const char* field, TypePtr t)
|
||||
{
|
||||
auto offset = rt->FieldOffset(field);
|
||||
|
||||
if ( (*fields)[offset] )
|
||||
return (*fields)[offset];
|
||||
|
||||
(*fields)[offset] = std::make_shared<DefinitionItem>(this, field, std::move(t));
|
||||
|
||||
return (*fields)[offset];
|
||||
}
|
||||
|
||||
std::shared_ptr<DefinitionItem> DefinitionItem::CreateField(int offset, TypePtr t)
|
||||
{
|
||||
if ( (*fields)[offset] )
|
||||
return (*fields)[offset];
|
||||
|
||||
auto field = rt->FieldName(offset);
|
||||
|
||||
(*fields)[offset] = std::make_shared<DefinitionItem>(this, field, std::move(t));
|
||||
|
||||
return (*fields)[offset];
|
||||
}
|
||||
|
||||
void DefinitionItem::CheckForRecord()
|
||||
{
|
||||
if ( ! IsRecord() )
|
||||
{
|
||||
rt = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
rt = t->AsRecordType();
|
||||
num_fields = rt->NumFields();
|
||||
fields = std::vector<std::shared_ptr<DefinitionItem>>(num_fields);
|
||||
}
|
||||
|
||||
std::shared_ptr<DefinitionItem> DefItemMap::GetExprDI(const Expr* expr)
|
||||
{
|
||||
if ( expr->Tag() == EXPR_NAME )
|
||||
{
|
||||
auto id_e = expr->AsNameExpr();
|
||||
auto id = id_e->Id();
|
||||
return GetID_DI(id);
|
||||
}
|
||||
|
||||
else if ( expr->Tag() == EXPR_FIELD )
|
||||
{
|
||||
auto f = expr->AsFieldExpr();
|
||||
auto r = f->Op();
|
||||
|
||||
auto r_def = GetExprDI(r);
|
||||
|
||||
if ( ! r_def )
|
||||
return nullptr;
|
||||
|
||||
auto field = f->FieldName();
|
||||
return r_def->FindField(field);
|
||||
}
|
||||
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<DefinitionItem> DefItemMap::GetID_DI(const ID* id)
|
||||
{
|
||||
auto di = i2d.find(id);
|
||||
if ( di == i2d.end() )
|
||||
{
|
||||
auto new_entry = std::make_shared<DefinitionItem>(id);
|
||||
i2d[id] = new_entry;
|
||||
return new_entry;
|
||||
}
|
||||
else
|
||||
return di->second;
|
||||
}
|
||||
|
||||
const DefinitionItem* DefItemMap::GetConstID_DI(const ID* id) const
|
||||
{
|
||||
auto di = i2d.find(id);
|
||||
return di == i2d.end() ? nullptr : di->second.get();
|
||||
}
|
||||
|
||||
const DefinitionItem* DefItemMap::GetConstID_DI(const DefinitionItem* di,
|
||||
const char* field_name) const
|
||||
{
|
||||
return di->FindField(field_name).get();
|
||||
}
|
||||
|
||||
} // zeek::detail
|
|
@ -1,103 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "zeek/ID.h"
|
||||
#include "zeek/Type.h"
|
||||
#include "zeek/script_opt/DefPoint.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
||||
// A definition item is a Zeek script entity that can be assigned to.
|
||||
// Currently, we track variables and record fields; the latter can
|
||||
// be nested (for example, a field that's in a record that itself is
|
||||
// a field in another record). In principle we could try to track
|
||||
// table or vector elements, but that's only going to be feasible for
|
||||
// constant indices, so presumably not much bang-for-the-buck.
|
||||
//
|
||||
// For script optimization, we only need to track variables, and we could
|
||||
// considerably simplify the code by doing so. However, there's long
|
||||
// been a desire to be able to statically determine that a record field
|
||||
// will be used without first having been set, hence we go the more
|
||||
// complicated route here.
|
||||
|
||||
class DefinitionItem
|
||||
{
|
||||
public:
|
||||
// Constructor for the simple case of tracking assignments to
|
||||
// a variable.
|
||||
DefinitionItem(const ID* _id);
|
||||
|
||||
// The more complicated case of assigning to a field in a record
|
||||
// (which itself might be a field in a record).
|
||||
DefinitionItem(const DefinitionItem* _di, const char* _field_name, TypePtr _t);
|
||||
|
||||
const char* Name() const { return name.c_str(); }
|
||||
|
||||
TypePtr GetType() const { return t; }
|
||||
bool IsRecord() const { return t->Tag() == TYPE_RECORD; }
|
||||
|
||||
// The identifier to which this item ultimately belongs.
|
||||
const ID* RootID() const { return di ? di->RootID() : id; }
|
||||
|
||||
// For this definition item, look for a field corresponding
|
||||
// to the given name or offset. Nil if the field has not (yet)
|
||||
// been created.
|
||||
std::shared_ptr<DefinitionItem> FindField(const char* field) const;
|
||||
std::shared_ptr<DefinitionItem> FindField(int offset) const;
|
||||
|
||||
// Start tracking a field in this definition item with the
|
||||
// given name or offset, returning the associated item.
|
||||
//
|
||||
// If the field already exists, then it's simply returned.
|
||||
std::shared_ptr<DefinitionItem> CreateField(const char* field, TypePtr t);
|
||||
std::shared_ptr<DefinitionItem> CreateField(int offset, TypePtr t);
|
||||
|
||||
protected:
|
||||
void CheckForRecord();
|
||||
|
||||
bool is_id;
|
||||
const ID* id;
|
||||
const DefinitionItem* di;
|
||||
const char* field_name;
|
||||
|
||||
TypePtr t;
|
||||
std::string name;
|
||||
|
||||
const RecordType* rt;
|
||||
|
||||
// If present, tracks definition items for a record's fields as
|
||||
// these are seen (i.e., as they are entered via CreateField()).
|
||||
std::optional<std::vector<std::shared_ptr<DefinitionItem>>> fields;
|
||||
int num_fields;
|
||||
};
|
||||
|
||||
// For a given identifier, locates its associated definition item.
|
||||
typedef std::unordered_map<const ID*, std::shared_ptr<DefinitionItem>> ID_to_DI_Map;
|
||||
|
||||
// Class for managing a set of IDs and their associated definition items.
|
||||
class DefItemMap
|
||||
{
|
||||
public:
|
||||
// Gets the definition for either a name or a record field reference.
|
||||
// Returns nil if "expr" lacks such a form, or if there isn't
|
||||
// any such definition.
|
||||
std::shared_ptr<DefinitionItem> GetExprDI(const Expr* expr);
|
||||
|
||||
// Returns the definition item for a given ID; creates it if
|
||||
// it doesn't already exist.
|
||||
std::shared_ptr<DefinitionItem> GetID_DI(const ID* id);
|
||||
|
||||
// Returns the definition item for a given ID, or nil if it
|
||||
// doesn't exist.
|
||||
const DefinitionItem* GetConstID_DI(const ID* id) const;
|
||||
|
||||
// The same for a record field for a given definition item.
|
||||
const DefinitionItem* GetConstID_DI(const DefinitionItem* di, const char* field_name) const;
|
||||
|
||||
protected:
|
||||
ID_to_DI_Map i2d;
|
||||
};
|
||||
|
||||
} // zeek::detail
|
|
@ -1,108 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "zeek/Expr.h"
|
||||
#include "zeek/Func.h"
|
||||
#include "zeek/Stmt.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
||||
// A DefinitionPoint is a location where a variable, or possibly a record
|
||||
// field, is defined (i.e., assigned to). The class tracks the type of
|
||||
// definition (a statement, inside an expression, an aggregate passed to
|
||||
// a function or hook, or at the start of a function).
|
||||
|
||||
enum DefPointType
|
||||
{
|
||||
// Used to capture the notion "the variable may have no definition
|
||||
// at this point" (or "has no definition", depending on whether we're
|
||||
// concerned with minimal or maximal RDs).
|
||||
NO_DEF_POINT,
|
||||
|
||||
// Assigned at the given statement.
|
||||
STMT_DEF,
|
||||
|
||||
// The following includes assignments, +=, vec+=, $, ?$ ...
|
||||
// ... plus names (for implicit creation of records upon
|
||||
// seeing use) and calls (for aggregates).
|
||||
//
|
||||
// Note that ?$ does not in fact create a definition. We include
|
||||
// it as a heuristic meaning "code after this point can assume
|
||||
// that the given record field is defined". The heuristic can
|
||||
// fail if the ?$ predicate is ultimately negated, something that
|
||||
// we don't try to identify. Basically, the idea is that if the
|
||||
// script writer is cognizant of needing to check for the existence
|
||||
// of a field, most likely they got the check correct. Any errors
|
||||
// we make in this regard only lead to mistakes in identify usage
|
||||
// problems, not in actual run-time execution.
|
||||
EXPR_DEF,
|
||||
|
||||
// The variable is assigned when the function begins executing,
|
||||
// either through an explicit initialization for a local, or because
|
||||
// it's a function parameter.
|
||||
FUNC_DEF,
|
||||
|
||||
};
|
||||
|
||||
class DefinitionPoint
|
||||
{
|
||||
public:
|
||||
DefinitionPoint()
|
||||
{
|
||||
o = nullptr;
|
||||
t = NO_DEF_POINT;
|
||||
}
|
||||
|
||||
DefinitionPoint(const Stmt* s)
|
||||
{
|
||||
o = s;
|
||||
t = STMT_DEF;
|
||||
}
|
||||
|
||||
DefinitionPoint(const Expr* e)
|
||||
{
|
||||
o = e;
|
||||
t = EXPR_DEF;
|
||||
}
|
||||
|
||||
DefinitionPoint(const Func* f)
|
||||
{
|
||||
o = f;
|
||||
t = FUNC_DEF;
|
||||
}
|
||||
|
||||
DefPointType Tag() const { return t; }
|
||||
|
||||
const Obj* OpaqueVal() const { return o; }
|
||||
|
||||
const Stmt* StmtVal() const
|
||||
{
|
||||
ASSERT(t == STMT_DEF);
|
||||
return (const Stmt*)o;
|
||||
}
|
||||
|
||||
const Expr* ExprVal() const
|
||||
{
|
||||
ASSERT(t == EXPR_DEF);
|
||||
return (const Expr*)o;
|
||||
}
|
||||
|
||||
const Func* FuncVal() const
|
||||
{
|
||||
ASSERT(t == FUNC_DEF);
|
||||
return (const Func*)o;
|
||||
}
|
||||
|
||||
bool SameAs(const DefinitionPoint& dp) const
|
||||
{
|
||||
return dp.Tag() == Tag() && dp.OpaqueVal() == OpaqueVal();
|
||||
}
|
||||
|
||||
protected:
|
||||
DefPointType t;
|
||||
const Obj* o;
|
||||
};
|
||||
|
||||
} // zeek::detail
|
|
@ -1,60 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "zeek/script_opt/DefSetsMgr.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
||||
DefSetsMgr::DefSetsMgr()
|
||||
{
|
||||
pre_min_defs = make_intrusive<ReachingDefSet>(item_map);
|
||||
post_min_defs = make_intrusive<ReachingDefSet>(item_map);
|
||||
|
||||
pre_max_defs = make_intrusive<ReachingDefSet>(item_map);
|
||||
post_max_defs = make_intrusive<ReachingDefSet>(item_map);
|
||||
}
|
||||
|
||||
void DefSetsMgr::CreatePostDef(const ID* id, DefinitionPoint dp, bool min_only)
|
||||
{
|
||||
auto di = item_map.GetID_DI(id);
|
||||
CreatePostDef(di, dp, min_only);
|
||||
}
|
||||
|
||||
void DefSetsMgr::CreatePostDef(std::shared_ptr<DefinitionItem> di, DefinitionPoint dp,
|
||||
bool min_only)
|
||||
{
|
||||
auto where = dp.OpaqueVal();
|
||||
|
||||
if ( ! post_min_defs->HasRDs(where) )
|
||||
{
|
||||
// We haven't yet started creating post RDs for this
|
||||
// statement/expression, so create them.
|
||||
auto pre = GetPreMinRDs(where);
|
||||
SetPostFromPre(where);
|
||||
}
|
||||
|
||||
if ( ! min_only && ! post_max_defs->HasRDs(where) )
|
||||
{
|
||||
auto pre = GetPreMaxRDs(where);
|
||||
SetPostFromPre(where);
|
||||
}
|
||||
|
||||
CreateDef(std::move(di), dp, false, min_only);
|
||||
}
|
||||
|
||||
void DefSetsMgr::CreateDef(std::shared_ptr<DefinitionItem> di, DefinitionPoint dp, bool is_pre,
|
||||
bool min_only)
|
||||
{
|
||||
auto where = dp.OpaqueVal();
|
||||
RDSetPtr min_defs = is_pre ? pre_min_defs : post_min_defs;
|
||||
|
||||
min_defs->AddOrReplace(where, di.get(), dp);
|
||||
|
||||
if ( min_only )
|
||||
return;
|
||||
|
||||
RDSetPtr& max_defs = is_pre ? pre_max_defs : post_max_defs;
|
||||
max_defs->AddOrReplace(where, di.get(), dp);
|
||||
}
|
||||
|
||||
} // zeek::detail
|
|
@ -1,195 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "zeek/script_opt/DefItem.h"
|
||||
#include "zeek/script_opt/DefPoint.h"
|
||||
#include "zeek/script_opt/ReachingDefs.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
||||
// Class for managing collections of reaching definitions associated
|
||||
// with AST nodes.
|
||||
//
|
||||
// Each node has "pre" RDs reflecting the reaching definitions active
|
||||
// before the node executes, and "post" RDs reflecting the state after
|
||||
// executing.
|
||||
//
|
||||
// In addition, we track both *minimal* RDs (those guaranteed to exist)
|
||||
// and *maximal* RDs (those that _could_ exist).
|
||||
//
|
||||
// To illustrate both of these notions with an example, consider this
|
||||
// scripting code:
|
||||
//
|
||||
// local x = 5;
|
||||
// if ( predicate() )
|
||||
// x = 9;
|
||||
// foobar();
|
||||
//
|
||||
// The "x = 5" node has empty pre-RDs, and minimal and maximal post-RDs
|
||||
// of {x = 5 <node-at-line-1>}. The "if" node and its interior call
|
||||
// to predicate() both inherit those post-RDs as their pre-RDs, and
|
||||
// these are also the post-RDs for predicate().
|
||||
//
|
||||
// The assignment "x = 9" inherits those post-RDs as its pre-RDs.
|
||||
// When it executes, has leaves both minimal and maximal post-RDs
|
||||
// of { x = 9 <node-at-line-3> }.
|
||||
//
|
||||
// The post-RDs for the "if" statement (and thus the pre-RDs for the foobar()
|
||||
// call) have a minimal set of { x = SOMETHING }, and a maximal set of
|
||||
// { x = 5 <node-at-line-1>, x = 9 <node-at-line-3> }. The minimal set
|
||||
// here captures the notion that "x is definitely assigned to a value
|
||||
// at this point, but it's uncertain just what that value is".
|
||||
|
||||
class DefSetsMgr
|
||||
{
|
||||
public:
|
||||
DefSetsMgr();
|
||||
|
||||
// Returns the minimal or maximal pre-RDs associated with a given node.
|
||||
RDPtr GetPreMinRDs(const Obj* o) const { return GetRDs(pre_min_defs, o); }
|
||||
RDPtr GetPreMaxRDs(const Obj* o) const { return GetRDs(pre_max_defs, o); }
|
||||
|
||||
// Same, but for post-RDs.
|
||||
RDPtr GetPostMinRDs(const Obj* o) const
|
||||
{
|
||||
if ( HasPostMinRDs(o) )
|
||||
return GetRDs(post_min_defs, o);
|
||||
else
|
||||
return GetPreMinRDs(o);
|
||||
}
|
||||
RDPtr GetPostMaxRDs(const Obj* o) const
|
||||
{
|
||||
if ( HasPostMaxRDs(o) )
|
||||
return GetRDs(post_max_defs, o);
|
||||
else
|
||||
return GetPreMaxRDs(o);
|
||||
}
|
||||
|
||||
// Initialize a node's pre-RDs to be empty.
|
||||
void SetEmptyPre(const Obj* o)
|
||||
{
|
||||
auto empty_rds = make_intrusive<ReachingDefs>();
|
||||
SetPreMinRDs(o, empty_rds);
|
||||
SetPreMaxRDs(o, empty_rds);
|
||||
}
|
||||
|
||||
// Inherit a node's pre-RDs from those of another node.
|
||||
void SetPreFromPre(const Obj* target, const Obj* source)
|
||||
{
|
||||
SetPreMinRDs(target, GetPreMinRDs(source));
|
||||
SetPreMaxRDs(target, GetPreMaxRDs(source));
|
||||
}
|
||||
|
||||
// Inherit a node's pre-RDs from the post-RDs of another node.
|
||||
void SetPreFromPost(const Obj* target, const Obj* source)
|
||||
{
|
||||
SetPreMinRDs(target, GetPostMinRDs(source));
|
||||
SetPreMaxRDs(target, GetPostMaxRDs(source));
|
||||
}
|
||||
|
||||
// Set the post-RDs for a given node to the given min/max values.
|
||||
void SetPostRDs(const Obj* o, RDPtr min_rd, RDPtr max_rd)
|
||||
{
|
||||
SetPostMinRDs(o, std::move(min_rd));
|
||||
SetPostMaxRDs(o, std::move(max_rd));
|
||||
}
|
||||
|
||||
// Propagate the node's pre-RDs to also be its post-RDs.
|
||||
void SetPostFromPre(const Obj* o)
|
||||
{
|
||||
SetPostMinRDs(o, GetPreMinRDs(o));
|
||||
SetPostMaxRDs(o, GetPreMaxRDs(o));
|
||||
}
|
||||
|
||||
// Inherit a node's post-RDs from another node's pre-RDs.
|
||||
void SetPostFromPre(const Obj* target, const Obj* source)
|
||||
{
|
||||
SetPostMinRDs(target, GetPreMinRDs(source));
|
||||
SetPostMaxRDs(target, GetPreMaxRDs(source));
|
||||
}
|
||||
|
||||
// Inherit a node's post-RDs from another node's post-RDs.
|
||||
void SetPostFromPost(const Obj* target, const Obj* source)
|
||||
{
|
||||
SetPostMinRDs(target, GetPostMinRDs(source));
|
||||
SetPostMaxRDs(target, GetPostMaxRDs(source));
|
||||
}
|
||||
|
||||
// Fine-grained control for setting RDs.
|
||||
void SetPreMinRDs(const Obj* o, RDPtr rd) { pre_min_defs->SetRDs(o, std::move(rd)); }
|
||||
void SetPreMaxRDs(const Obj* o, RDPtr rd) { pre_max_defs->SetRDs(o, std::move(rd)); }
|
||||
|
||||
void SetPostMinRDs(const Obj* o, RDPtr rd) { post_min_defs->SetRDs(o, std::move(rd)); }
|
||||
void SetPostMaxRDs(const Obj* o, RDPtr rd) { post_max_defs->SetRDs(o, std::move(rd)); }
|
||||
|
||||
// Used for confluence: add a set of RDs into those already
|
||||
// associated with a node's pre-RDs / post-RDs. Only applies
|
||||
// to maximal RDs.
|
||||
void MergeIntoPre(const Obj* o, const RDPtr& rds) { pre_max_defs->AddRDs(o, rds); }
|
||||
void MergeIntoPost(const Obj* o, const RDPtr& rds) { post_max_defs->AddRDs(o, rds); }
|
||||
|
||||
// The same, but merging a node's own maximal post-RDs into
|
||||
// its maximal pre-RDs.
|
||||
void MergePostIntoPre(const Obj* o) { MergeIntoPre(o, GetPostMaxRDs(o)); }
|
||||
|
||||
// The following predicates look up whether a given node exists
|
||||
// in the given pre/post minimal/maximal RDs.
|
||||
bool HasPreMinRDs(const Obj* o) const { return pre_min_defs && pre_min_defs->HasRDs(o); }
|
||||
bool HasPreMaxRDs(const Obj* o) const { return pre_max_defs && pre_max_defs->HasRDs(o); }
|
||||
|
||||
bool HasPostMinRDs(const Obj* o) const { return post_min_defs && post_min_defs->HasRDs(o); }
|
||||
bool HasPostMaxRDs(const Obj* o) const { return post_max_defs && post_max_defs->HasRDs(o); }
|
||||
|
||||
// True if the given node has a minimal pre-RD associated
|
||||
// with the given identifier.
|
||||
bool HasPreMinRD(const Obj* o, const ID* id) const
|
||||
{
|
||||
return pre_min_defs && pre_min_defs->HasRD(o, id);
|
||||
}
|
||||
|
||||
// True if at the given node, there's a single *unambiguous*
|
||||
// pre RD for the given identifier.
|
||||
bool HasSinglePreMinRD(const Obj* o, const ID* id) const
|
||||
{
|
||||
return pre_min_defs && pre_min_defs->HasSingleRD(o, id);
|
||||
}
|
||||
|
||||
// Methods for creating new pre/post RDs. If min_only is true,
|
||||
// then only done for minimal RDs.
|
||||
void CreatePreDef(std::shared_ptr<DefinitionItem> di, DefinitionPoint dp, bool min_only)
|
||||
{
|
||||
CreateDef(std::move(di), dp, true, min_only);
|
||||
}
|
||||
void CreatePostDef(const ID* id, DefinitionPoint dp, bool min_only);
|
||||
void CreatePostDef(std::shared_ptr<DefinitionItem> di, DefinitionPoint dp, bool min_only);
|
||||
|
||||
std::shared_ptr<DefinitionItem> GetExprDI(const Expr* e) { return item_map.GetExprDI(e); }
|
||||
std::shared_ptr<DefinitionItem> GetID_DI(const ID* id) { return item_map.GetID_DI(id); }
|
||||
const DefinitionItem* GetConstID_DI(const ID* id) const { return item_map.GetConstID_DI(id); }
|
||||
const DefinitionItem* GetConstID_DI(const DefinitionItem* di, const char* field_name) const
|
||||
{
|
||||
return item_map.GetConstID_DI(di, field_name);
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateDef(std::shared_ptr<DefinitionItem> di, DefinitionPoint dp, bool is_pre,
|
||||
bool min_only);
|
||||
|
||||
RDPtr GetRDs(const RDSetPtr& defs, const Obj* o) const { return defs->FindRDs(o); }
|
||||
|
||||
// Mappings of minimal reaching defs pre- and post- execution
|
||||
// of the given node.
|
||||
RDSetPtr pre_min_defs;
|
||||
RDSetPtr post_min_defs;
|
||||
|
||||
// Mappings of maximal reaching defs pre- and post- execution
|
||||
// of the given node.
|
||||
RDSetPtr pre_max_defs;
|
||||
RDSetPtr post_max_defs;
|
||||
|
||||
DefItemMap item_map;
|
||||
};
|
||||
|
||||
} // zeek::detail
|
File diff suppressed because it is too large
Load diff
|
@ -1,146 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
// Class for generating Reaching Definitions by traversing a function
|
||||
// body's AST.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "zeek/script_opt/DefSetsMgr.h"
|
||||
#include "zeek/script_opt/ProfileFunc.h"
|
||||
#include "zeek/script_opt/ReachingDefs.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
||||
// Helper class that tracks definitions gathered in a block that either
|
||||
// need to be propagated to the beginning of the block or to the end.
|
||||
// Used for RD propagation due to altered control flow (next/break/fallthrough).
|
||||
// Managed as a stack (vector) to deal with nested loops, switches, etc.
|
||||
// Only applies to gathering maximum RDs.
|
||||
class BlockDefs
|
||||
{
|
||||
public:
|
||||
BlockDefs(bool _is_case) { is_case = _is_case; }
|
||||
|
||||
void AddPreRDs(RDPtr RDs) { pre_RDs.push_back(std::move(RDs)); }
|
||||
void AddPostRDs(RDPtr RDs) { post_RDs.push_back(std::move(RDs)); }
|
||||
void AddFutureRDs(RDPtr RDs) { future_RDs.push_back(std::move(RDs)); }
|
||||
|
||||
const std::vector<RDPtr>& PreRDs() const { return pre_RDs; }
|
||||
const std::vector<RDPtr>& PostRDs() const { return post_RDs; }
|
||||
const std::vector<RDPtr>& FutureRDs() const { return future_RDs; }
|
||||
|
||||
void Clear()
|
||||
{
|
||||
pre_RDs.clear();
|
||||
post_RDs.clear();
|
||||
future_RDs.clear();
|
||||
}
|
||||
|
||||
bool IsCase() const { return is_case; }
|
||||
|
||||
private:
|
||||
std::vector<RDPtr> pre_RDs;
|
||||
std::vector<RDPtr> post_RDs;
|
||||
std::vector<RDPtr> future_RDs; // RDs for next case block
|
||||
|
||||
// Whether this block is for a switch case. If not,
|
||||
// it's for a loop body.
|
||||
bool is_case;
|
||||
};
|
||||
|
||||
class RD_Decorate : public TraversalCallback
|
||||
{
|
||||
public:
|
||||
RD_Decorate(std::shared_ptr<ProfileFunc> _pf, const Func* f, ScopePtr scope, StmtPtr body);
|
||||
|
||||
const DefSetsMgr* GetDefSetsMgr() const { return &mgr; }
|
||||
|
||||
private:
|
||||
// Traverses the given function body, using the first two
|
||||
// arguments for context.
|
||||
void TraverseFunction(const Func* f, ScopePtr scope, StmtPtr body);
|
||||
|
||||
TraversalCode PreStmt(const Stmt*) override;
|
||||
TraversalCode PostStmt(const Stmt*) override;
|
||||
TraversalCode PreExpr(const Expr*) override;
|
||||
TraversalCode PostExpr(const Expr*) override;
|
||||
|
||||
// The following implement various types of "confluence", i.e.,
|
||||
// situations in which control flow merges from multiple possible
|
||||
// paths to a given point.
|
||||
void TraverseSwitch(const SwitchStmt* sw);
|
||||
void DoIfStmtConfluence(const IfStmt* i);
|
||||
void DoLoopConfluence(const Stmt* s, const Stmt* top, const Stmt* body);
|
||||
|
||||
// Analyzes the target of an assignment. Returns true if the LHS
|
||||
// was an expression for which we can track it as a definition
|
||||
// (e.g., assignments to variables or record fields, but not to
|
||||
// table or vector elements).
|
||||
bool CheckLHS(const Expr* lhs, const Expr* a);
|
||||
|
||||
// True if the given expression directly represents an aggregate.
|
||||
bool IsAggr(const Expr* e) const;
|
||||
|
||||
// Checks for whether the given identifier present in the given
|
||||
// expression is undefined at that point, per the associated RDs.
|
||||
// If check_fields is true, then we check the fields of records
|
||||
// in addition to the record itself.
|
||||
void CheckVar(const Expr* e, const ID* id, bool check_fields);
|
||||
|
||||
// The following enable tracking of either identifiers or
|
||||
// record fields before/after the given definition point.
|
||||
void CreateInitPreDef(const ID* id, DefinitionPoint dp);
|
||||
void CreateInitPostDef(const ID* id, DefinitionPoint dp, bool assume_full, const Expr* rhs);
|
||||
void CreateInitPostDef(std::shared_ptr<DefinitionItem> di, DefinitionPoint dp, bool assume_full,
|
||||
const Expr* rhs);
|
||||
void CreateInitDef(std::shared_ptr<DefinitionItem> di, DefinitionPoint dp, bool is_pre,
|
||||
bool assume_full, const Expr* rhs);
|
||||
|
||||
// Helper functions for generating RDs associated with record
|
||||
// fields.
|
||||
void CreateRecordRDs(std::shared_ptr<DefinitionItem> di, DefinitionPoint dp, bool assume_full,
|
||||
const DefinitionItem* rhs_di)
|
||||
{
|
||||
CreateRecordRDs(std::move(di), dp, false, assume_full, rhs_di);
|
||||
}
|
||||
void CreateRecordRDs(std::shared_ptr<DefinitionItem> di, DefinitionPoint dp, bool is_pre,
|
||||
bool assume_full, const DefinitionItem* rhs_di);
|
||||
void CheckRecordRDs(std::shared_ptr<DefinitionItem> di, DefinitionPoint dp,
|
||||
const RDPtr& pre_rds, const Obj* o);
|
||||
|
||||
void CreateEmptyPostRDs(const Stmt* s);
|
||||
|
||||
// Helper function for tracking block definitions, i.e., those
|
||||
// associated with loops or switches. We always track the
|
||||
// maximal "pre" RDs for the given statement. If is_pre is
|
||||
// true, then we track them as RDs to propagate to the beginning
|
||||
// of the block. Otherwise, they are to propagate to the end
|
||||
// of the block; and, if is_future is true, then also to the
|
||||
// beginning of the next block (used for "fallthrough" switch
|
||||
// blocks).
|
||||
//
|
||||
// is_case specifies whether we are adding definitions associated
|
||||
// with a switch case.
|
||||
void AddBlockDefs(const Stmt* s, bool is_pre, bool is_future, bool is_case);
|
||||
|
||||
// Profile for the function. Currently, all we actually need from
|
||||
// this is the list of globals.
|
||||
std::shared_ptr<ProfileFunc> pf;
|
||||
|
||||
// Whether the Func is an event/hook/function. We currently only
|
||||
// need to know whether it's a hook, so we correctly interpret an
|
||||
// outer "break" in that context.
|
||||
FunctionFlavor func_flavor;
|
||||
|
||||
// Manager for the associated pre/post minimal/maximal RDs.
|
||||
DefSetsMgr mgr;
|
||||
|
||||
// A stack of definitions associated with (potentially nested) loop
|
||||
// and switch blocks.
|
||||
std::vector<std::unique_ptr<BlockDefs>> block_defs;
|
||||
};
|
||||
|
||||
} // zeek::detail
|
|
@ -1,211 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "zeek/script_opt/ReachingDefs.h"
|
||||
|
||||
#include "zeek/Desc.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
||||
ReachingDefs::ReachingDefs()
|
||||
{
|
||||
my_rd_map = std::make_shared<ReachingDefsMap>();
|
||||
const_rd_map = nullptr;
|
||||
}
|
||||
|
||||
ReachingDefs::ReachingDefs(RDPtr rd)
|
||||
{
|
||||
const_rd_map = rd->RDMap();
|
||||
my_rd_map = nullptr;
|
||||
}
|
||||
|
||||
void ReachingDefs::AddRD(const DefinitionItem* di, const DefinitionPoint& dp)
|
||||
{
|
||||
auto points = FindItem(di);
|
||||
|
||||
if ( points && HasPoint(dp, *points) )
|
||||
return;
|
||||
|
||||
if ( ! my_rd_map )
|
||||
{
|
||||
CopyMap();
|
||||
points = FindItem(di);
|
||||
}
|
||||
|
||||
if ( points )
|
||||
points->push_back(dp);
|
||||
else
|
||||
my_rd_map->emplace(di, std::make_unique<DefPoints>(&dp, 1));
|
||||
}
|
||||
|
||||
void ReachingDefs::AddOrFullyReplace(const DefinitionItem* di, const DefinitionPoint& dp)
|
||||
{
|
||||
CopyMapIfNeeded();
|
||||
|
||||
auto curr_defs = my_rd_map->find(di);
|
||||
|
||||
if ( curr_defs != my_rd_map->end() )
|
||||
my_rd_map->erase(curr_defs);
|
||||
|
||||
AddRD(di, dp);
|
||||
}
|
||||
|
||||
RDPtr ReachingDefs::Intersect(const RDPtr& r) const
|
||||
{
|
||||
// The following is used when there are different definitions for
|
||||
// the same item in the intersection, as a way to capture "it will
|
||||
// be defined", but not providing a specific point-of-definition
|
||||
// (since it's ambiguous which one in particular to use).
|
||||
static DefinitionPoint multi_dps;
|
||||
|
||||
auto res = make_intrusive<ReachingDefs>();
|
||||
|
||||
for ( const auto& i : *RDMap() )
|
||||
for ( const auto& dp : *i.second )
|
||||
{
|
||||
if ( r->HasPair(i.first, dp) )
|
||||
// Same definition present in both.
|
||||
res->AddRD(i.first, dp);
|
||||
|
||||
else if ( r->HasDI(i.first) )
|
||||
// There's a definition in r, just not the same
|
||||
// one. Mark as present-but-not-specific.
|
||||
res->AddRD(i.first, multi_dps);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
RDPtr ReachingDefs::Union(const RDPtr& r) const
|
||||
{
|
||||
auto res = make_intrusive<ReachingDefs>();
|
||||
|
||||
res->AddRDs(r);
|
||||
|
||||
for ( const auto& i : *RDMap() )
|
||||
for ( const auto& dp : *i.second )
|
||||
res->AddRD(i.first, dp);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
RDPtr ReachingDefs::IntersectWithConsolidation(const RDPtr& r, const DefinitionPoint& di) const
|
||||
{
|
||||
// Same notion as for the Intersect method.
|
||||
static DefinitionPoint multi_dps;
|
||||
|
||||
auto res = make_intrusive<ReachingDefs>();
|
||||
|
||||
for ( const auto& i : *RDMap() )
|
||||
for ( const auto& dp : *i.second )
|
||||
{
|
||||
if ( r->HasPair(i.first, dp) )
|
||||
// Item and definition point are shared,
|
||||
// include in result.
|
||||
res->AddRD(i.first, dp);
|
||||
else
|
||||
// Regardless of whether r has the item,
|
||||
// treat it as such and capture this as
|
||||
// a multi-definition-point definition.
|
||||
res->AddRD(i.first, multi_dps);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ReachingDefs::HasPair(const DefinitionItem* di, const DefinitionPoint& dp) const
|
||||
{
|
||||
auto points = FindItem(di);
|
||||
return points && HasPoint(dp, *points);
|
||||
}
|
||||
|
||||
DefPoints* ReachingDefs::FindItem(const DefinitionItem* di) const
|
||||
{
|
||||
const auto& map = RDMap();
|
||||
auto it = map->find(di);
|
||||
|
||||
if ( it == map->end() )
|
||||
return nullptr;
|
||||
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
bool ReachingDefs::HasPoint(const DefinitionPoint& dp, const DefPoints& dps) const
|
||||
{
|
||||
for ( const auto& l_dp : dps )
|
||||
if ( l_dp.SameAs(dp) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReachingDefs::AddRDs(const std::shared_ptr<ReachingDefsMap>& rd_m)
|
||||
{
|
||||
for ( const auto& one_rd : *rd_m )
|
||||
for ( const auto& dp : *one_rd.second )
|
||||
AddRD(one_rd.first, dp);
|
||||
}
|
||||
|
||||
void ReachingDefs::CopyMap()
|
||||
{
|
||||
my_rd_map = std::make_shared<ReachingDefsMap>();
|
||||
auto old_const_rd_map = std::move(const_rd_map);
|
||||
const_rd_map = nullptr;
|
||||
AddRDs(old_const_rd_map);
|
||||
}
|
||||
|
||||
void ReachingDefs::Dump() const
|
||||
{
|
||||
DumpMap(RDMap());
|
||||
}
|
||||
|
||||
void ReachingDefs::DumpMap(const std::shared_ptr<ReachingDefsMap>& map) const
|
||||
{
|
||||
printf("%d RD element%s: ", Size(), Size() == 1 ? "" : "s");
|
||||
|
||||
int n = 0;
|
||||
for ( auto r = map->begin(); r != map->end(); ++r )
|
||||
{
|
||||
if ( ++n > 1 )
|
||||
printf(", ");
|
||||
|
||||
PrintRD(r->first, r->second.get());
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void ReachingDefs::PrintRD(const DefinitionItem* di, const DefPoints* dps) const
|
||||
{
|
||||
printf("%s (", di->Name());
|
||||
|
||||
loop_over_list(*dps, i)
|
||||
{
|
||||
if ( i > 0 )
|
||||
printf(",");
|
||||
printf("%lx", (unsigned long)(*dps)[i].OpaqueVal());
|
||||
}
|
||||
|
||||
printf(")");
|
||||
}
|
||||
|
||||
const RDPtr& ReachingDefSet::FindRDs(const Obj* o) const
|
||||
{
|
||||
auto rd = a_i.find(o);
|
||||
if ( rd == a_i.end() )
|
||||
{
|
||||
static RDPtr empty_rds;
|
||||
return empty_rds;
|
||||
}
|
||||
|
||||
return rd->second;
|
||||
}
|
||||
|
||||
void ReachingDefSet::AddOrReplace(const Obj* o, const DefinitionItem* di, const DefinitionPoint& dp)
|
||||
{
|
||||
auto rd = a_i.find(o);
|
||||
ASSERT(rd != a_i.end());
|
||||
rd->second->AddOrFullyReplace(di, dp);
|
||||
}
|
||||
|
||||
} // zeek::detail
|
|
@ -1,246 +0,0 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "zeek/script_opt/DefItem.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
||||
// Maps a DefinitionItem (i.e., a variable or a record field within a
|
||||
// DefinitionItem) to a list of all the points where the item is defined.
|
||||
//
|
||||
// Those points are specific to a given location in a script (see
|
||||
// AnalyInfo below). For example, right after an assignment to a variable,
|
||||
// it will have exactly one associated point (the assignment). But at
|
||||
// other points there can be more than one reaching definition; for example,
|
||||
// a variable defined in both branches of an if-else will cause the location
|
||||
// in the script after the if-else statement to have both of those definitions
|
||||
// as (maximally) reaching.
|
||||
|
||||
typedef List<DefinitionPoint> DefPoints;
|
||||
typedef std::unordered_map<const DefinitionItem*, std::unique_ptr<DefPoints>> ReachingDefsMap;
|
||||
|
||||
// The ReachingDefs class tracks all of the RDs associated with a given
|
||||
// AST node. Often these are the same as for the node's predecessor, so
|
||||
// the class allows for either have a distinct set of RDs or instead
|
||||
// pointing to the predecessor's RDs.
|
||||
|
||||
class ReachingDefs;
|
||||
class ReachingDefSet;
|
||||
using RDPtr = IntrusivePtr<ReachingDefs>;
|
||||
using RDSetPtr = IntrusivePtr<ReachingDefSet>;
|
||||
|
||||
class ReachingDefs : public Obj
|
||||
{
|
||||
public:
|
||||
// Create a new object from scratch.
|
||||
ReachingDefs();
|
||||
|
||||
// Create a new object, using the RDs from another object.
|
||||
ReachingDefs(RDPtr rd);
|
||||
|
||||
// Add in all the definition points from rd into our set, if
|
||||
// we don't already have them.
|
||||
void AddRDs(const RDPtr& rd) { AddRDs(rd->RDMap()); }
|
||||
|
||||
// Add in a single definition pair, creating the entry for
|
||||
// the item if necessary.
|
||||
void AddRD(const DefinitionItem* di, const DefinitionPoint& dp);
|
||||
|
||||
// Add a single definition pair, if missing. If present,
|
||||
// replace everything currently associated with new definition.
|
||||
void AddOrFullyReplace(const DefinitionItem* di, const DefinitionPoint& dp);
|
||||
|
||||
// True if the given definition item is present in our RDs.
|
||||
bool HasDI(const DefinitionItem* di) const
|
||||
{
|
||||
const auto& map = RDMap();
|
||||
return map->find(di) != map->end();
|
||||
}
|
||||
|
||||
// For the given definition item, returns all of its definition
|
||||
// points at our location in the AST.
|
||||
DefPoints* GetDefPoints(const DefinitionItem* di)
|
||||
{
|
||||
const auto& map = RDMap();
|
||||
auto dps = map->find(di);
|
||||
return dps == map->end() ? nullptr : dps->second.get();
|
||||
}
|
||||
|
||||
// Returns true if two sets of definition points are equivalent,
|
||||
// *including ordering*.
|
||||
bool SameDefPoints(const DefPoints* dp1, const DefPoints* dp2) const
|
||||
{
|
||||
if ( ! dp1 || ! dp2 )
|
||||
return ! dp1 && ! dp2;
|
||||
|
||||
if ( dp1->length() != dp2->length() )
|
||||
return false;
|
||||
|
||||
for ( auto i = 0; i < dp1->length(); ++i )
|
||||
if ( ! (*dp1)[i].SameAs((*dp2)[i]) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return a new object representing the intersection/union of
|
||||
// this object's RDs and those of another.
|
||||
RDPtr Intersect(const RDPtr& r) const;
|
||||
RDPtr Union(const RDPtr& r) const;
|
||||
|
||||
// The following intersects this RD with another, but for
|
||||
// DefinitionItem's that have different DefPoints, rather than
|
||||
// just fully omitting them (which is what Intersect() will do),
|
||||
// creates a joint entry with a special DefinitionPoint
|
||||
// corresponding to "multiple definitions". This allows
|
||||
// minimal RDs to capture the notions (1) "yes, that value will
|
||||
// be defined at this point", but also (2) "however, we can't
|
||||
// rely on which definition reaches".
|
||||
//
|
||||
// We also do this for items *not* present in r. The reason is
|
||||
// that this method is only called (1) when we know that the items
|
||||
// in r have control flow to di, and (2) for "this" being minimal
|
||||
// RDs that were present going into the block that resulted in r.
|
||||
// Thus, those minimal values will always be present at di; they
|
||||
// might not be in r due to the way that r is computed. (For
|
||||
// example, computing them correctly for "for" loop bodies is
|
||||
// messy, and irrevant in terms of actually having the correct
|
||||
// values for them.)
|
||||
RDPtr IntersectWithConsolidation(const RDPtr& r, const DefinitionPoint& di) const;
|
||||
|
||||
int Size() const { return RDMap()->size(); }
|
||||
|
||||
// Print out the RDs, for debugging purposes.
|
||||
void Dump() const;
|
||||
void DumpMap(const std::shared_ptr<ReachingDefsMap>& map) const;
|
||||
|
||||
protected:
|
||||
// True if our RDs include the given definition item, defined at
|
||||
// the given definition point.
|
||||
bool HasPair(const DefinitionItem* di, const DefinitionPoint& dp) const;
|
||||
|
||||
DefPoints* FindItem(const DefinitionItem* di) const;
|
||||
|
||||
bool HasPoint(const DefinitionPoint&, const DefPoints& dps) const;
|
||||
|
||||
// Adds in the given RDs if we don't already have them.
|
||||
void AddRDs(const std::shared_ptr<ReachingDefsMap>& rd_m);
|
||||
|
||||
const std::shared_ptr<ReachingDefsMap>& RDMap() const
|
||||
{
|
||||
return my_rd_map ? my_rd_map : const_rd_map;
|
||||
}
|
||||
|
||||
// If we don't already have our own map, copy the one we're using
|
||||
// so that we then do.
|
||||
void CopyMapIfNeeded()
|
||||
{
|
||||
if ( ! my_rd_map )
|
||||
CopyMap();
|
||||
}
|
||||
void CopyMap();
|
||||
|
||||
void PrintRD(const DefinitionItem* di, const DefPoints* dp) const;
|
||||
void PrintRD(const DefinitionItem* di, const DefinitionPoint& dp) const;
|
||||
|
||||
// If my_rd_map is non-nil, then we use that map. Otherwise,
|
||||
// we use the map that const_rd_map points to. The "const" in
|
||||
// the latter's name is a reminder to not make any changes to
|
||||
// that map.
|
||||
std::shared_ptr<ReachingDefsMap> my_rd_map;
|
||||
std::shared_ptr<ReachingDefsMap> const_rd_map;
|
||||
};
|
||||
|
||||
// Maps script locations (which are represented by their underlying Obj
|
||||
// pointers) to the reaching definitions for that particular point.
|
||||
//
|
||||
// In spirit, the inverse of ReachingDefsMap.
|
||||
typedef std::unordered_map<const Obj*, RDPtr> AnalyInfo;
|
||||
|
||||
// Reaching definitions associated with a collection of Obj's.
|
||||
class ReachingDefSet : public Obj
|
||||
{
|
||||
public:
|
||||
ReachingDefSet(DefItemMap& _item_map) : item_map(_item_map) { }
|
||||
|
||||
// Whether in our collection we have RDs associated with the
|
||||
// given AST node.
|
||||
bool HasRDs(const Obj* o) const { return a_i.count(o) > 0; }
|
||||
|
||||
// Whether in our collection we have RDs associated with the
|
||||
// given variable.
|
||||
bool HasRD(const Obj* o, const ID* id) const { return HasRD(o, item_map.GetConstID_DI(id)); }
|
||||
|
||||
// Whether the given variable has a single = unambiguous RD
|
||||
// at the given AST node.
|
||||
bool HasSingleRD(const Obj* o, const ID* id) const
|
||||
{
|
||||
auto RDs = a_i.find(o);
|
||||
if ( RDs == a_i.end() )
|
||||
return false;
|
||||
|
||||
auto di = item_map.GetConstID_DI(id);
|
||||
auto dps = RDs->second->GetDefPoints(di);
|
||||
|
||||
if ( ! dps || dps->length() != 1 )
|
||||
return false;
|
||||
|
||||
return (*dps)[0].Tag() != NO_DEF_POINT;
|
||||
}
|
||||
|
||||
// Whether the given definition item has an RD at the given
|
||||
// AST node.
|
||||
bool HasRD(const Obj* o, const DefinitionItem* di) const
|
||||
{
|
||||
auto RDs = a_i.find(o);
|
||||
if ( RDs == a_i.end() )
|
||||
return false;
|
||||
|
||||
return RDs->second->HasDI(di);
|
||||
}
|
||||
|
||||
// Returns the RDs associated with a given AST node, if any.
|
||||
// If none are, returns an empty ReachingDef object.
|
||||
const RDPtr& FindRDs(const Obj* o) const;
|
||||
|
||||
// Associates the given RDs with the given AST node.
|
||||
void SetRDs(const Obj* o, RDPtr rd)
|
||||
{
|
||||
auto new_rd = make_intrusive<ReachingDefs>(std::move(rd));
|
||||
a_i[o] = new_rd;
|
||||
}
|
||||
|
||||
// If the given di is new, add this definition. If it
|
||||
// already exists, replace *all* of its reaching definitions
|
||||
// with this new one.
|
||||
void AddOrReplace(const Obj* o, const DefinitionItem* di, const DefinitionPoint& dp);
|
||||
|
||||
// Add the given RDs to those associated with the AST node 'o'.
|
||||
void AddRDs(const Obj* o, const RDPtr& rd)
|
||||
{
|
||||
if ( HasRDs(o) )
|
||||
MergeRDs(o, rd);
|
||||
else
|
||||
a_i[o] = rd;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Merge in the given RDs with those associated with o's.
|
||||
//
|
||||
// The caller needs to ensure that we're already tracking the
|
||||
// RDs of 'o'.
|
||||
void MergeRDs(const Obj* o, const RDPtr& rd)
|
||||
{
|
||||
auto curr_rds = a_i.find(o)->second;
|
||||
curr_rds->AddRDs(rd);
|
||||
}
|
||||
|
||||
AnalyInfo a_i;
|
||||
DefItemMap& item_map;
|
||||
};
|
||||
|
||||
} // zeek::detail
|
|
@ -11,7 +11,6 @@
|
|||
#include "zeek/script_opt/CPP/Compile.h"
|
||||
#include "zeek/script_opt/CPP/Func.h"
|
||||
#include "zeek/script_opt/GenIDDefs.h"
|
||||
#include "zeek/script_opt/GenRDs.h"
|
||||
#include "zeek/script_opt/Inline.h"
|
||||
#include "zeek/script_opt/ProfileFunc.h"
|
||||
#include "zeek/script_opt/Reduce.h"
|
||||
|
@ -144,12 +143,6 @@ static void optimize_func(ScriptFunc* f, std::shared_ptr<ProfileFunc> pf, ScopeP
|
|||
f->ReplaceBody(body, new_body);
|
||||
body = new_body;
|
||||
|
||||
if ( analysis_options.usage_issues > 1 )
|
||||
{
|
||||
// Use the old-school approach for this.
|
||||
RD_Decorate reduced_rds(pf, f, scope, body);
|
||||
}
|
||||
|
||||
if ( analysis_options.optimize_AST && ! optimize_AST(f, pf, rc, scope, body) )
|
||||
{
|
||||
pop_scope();
|
||||
|
@ -268,7 +261,7 @@ static void init_options()
|
|||
auto usage = getenv("ZEEK_USAGE_ISSUES");
|
||||
|
||||
if ( usage )
|
||||
analysis_options.usage_issues = atoi(usage) > 1 ? 2 : 1;
|
||||
analysis_options.usage_issues = 1;
|
||||
|
||||
if ( ! analysis_options.only_func )
|
||||
{
|
||||
|
|
|
@ -78,11 +78,9 @@ struct AnalyOpt
|
|||
bool dump_ZAM = false;
|
||||
|
||||
// If non-zero, looks for variables that are used-but-possibly-not-set,
|
||||
// or set-but-not-used.
|
||||
//
|
||||
// If > 1, also reports on uses of uninitialized record fields and
|
||||
// analyzes nested records in depth. Warning: with the current
|
||||
// data structures this greatly increases analysis time.
|
||||
// or set-but-not-used. We store this as an int rather than a bool
|
||||
// because we might at some point extend the analysis to deeper forms
|
||||
// of usage issues, such as those present in record fields.
|
||||
//
|
||||
// Included here with other ZAM-related options since conducting
|
||||
// the analysis requires activating some of the machinery used
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "zeek/Expr.h"
|
||||
#include "zeek/ID.h"
|
||||
#include "zeek/script_opt/IDOptInfo.h"
|
||||
#include "zeek/script_opt/ReachingDefs.h"
|
||||
|
||||
namespace zeek::detail
|
||||
{
|
||||
|
@ -43,9 +42,6 @@ public:
|
|||
IDPtr Alias() const { return alias; }
|
||||
void SetAlias(IDPtr id);
|
||||
|
||||
const RDPtr& MaxRDs() const { return max_rds; }
|
||||
void SetMaxRDs(RDPtr rds) { max_rds = std::move(rds); }
|
||||
|
||||
protected:
|
||||
std::string name;
|
||||
IDPtr id;
|
||||
|
@ -53,7 +49,6 @@ protected:
|
|||
ExprPtr rhs;
|
||||
bool active = true;
|
||||
IDPtr alias;
|
||||
RDPtr max_rds;
|
||||
};
|
||||
|
||||
} // zeek::detail
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
|
@ -1,6 +0,0 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]
|
||||
[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]
|
||||
[no_worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>], worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]]
|
||||
[no_worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>], worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]]
|
||||
T
|
|
@ -1,4 +0,0 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
warning: "-O optimize-AST" option is incompatible with -u option, deactivating optimization
|
||||
warning in <...>/uninitialized-local3.zeek, line 40: possibly used without definition (x4)
|
||||
expression error in <...>/uninitialized-local3.zeek, line 40: value used but not set (x4)
|
|
@ -1,10 +0,0 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
x$a (x <...>/uninitialized-local3.zeek, line 22) possibly used without being set
|
||||
x$e (x <...>/uninitialized-local3.zeek, line 22) possibly used without being set
|
||||
x$e (x <...>/uninitialized-local3.zeek, line 26) possibly used without being set
|
||||
x2$worries$a (x2 <...>/uninitialized-local3.zeek, line 29) possibly used without being set
|
||||
x2$worries$e (x2 <...>/uninitialized-local3.zeek, line 29) possibly used without being set
|
||||
[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]
|
||||
[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]
|
||||
[no_worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>], worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]]
|
||||
[no_worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>], worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]]
|
|
@ -1,3 +0,0 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
warning in <...>/uninitialized-local3.zeek, line 40: possibly used without definition (x4)
|
||||
expression error in <...>/uninitialized-local3.zeek, line 40: value used but not set (x4)
|
|
@ -1,10 +0,0 @@
|
|||
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||
x$a (x <...>/uninitialized-local3.zeek, line 22) possibly used without being set
|
||||
x$e (x <...>/uninitialized-local3.zeek, line 22) possibly used without being set
|
||||
x$e (x <...>/uninitialized-local3.zeek, line 26) possibly used without being set
|
||||
x2$worries$a (x2 <...>/uninitialized-local3.zeek, line 29) possibly used without being set
|
||||
x2$worries$e (x2 <...>/uninitialized-local3.zeek, line 29) possibly used without being set
|
||||
[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]
|
||||
[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]
|
||||
[no_worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>], worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]]
|
||||
[no_worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>], worries=[a=<uninitialized>, b=<uninitialized>, c=9, d=<uninitialized>, e=<uninitialized>]]
|
|
@ -79,13 +79,6 @@ ZEEK_XFORM=1
|
|||
ZEEK_USAGE_ISSUES=1
|
||||
BTEST_BASELINE_DIR=%(testbase)s/Baseline.usage:%(testbase)s/Baseline.xform:%(testbase)s/Baseline
|
||||
|
||||
# The same, but for -uu (which supercedes -u functionality). We keep them
|
||||
# separate because -uu is a lot slower.
|
||||
[environment-usage2]
|
||||
ZEEK_XFORM=1
|
||||
ZEEK_USAGE_ISSUES=2
|
||||
BTEST_BASELINE_DIR=%(testbase)s/Baseline.usage:%(testbase)s/Baseline.xform:%(testbase)s/Baseline
|
||||
|
||||
[environment-cpp]
|
||||
ZEEK_USE_CPP=1
|
||||
BTEST_BASELINE_DIR=%(testbase)s/Baseline.cpp:%(testbase)s/Baseline
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
# @TEST-REQUIRES: test "${ZEEK_ZAM}" != "1"
|
||||
# @TEST-EXEC: ZEEK_USAGE_ISSUES=2 zeek -b %INPUT >out 2>err
|
||||
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff out
|
||||
# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff err
|
||||
|
||||
type r: record {
|
||||
a: count;
|
||||
b: count &optional;
|
||||
c: count &default = 9;
|
||||
d: string &is_assigned;
|
||||
e: string;
|
||||
};
|
||||
|
||||
type r2: record {
|
||||
no_worries: r &is_assigned;
|
||||
worries: r;
|
||||
};
|
||||
|
||||
event zeek_init()
|
||||
{
|
||||
local x: r;
|
||||
print x;
|
||||
|
||||
if ( x?$a )
|
||||
x$e = "I'm set";
|
||||
print x; # should complain about $e, but not about $a
|
||||
|
||||
local x2: r2;
|
||||
print x2;
|
||||
|
||||
local x3: r2 &is_assigned;
|
||||
print x3;
|
||||
|
||||
local x4: count;
|
||||
# note, no execution after this point due to error
|
||||
|
||||
# We use this slightly baroque expression because compiled code
|
||||
# may have x4 genuinely uninitialized, and we want deterministic
|
||||
# output in that case.
|
||||
if ( x4 > 5 )
|
||||
print T;
|
||||
else
|
||||
print T;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue