mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
365 lines
12 KiB
C++
365 lines
12 KiB
C++
// See the file "COPYING" in the main distribution directory for copyright.
|
|
|
|
#include "zeek/script_opt/CPP/Compile.h"
|
|
|
|
namespace zeek::detail {
|
|
|
|
using namespace std;
|
|
|
|
void CPPCompile::DeclareFunc(const FuncInfo& func) {
|
|
if ( ! IsCompilable(func) )
|
|
return;
|
|
|
|
auto fname = Canonicalize(BodyName(func).c_str()) + "_zf";
|
|
auto pf = func.Profile();
|
|
auto f = func.Func();
|
|
const auto& body = func.Body();
|
|
auto priority = func.Priority();
|
|
|
|
CreateFunction(f->GetType(), pf, fname, body, priority, nullptr, f->Flavor());
|
|
|
|
if ( f->GetBodies().size() == 1 )
|
|
compiled_simple_funcs[f->Name()] = fname;
|
|
}
|
|
|
|
void CPPCompile::DeclareLambda(const LambdaExpr* l, const ProfileFunc* pf) {
|
|
ASSERT(is_CPP_compilable(pf));
|
|
|
|
auto lname = Canonicalize(l->Name().c_str()) + "_lb";
|
|
auto body = l->Ingredients()->Body();
|
|
auto l_id = l->Ingredients()->GetID();
|
|
auto& ids = l->OuterIDs();
|
|
|
|
for ( auto id : ids )
|
|
lambda_names[id] = CaptureName(id);
|
|
|
|
CreateFunction(l_id->GetType<FuncType>(), pf, lname, body, 0, l, FUNC_FLAVOR_FUNCTION);
|
|
}
|
|
|
|
void CPPCompile::CreateFunction(const FuncTypePtr& ft, const ProfileFunc* pf, const string& fname, const StmtPtr& body,
|
|
int priority, const LambdaExpr* l, FunctionFlavor flavor) {
|
|
const auto& yt = ft->Yield();
|
|
in_hook = flavor == FUNC_FLAVOR_HOOK;
|
|
|
|
IDPList effective_lambda_ids;
|
|
if ( l )
|
|
effective_lambda_ids = l->OuterIDs();
|
|
|
|
const IDPList* lambda_ids = l ? &effective_lambda_ids : nullptr;
|
|
|
|
string args = BindArgs(ft, lambda_ids);
|
|
|
|
auto yt_decl = in_hook ? "bool" : FullTypeName(yt);
|
|
|
|
vector<string> p_types;
|
|
GatherParamTypes(p_types, ft, lambda_ids, pf);
|
|
|
|
string cast = string(yt_decl) + "(*)(";
|
|
for ( auto& pt : p_types )
|
|
cast += pt + ", ";
|
|
cast += string("Frame*)");
|
|
|
|
// We need to distinguish between hooks and non-hooks that happen
|
|
// to have matching type signatures. They'll be equivalent if they
|
|
// have identical cast's. To keep them separate, we cheat and
|
|
// make hook casts different, string-wise, without altering their
|
|
// semantics.
|
|
if ( in_hook )
|
|
cast += " ";
|
|
|
|
func_index[fname] = cast;
|
|
|
|
if ( ! l && casting_index.count(cast) == 0 ) {
|
|
casting_index[cast] = func_casting_glue.size();
|
|
|
|
DispatchInfo di;
|
|
di.cast = cast;
|
|
di.args = args;
|
|
di.is_hook = in_hook;
|
|
di.yield = yt;
|
|
|
|
func_casting_glue.emplace_back(di);
|
|
}
|
|
|
|
if ( lambda_ids ) {
|
|
DeclareSubclass(ft, pf, fname, args, lambda_ids);
|
|
BuildLambda(ft, pf, fname, body, l, lambda_ids);
|
|
EndBlock(true);
|
|
}
|
|
else {
|
|
Emit("static %s %s(%s);", yt_decl, fname, ParamDecl(ft, lambda_ids, pf));
|
|
|
|
// Track this function as known to have been compiled.
|
|
// We don't track lambda bodies as compiled because they
|
|
// can't be instantiated directly without also supplying
|
|
// the captures. In principle we could make an exception
|
|
// for lambdas that don't take any arguments, but that
|
|
// seems potentially more confusing than beneficial.
|
|
compiled_funcs.emplace(fname);
|
|
}
|
|
|
|
body_hashes[fname] = pf->HashVal();
|
|
body_priorities[fname] = priority;
|
|
body_locs[fname] = body->GetLocationInfo();
|
|
body_names.emplace(body.get(), fname);
|
|
}
|
|
|
|
void CPPCompile::DeclareSubclass(const FuncTypePtr& ft, const ProfileFunc* pf, const string& fname, const string& args,
|
|
const IDPList* lambda_ids) {
|
|
const auto& yt = ft->Yield();
|
|
|
|
auto yt_decl = in_hook ? "bool" : FullTypeName(yt);
|
|
|
|
NL();
|
|
Emit("static %s %s(%s);", yt_decl, fname, ParamDecl(ft, lambda_ids, pf));
|
|
|
|
Emit("class %s_cl final : public CPPStmt", fname);
|
|
StartBlock();
|
|
|
|
Emit("public:");
|
|
|
|
string addl_args; // captures passed in on construction
|
|
string inits; // initializers for corresponding member vars
|
|
|
|
if ( lambda_ids ) {
|
|
for ( auto& id : *lambda_ids ) {
|
|
auto name = lambda_names[id];
|
|
auto tn = FullTypeName(id->GetType());
|
|
addl_args = addl_args + ", " + tn + " _" + name;
|
|
|
|
inits = inits + ", " + name + "(std::move(_" + name + "))";
|
|
}
|
|
}
|
|
|
|
const Obj* stmts = pf->ProfiledBody();
|
|
if ( ! stmts )
|
|
stmts = pf->ProfiledExpr();
|
|
|
|
auto loc = stmts->GetLocationInfo();
|
|
auto loc_info = string("\"") + loc->filename + "\", " + Fmt(loc->first_line);
|
|
Emit("%s_cl(const char* name%s) : CPPStmt(name, %s)%s { }", fname, addl_args, loc_info, inits);
|
|
|
|
// An additional constructor just used to generate place-holder
|
|
// instances, due to the misdesign that lambdas are identified
|
|
// by their Func objects rather than their FuncVal objects.
|
|
if ( lambda_ids && lambda_ids->length() > 0 )
|
|
Emit("%s_cl(const char* name) : CPPStmt(name, %s) { }", fname, loc_info);
|
|
|
|
Emit("ValPtr Exec(Frame* f, StmtFlowType& flow) override");
|
|
StartBlock();
|
|
|
|
Emit("flow = FLOW_RETURN;");
|
|
Emit("f->SetOnlyCall(ce.get());");
|
|
|
|
if ( in_hook ) {
|
|
Emit("if ( ! %s(%s) )", fname, args);
|
|
StartBlock();
|
|
Emit("flow = FLOW_BREAK;");
|
|
EndBlock();
|
|
Emit("return nullptr;");
|
|
}
|
|
|
|
else if ( IsNativeType(yt) )
|
|
GenInvokeBody(fname, yt, args);
|
|
|
|
else
|
|
Emit("return %s(%s);", fname, args);
|
|
|
|
EndBlock();
|
|
}
|
|
|
|
void CPPCompile::DeclareDynCPPStmt() {
|
|
Emit("// A version of CPPStmt that manages a function pointer and");
|
|
Emit("// dynamically casts it to a given type to call it via Exec().");
|
|
Emit("// We will later generate a custom Exec method to support this");
|
|
Emit("// dispatch. All of this is ugly, and only needed because clang");
|
|
Emit("// goes nuts (super slow) in the face of thousands of templates");
|
|
Emit("// in a given context (initializers, or a function body).");
|
|
Emit("class CPPDynStmt final : public CPPStmt");
|
|
Emit("\t{");
|
|
Emit("public:");
|
|
Emit(
|
|
"\tCPPDynStmt(const char* _name, void* _func, int _type_signature, const char* filename, "
|
|
"int line_num) : CPPStmt(_name, filename, line_num), "
|
|
"func(_func), type_signature(_type_signature) { }");
|
|
Emit("\tValPtr Exec(Frame* f, StmtFlowType& flow) override;");
|
|
Emit("private:");
|
|
Emit("\t// The function to call in Exec().");
|
|
Emit("\tvoid* func;");
|
|
Emit("\t// Used via a switch in the dynamically-generated Exec() method");
|
|
Emit("\t// to cast func to the write type, and to call it with the");
|
|
Emit("\t// right arguments pulled out of the frame.");
|
|
Emit("\tint type_signature;");
|
|
Emit("\t};");
|
|
}
|
|
|
|
void CPPCompile::BuildLambda(const FuncTypePtr& ft, const ProfileFunc* pf, const string& fname, const StmtPtr& body,
|
|
const LambdaExpr* l, const IDPList* lambda_ids) {
|
|
// Declare the member variables for holding the captures.
|
|
for ( auto& id : *lambda_ids ) {
|
|
auto name = lambda_names[id];
|
|
auto tn = FullTypeName(id->GetType());
|
|
Emit("%s %s;", tn, name);
|
|
}
|
|
|
|
// Generate initialization to create and register the lambda.
|
|
auto h = pf->HashVal();
|
|
auto nl = lambda_ids->length();
|
|
bool has_captures = nl > 0;
|
|
|
|
auto gi = make_shared<LambdaRegistrationInfo>(this, l->Name(), ft, fname + "_cl", h, has_captures);
|
|
lambda_reg_info->AddInstance(gi);
|
|
|
|
// Generate method to extract the lambda captures from a deserialized
|
|
// Frame object.
|
|
Emit("void SetLambdaCaptures(Frame* f) override");
|
|
StartBlock();
|
|
for ( int i = 0; i < nl; ++i ) {
|
|
auto l_i = (*lambda_ids)[i];
|
|
const auto& t_i = l_i->GetType();
|
|
auto cap_i = string("f->GetElement(") + Fmt(i) + ")";
|
|
Emit("%s = %s;", lambda_names[l_i], GenericValPtrToGT(cap_i, t_i, GEN_NATIVE));
|
|
}
|
|
EndBlock();
|
|
|
|
// Generate the method for serializing the captures.
|
|
Emit("std::vector<ValPtr> SerializeLambdaCaptures() const override");
|
|
StartBlock();
|
|
Emit("std::vector<ValPtr> vals;");
|
|
for ( int i = 0; i < nl; ++i ) {
|
|
auto l_i = (*lambda_ids)[i];
|
|
const auto& t_i = l_i->GetType();
|
|
Emit("vals.emplace_back(%s);", NativeToGT(lambda_names[l_i], t_i, GEN_VAL_PTR));
|
|
}
|
|
Emit("return vals;");
|
|
EndBlock();
|
|
|
|
// Generate the Clone() method.
|
|
Emit("CPPStmtPtr Clone() override");
|
|
StartBlock();
|
|
auto arg_clones = GenLambdaClone(l, true);
|
|
Emit("return make_intrusive<%s_cl>(name.c_str()%s);", fname, arg_clones);
|
|
EndBlock();
|
|
}
|
|
|
|
string CPPCompile::BindArgs(const FuncTypePtr& ft, const IDPList* lambda_ids) {
|
|
const auto& params = ft->Params();
|
|
auto t = params->Types();
|
|
|
|
string res;
|
|
|
|
int n = t ? t->size() : 0;
|
|
for ( auto i = 0; i < n; ++i ) {
|
|
auto arg_i = string("f->GetElement(") + Fmt(i) + ")";
|
|
const auto& pt = params->GetFieldType(i);
|
|
|
|
if ( IsNativeType(pt) )
|
|
res += arg_i + NativeAccessor(pt);
|
|
else
|
|
res += GenericValPtrToGT(arg_i, pt, GEN_VAL_PTR);
|
|
|
|
res += ", ";
|
|
}
|
|
|
|
if ( lambda_ids ) {
|
|
for ( auto& id : *lambda_ids )
|
|
res += lambda_names[id] + ", ";
|
|
}
|
|
|
|
// Add the final frame argument.
|
|
return res + "f";
|
|
}
|
|
|
|
string CPPCompile::ParamDecl(const FuncTypePtr& ft, const IDPList* lambda_ids, const ProfileFunc* pf) {
|
|
vector<string> p_types;
|
|
vector<string> p_names;
|
|
|
|
GatherParamTypes(p_types, ft, lambda_ids, pf);
|
|
GatherParamNames(p_names, ft, lambda_ids, pf);
|
|
|
|
ASSERT(p_types.size() == p_names.size());
|
|
|
|
string decl;
|
|
|
|
for ( auto i = 0U; i < p_types.size(); ++i )
|
|
decl += p_types[i] + " " + p_names[i] + ", ";
|
|
|
|
// Add in the declaration of the frame.
|
|
return decl + "Frame* f__CPP";
|
|
}
|
|
|
|
void CPPCompile::GatherParamTypes(vector<string>& p_types, const FuncTypePtr& ft, const IDPList* lambda_ids,
|
|
const ProfileFunc* pf) {
|
|
const auto& params = ft->Params();
|
|
int n = params->NumFields();
|
|
|
|
for ( auto i = 0; i < n; ++i ) {
|
|
const auto& t = params->GetFieldType(i);
|
|
auto tn = FullTypeName(t);
|
|
auto param_id = FindParam(i, pf);
|
|
|
|
if ( IsNativeType(t) )
|
|
// Native types are always pass-by-value.
|
|
p_types.emplace_back(tn);
|
|
else {
|
|
if ( param_id && pf->Assignees().count(param_id) > 0 )
|
|
// We modify the parameter.
|
|
p_types.emplace_back(tn);
|
|
else
|
|
// Not modified, so pass by const reference.
|
|
p_types.emplace_back(string("const ") + tn + "&");
|
|
}
|
|
}
|
|
|
|
if ( lambda_ids )
|
|
// Add the captures as additional parameters.
|
|
for ( auto& id : *lambda_ids ) {
|
|
const auto& t = id->GetType();
|
|
auto tn = FullTypeName(t);
|
|
|
|
// Allow the captures to be modified.
|
|
p_types.emplace_back(string(tn) + "&");
|
|
}
|
|
}
|
|
|
|
void CPPCompile::GatherParamNames(vector<string>& p_names, const FuncTypePtr& ft, const IDPList* lambda_ids,
|
|
const ProfileFunc* pf) {
|
|
const auto& params = ft->Params();
|
|
int n = params->NumFields();
|
|
|
|
for ( auto i = 0; i < n; ++i ) {
|
|
const auto& t = params->GetFieldType(i);
|
|
auto param_id = FindParam(i, pf);
|
|
|
|
if ( param_id ) {
|
|
if ( t->Tag() == TYPE_ANY && param_id->GetType()->Tag() != TYPE_ANY )
|
|
// We'll need to translate the parameter from its current
|
|
// representation to type "any".
|
|
p_names.emplace_back(string("any_param__CPP_") + Fmt(i));
|
|
else
|
|
p_names.emplace_back(LocalName(param_id));
|
|
}
|
|
else
|
|
// Parameters that are unused don't wind up in the ProfileFunc.
|
|
// Rather than dig their name out of the function's declaration,
|
|
// we explicitly name them to reflect that they're unused.
|
|
p_names.emplace_back(string("unused_param__CPP_") + Fmt(i));
|
|
}
|
|
|
|
if ( lambda_ids )
|
|
// Add the captures as additional parameters.
|
|
for ( auto& id : *lambda_ids )
|
|
p_names.emplace_back(lambda_names[id]);
|
|
}
|
|
|
|
const ID* CPPCompile::FindParam(int i, const ProfileFunc* pf) {
|
|
const auto& params = pf->Params();
|
|
|
|
for ( const auto& p : params )
|
|
if ( p->Offset() == i )
|
|
return p;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace zeek::detail
|