mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
Fix deferred record initialization
Put RecordFieldInit instances into creation_inits during parsing and determine their deferrability in an InitPostScript step. Any RecordFieldInits can be deferred are moved into deferred_inits. Closes #3260
This commit is contained in:
parent
7d6c8d7224
commit
384e7e6b25
3 changed files with 107 additions and 1 deletions
93
src/Type.cc
93
src/Type.cc
|
@ -13,7 +13,9 @@
|
|||
#include "zeek/Desc.h"
|
||||
#include "zeek/Expr.h"
|
||||
#include "zeek/Reporter.h"
|
||||
#include "zeek/RunState.h"
|
||||
#include "zeek/Scope.h"
|
||||
#include "zeek/Traverse.h"
|
||||
#include "zeek/Val.h"
|
||||
#include "zeek/Var.h"
|
||||
#include "zeek/module_util.h"
|
||||
|
@ -1065,6 +1067,8 @@ public:
|
|||
return ZVal(v, init_type);
|
||||
}
|
||||
|
||||
bool IsDeferrable() const override { return false; }
|
||||
|
||||
private:
|
||||
detail::ExprPtr init_expr;
|
||||
TypePtr init_type;
|
||||
|
@ -1080,6 +1084,12 @@ public:
|
|||
|
||||
ZVal Generate() const override { return ZVal(new RecordVal(init_type)); }
|
||||
|
||||
bool IsDeferrable() const override
|
||||
{
|
||||
assert(! run_state::is_parsing);
|
||||
return init_type->IsDeferrable();
|
||||
}
|
||||
|
||||
private:
|
||||
RecordTypePtr init_type;
|
||||
};
|
||||
|
@ -1116,6 +1126,59 @@ private:
|
|||
|
||||
} // namespace detail
|
||||
|
||||
// Helper TraversalCallback optimizing RecordType instances by moving
|
||||
// deferrable FieldInits from creation_inits to deferred_inits once
|
||||
// parsing has completed.
|
||||
class RecordType::CreationInitsOptimizer : public detail::TraversalCallback
|
||||
{
|
||||
public:
|
||||
detail::TraversalCode PreID(const detail::ID* id) override
|
||||
{
|
||||
if ( const auto& t = id->GetType() )
|
||||
HANDLE_TC_TYPE_POST(t->Traverse(this));
|
||||
|
||||
return detail::TC_CONTINUE;
|
||||
}
|
||||
|
||||
detail::TraversalCode PreType(const Type* t) override
|
||||
{
|
||||
if ( analyzed_types.count(t) > 0 )
|
||||
return detail::TC_ABORTSTMT;
|
||||
|
||||
analyzed_types.emplace(t);
|
||||
|
||||
if ( t->Tag() == TYPE_RECORD )
|
||||
{
|
||||
auto* rt = const_cast<RecordType*>(t->AsRecordType());
|
||||
OptimizeCreationInits(rt);
|
||||
}
|
||||
|
||||
return detail::TC_CONTINUE;
|
||||
}
|
||||
|
||||
private:
|
||||
void OptimizeCreationInits(RecordType* rt)
|
||||
{
|
||||
int i = 0;
|
||||
for ( auto& ci : rt->creation_inits )
|
||||
{
|
||||
if ( ! ci.second->IsDeferrable() )
|
||||
rt->creation_inits[i++] = std::move(ci);
|
||||
else
|
||||
{
|
||||
assert(! rt->deferred_inits[ci.first]);
|
||||
rt->deferred_inits[ci.first].swap(ci.second);
|
||||
}
|
||||
}
|
||||
|
||||
// Discard remaining elements.
|
||||
rt->creation_inits.resize(i);
|
||||
}
|
||||
|
||||
// Endless recursion avoidance.
|
||||
std::unordered_set<const Type*> analyzed_types;
|
||||
};
|
||||
|
||||
RecordType::RecordType(type_decl_list* arg_types) : Type(TYPE_RECORD)
|
||||
{
|
||||
types = arg_types;
|
||||
|
@ -1132,6 +1195,12 @@ RecordType::RecordType(type_decl_list* arg_types) : Type(TYPE_RECORD)
|
|||
num_orig_fields = num_fields;
|
||||
}
|
||||
|
||||
void RecordType::InitPostScript()
|
||||
{
|
||||
auto cb = CreationInitsOptimizer();
|
||||
detail::traverse_all(&cb);
|
||||
}
|
||||
|
||||
// in this case the clone is actually not so shallow, since
|
||||
// it gets modified by everyone.
|
||||
TypePtr RecordType::ShallowClone()
|
||||
|
@ -1205,7 +1274,15 @@ void RecordType::AddField(unsigned int field, const TypeDecl* td)
|
|||
TypeTag tag = type->Tag();
|
||||
|
||||
if ( tag == TYPE_RECORD )
|
||||
init = std::make_unique<detail::RecordFieldInit>(cast_intrusive<RecordType>(type));
|
||||
{
|
||||
// Initially, put record fields into creation_inits. Once parsing has
|
||||
// completed, they may move into deferred_inits. See RecordType::InitPostScript()
|
||||
// and RecordType::CreationInitisOptimizer.
|
||||
//
|
||||
// init (nil) is appended to deferred_inits as placeholder.
|
||||
auto rfi = std::make_unique<detail::RecordFieldInit>(cast_intrusive<RecordType>(type));
|
||||
creation_inits.emplace_back(field, std::move(rfi));
|
||||
}
|
||||
|
||||
else if ( tag == TYPE_TABLE )
|
||||
init = std::make_unique<detail::TableFieldInit>(cast_intrusive<TableType>(type), a);
|
||||
|
@ -1589,6 +1666,20 @@ detail::TraversalCode RecordType::Traverse(detail::TraversalCallback* cb) const
|
|||
HANDLE_TC_TYPE_POST(tc);
|
||||
}
|
||||
|
||||
bool RecordType::IsDeferrable() const
|
||||
{
|
||||
assert(! run_state::is_parsing);
|
||||
auto is_deferrable = [](const auto& p) -> bool
|
||||
{
|
||||
return p.second->IsDeferrable();
|
||||
};
|
||||
|
||||
// If all creation_inits are deferrable, this record type is deferrable, too.
|
||||
// It will be optimized later on. Note, all_of() returns true for an empty
|
||||
// range, which is correct.
|
||||
return std::all_of(creation_inits.begin(), creation_inits.end(), is_deferrable);
|
||||
}
|
||||
|
||||
FileType::FileType(TypePtr yield_type) : Type(TYPE_FILE), yield(std::move(yield_type)) { }
|
||||
|
||||
FileType::~FileType() = default;
|
||||
|
|
13
src/Type.h
13
src/Type.h
|
@ -44,6 +44,9 @@ public:
|
|||
|
||||
// Return the initialization value of the field.
|
||||
virtual ZVal Generate() const = 0;
|
||||
|
||||
// Can initialization of the field be deferred?
|
||||
virtual bool IsDeferrable() const { return true; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
@ -734,6 +737,15 @@ public:
|
|||
|
||||
detail::TraversalCode Traverse(detail::TraversalCallback* cb) const override;
|
||||
|
||||
// Can initialization of record values of this type be deferred?
|
||||
//
|
||||
// When record types contain non-const &default expressions or recursively
|
||||
// contain any nested records that themselves are not deferrable,
|
||||
// initialization can not be deferred, otherwise possible.
|
||||
bool IsDeferrable() const;
|
||||
|
||||
static void InitPostScript();
|
||||
|
||||
private:
|
||||
RecordType() { types = nullptr; }
|
||||
|
||||
|
@ -754,6 +766,7 @@ private:
|
|||
// <fieldoffset, init> pairs.
|
||||
std::vector<std::pair<int, std::unique_ptr<detail::FieldInit>>> creation_inits;
|
||||
|
||||
class CreationInitsOptimizer;
|
||||
friend zeek::RecordVal;
|
||||
const auto& DeferredInits() const { return deferred_inits; }
|
||||
const auto& CreationInits() const { return creation_inits; }
|
||||
|
|
|
@ -869,6 +869,8 @@ SetupResult setup(int argc, char** argv, Options* zopts)
|
|||
if ( reporter->Errors() > 0 )
|
||||
exit(1);
|
||||
|
||||
RecordType::InitPostScript();
|
||||
|
||||
telemetry_mgr->InitPostScript();
|
||||
iosource_mgr->InitPostScript();
|
||||
log_mgr->InitPostScript();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue