zeek/src/script_opt/CPP/DeclFunc.cc
Jon Siwek 1b825c51de Fix LGTM warnings in script_opt/CPP code
i.e. shadowed variables and "missing return values" (scare quotes)
2021-05-06 12:06:57 -07:00

321 lines
8.4 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#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();
DeclareSubclass(f->GetType(), pf, fname, body, priority, nullptr,
f->Flavor());
}
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().id;
auto& ids = l->OuterIDs();
for ( auto id : ids )
lambda_names[id] = LocalName(id);
DeclareSubclass(l_id->GetType<FuncType>(), pf, lname, body, 0, l,
FUNC_FLAVOR_FUNCTION);
}
void CPPCompile::DeclareSubclass(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;
const IDPList* lambda_ids = l ? &l->OuterIDs() : nullptr;
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 : 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 + "(_" + name + ")";
}
}
Emit("%s_cl(const char* name%s) : CPPStmt(name)%s { }",
fname, addl_args.c_str(), inits.c_str());
// An additional constructor just used to generate place-holder
// instances, due to the mis-design 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) { }", fname);
Emit("ValPtr Exec(Frame* f, StmtFlowType& flow) override final");
StartBlock();
Emit("flow = FLOW_RETURN;");
if ( in_hook )
{
Emit("if ( ! %s(%s) )", fname, BindArgs(ft, lambda_ids));
StartBlock();
Emit("flow = FLOW_BREAK;");
EndBlock();
Emit("return nullptr;");
}
else if ( IsNativeType(yt) )
GenInvokeBody(fname, yt, BindArgs(ft, lambda_ids));
else
Emit("return %s(%s);", fname, BindArgs(ft, lambda_ids));
EndBlock();
if ( lambda_ids )
BuildLambda(ft, pf, fname, body, l, lambda_ids);
else
{
// 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);
auto loc_f = script_specific_filename(body);
cf_locs[fname] = loc_f;
// Some guidance for those looking through the generated code.
Emit("// compiled body for: %s", loc_f);
}
EndBlock(true);
auto h = pf->HashVal();
body_hashes[fname] = h;
body_priorities[fname] = priority;
body_names.emplace(body.get(), fname);
names_to_bodies.emplace(fname, body.get());
total_hash = merge_p_hashes(total_hash, h);
}
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.c_str());
}
// Generate initialization to create and register the lambda.
auto literal_name = string("\"") + l->Name() + "\"";
auto instantiate = string("make_intrusive<") + fname + "_cl>(" +
literal_name + ")";
int nl = lambda_ids->length();
auto h = Fmt(pf->HashVal());
auto has_captures = nl > 0 ? "true" : "false";
auto l_init = string("register_lambda__CPP(") + instantiate +
", " + h + ", \"" + l->Name() + "\", " +
GenTypeName(ft) + ", " + has_captures + ");";
AddInit(l, l_init);
NoteInitDependency(l, TypeRep(ft));
// Make the lambda's body's initialization depend on the lambda's
// initialization. That way GenFuncVarInits() can generate
// initializations with the assurance that the associated body
// hashes will have been registered.
AddInit(body.get());
NoteInitDependency(body.get(), l);
// 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)
{
const auto& params = ft->Params();
int n = params->NumFields();
string decl;
for ( auto i = 0; i < n; ++i )
{
const auto& t = params->GetFieldType(i);
auto tn = FullTypeName(t);
auto param_id = FindParam(i, pf);
string fn;
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".
fn = string("any_param__CPP_") + Fmt(i);
else
fn = 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.
fn = string("unused_param__CPP_") + Fmt(i);
if ( IsNativeType(t) )
// Native types are always pass-by-value.
decl = decl + tn + " " + fn;
else
{
if ( param_id && pf->Assignees().count(param_id) > 0 )
// We modify the parameter.
decl = decl + tn + " " + fn;
else
// Not modified, so pass by const reference.
decl = decl + "const " + tn + "& " + fn;
}
decl += ", ";
}
if ( lambda_ids )
{
// Add the captures as additional parameters.
for ( auto& id : *lambda_ids )
{
auto name = lambda_names[id];
const auto& t = id->GetType();
auto tn = FullTypeName(t);
// Allow the captures to be modified.
decl = decl + tn + "& " + name + ", ";
}
}
// Add in the declaration of the frame.
return decl + "Frame* f__CPP";
}
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;
}
} // zeek::detail