/src/zeek/src/script_opt/CPP/GenFunc.cc
Line | Count | Source |
1 | | // See the file "COPYING" in the main distribution directory for copyright. |
2 | | |
3 | | #include "zeek/script_opt/CPP/Compile.h" |
4 | | |
5 | | namespace zeek::detail { |
6 | | |
7 | | using namespace std; |
8 | | |
9 | 0 | void CPPCompile::CompileFunc(const FuncInfo& func) { |
10 | 0 | if ( ! IsCompilable(func) ) |
11 | 0 | return; |
12 | | |
13 | 0 | auto fname = Canonicalize(BodyName(func)) + "_zf"; |
14 | 0 | auto pf = func.Profile(); |
15 | 0 | auto f = func.Func(); |
16 | 0 | const auto& body = func.Body(); |
17 | |
|
18 | 0 | DefineBody(f->GetType(), pf, fname, body, f->Flavor()); |
19 | 0 | } |
20 | | |
21 | 0 | void CPPCompile::CompileLambda(const LambdaExpr* l, const ProfileFunc* pf) { |
22 | 0 | auto lname = Canonicalize(l->Name()) + "_lb"; |
23 | 0 | auto body = l->Ingredients()->Body(); |
24 | 0 | auto l_id = l->Ingredients()->GetID(); |
25 | |
|
26 | 0 | lambda_ids = &l->OuterIDs(); |
27 | 0 | DefineBody(l_id->GetType<FuncType>(), pf, lname, body, FUNC_FLAVOR_FUNCTION); |
28 | 0 | lambda_ids = nullptr; |
29 | 0 | } |
30 | | |
31 | 0 | void CPPCompile::GenInvokeBody(const string& call, const TypePtr& t) { |
32 | 0 | if ( ! t || t->Tag() == TYPE_VOID ) { |
33 | 0 | Emit("%s;", call); |
34 | 0 | Emit("return nullptr;"); |
35 | 0 | } |
36 | 0 | else |
37 | 0 | Emit("return %s;", NativeToGT(call, t, GEN_VAL_PTR)); |
38 | 0 | } |
39 | | |
40 | | void CPPCompile::DefineBody(const FuncTypePtr& ft, const ProfileFunc* pf, const string& fname, const StmtPtr& body, |
41 | 0 | FunctionFlavor flavor) { |
42 | 0 | locals.clear(); |
43 | 0 | params.clear(); |
44 | |
|
45 | 0 | body_name = fname; |
46 | |
|
47 | 0 | func_type = ft; |
48 | 0 | ret_type = ft->Yield(); |
49 | |
|
50 | 0 | in_hook = flavor == FUNC_FLAVOR_HOOK; |
51 | 0 | auto ret_type_str = in_hook ? "bool" : FullTypeName(ret_type); |
52 | |
|
53 | 0 | for ( const auto& p : pf->Params() ) |
54 | 0 | params.emplace(p); |
55 | |
|
56 | 0 | NL(); |
57 | |
|
58 | 0 | Emit("%s %s(%s)", ret_type_str, fname, ParamDecl(ft, pf)); |
59 | |
|
60 | 0 | StartBlock(); |
61 | | |
62 | | // Deal with "any" parameters, if any. |
63 | 0 | TranslateAnyParams(ft, pf); |
64 | | |
65 | | // Make sure that any events referred to in this function have |
66 | | // been initialized. |
67 | 0 | InitializeEvents(pf); |
68 | | |
69 | | // Create the local variables. |
70 | 0 | DeclareLocals(pf); |
71 | |
|
72 | 0 | GenStmt(body); |
73 | |
|
74 | 0 | if ( in_hook ) { |
75 | 0 | Emit("return true;"); |
76 | 0 | in_hook = false; |
77 | 0 | } |
78 | | |
79 | | // Seatbelts for running off the end of a function that's supposed |
80 | | // to return a non-native type. |
81 | 0 | if ( ! IsNativeType(ret_type) ) |
82 | 0 | Emit("return nullptr;"); |
83 | |
|
84 | 0 | EndBlock(); |
85 | 0 | } |
86 | | |
87 | 0 | void CPPCompile::TranslateAnyParams(const FuncTypePtr& ft, const ProfileFunc* pf) { |
88 | 0 | const auto& formals = ft->Params(); |
89 | 0 | int n = formals->NumFields(); |
90 | |
|
91 | 0 | for ( auto i = 0; i < n; ++i ) { |
92 | 0 | const auto& t = formals->GetFieldType(i); |
93 | 0 | if ( t->Tag() != TYPE_ANY ) |
94 | | // Not a relevant parameter. |
95 | 0 | continue; |
96 | | |
97 | 0 | auto param_id = FindParam(i, pf); |
98 | 0 | if ( ! param_id ) |
99 | | // Parameter isn't used, skip it. |
100 | 0 | continue; |
101 | | |
102 | 0 | const auto& pt = param_id->GetType(); |
103 | 0 | if ( pt->Tag() == TYPE_ANY ) |
104 | | // It's already "any", nothing more to do. |
105 | 0 | continue; |
106 | | |
107 | 0 | auto any_i = string("any_param__CPP_") + Fmt(i); |
108 | |
|
109 | 0 | Emit("%s %s = %s;", FullTypeName(pt), LocalName(param_id), GenericValPtrToGT(any_i, pt, GEN_NATIVE)); |
110 | 0 | } |
111 | 0 | } |
112 | | |
113 | 0 | void CPPCompile::InitializeEvents(const ProfileFunc* pf) { |
114 | | // Make sure that any events referred to in this function have |
115 | | // been initialized. We have to do this dynamically because it |
116 | | // depends on whether the final script using the compiled code |
117 | | // happens to load the associated event handler |
118 | 0 | for ( const auto& e : pf->Events() ) { |
119 | 0 | auto ev_name = globals[e] + "_ev"; |
120 | | |
121 | | // Create a scope so we don't have to individualize the |
122 | | // variables. |
123 | 0 | Emit("{"); |
124 | 0 | Emit("static bool did_init = false;"); |
125 | 0 | Emit("if ( ! did_init )"); |
126 | 0 | StartBlock(); |
127 | | |
128 | | // We do both a Lookup and a Register because only the latter |
129 | | // returns an EventHandlerPtr, sigh. |
130 | 0 | Emit("if ( event_registry->Lookup(\"%s\") )", e); |
131 | 0 | StartBlock(); |
132 | 0 | Emit("%s = event_registry->Register(\"%s\");", ev_name, e); |
133 | 0 | EndBlock(); |
134 | 0 | Emit("did_init = true;"); |
135 | 0 | EndBlock(); |
136 | 0 | Emit("}"); |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | 0 | void CPPCompile::DeclareLocals(const ProfileFunc* pf) { |
141 | | // We track captures by their names rather than their ID*'s because the |
142 | | // latter can be inconsistent when inlining. |
143 | 0 | set<string> capture_names; |
144 | 0 | if ( lambda_ids ) |
145 | 0 | for ( const auto& li : *lambda_ids ) |
146 | 0 | capture_names.insert(CaptureName(li)); |
147 | |
|
148 | 0 | const auto& ls = pf->Locals(); |
149 | 0 | int num_params = static_cast<int>(pf->Params().size()); |
150 | | |
151 | | // Track whether we generated a declaration. This is just for |
152 | | // tidiness in the output. |
153 | 0 | bool did_decl = false; |
154 | |
|
155 | 0 | for ( const auto& l : ls ) { |
156 | 0 | auto ln = LocalName(l); |
157 | 0 | auto cn = CaptureName(l); |
158 | |
|
159 | 0 | if ( capture_names.contains(cn) ) |
160 | | // No need to declare these, they're passed in as parameters. |
161 | 0 | ln = std::move(cn); |
162 | | |
163 | 0 | else if ( ! params.contains(l) && l->Offset() >= num_params ) { // Not a parameter, so must be a local. |
164 | 0 | Emit("%s %s;", FullTypeName(l->GetType()), ln); |
165 | 0 | did_decl = true; |
166 | 0 | } |
167 | |
|
168 | 0 | locals.emplace(l, ln); |
169 | 0 | } |
170 | |
|
171 | 0 | if ( did_decl ) |
172 | 0 | NL(); |
173 | 0 | } |
174 | | |
175 | 0 | string CPPCompile::BodyName(const FuncInfo& func) { |
176 | 0 | const auto& f = func.Func(); |
177 | 0 | const auto& body = func.Body(); |
178 | 0 | auto fname = f->GetName(); |
179 | | |
180 | | // Extend name with location information. |
181 | 0 | auto loc = body->GetLocationInfo(); |
182 | 0 | if ( loc->FileName() ) { |
183 | 0 | auto fn = loc->FileName(); |
184 | | |
185 | | // Skip leading goop that gets added by search paths. |
186 | 0 | while ( *fn == '.' || *fn == '/' ) |
187 | 0 | ++fn; |
188 | |
|
189 | 0 | auto canonicalize = [](char c) -> char { return isalnum(c) ? c : '_'; }; |
190 | |
|
191 | 0 | string fns = fn; |
192 | 0 | std::ranges::transform(fns, fns.begin(), canonicalize); |
193 | |
|
194 | 0 | if ( ! isalpha(fns[0]) ) |
195 | | // This can happen for filenames beginning with numbers. |
196 | 0 | fns = "_" + fns; |
197 | |
|
198 | 0 | fname = fns + "__" + fname; |
199 | 0 | } |
200 | |
|
201 | 0 | const auto& bodies = f->GetBodies(); |
202 | |
|
203 | 0 | if ( bodies.size() == 1 ) |
204 | 0 | return fname; |
205 | | |
206 | | // Make the name distinct-per-body. |
207 | | |
208 | 0 | size_t i; |
209 | 0 | for ( i = 0; i < bodies.size(); ++i ) |
210 | 0 | if ( bodies[i].stmts == body ) |
211 | 0 | break; |
212 | |
|
213 | 0 | if ( i >= bodies.size() ) |
214 | 0 | reporter->InternalError("can't find body in CPPCompile::BodyName"); |
215 | |
|
216 | 0 | return fname + "__" + Fmt(static_cast<int>(i)); |
217 | 0 | } |
218 | | |
219 | 0 | p_hash_type CPPCompile::BodyHash(const Stmt* body) { |
220 | 0 | auto bn = body_names.find(body); |
221 | 0 | if ( bn == body_names.end() ) |
222 | 0 | bn = body_names.find(look_up_lambda_alias(body)); |
223 | 0 | ASSERT(bn != body_names.end()); |
224 | | |
225 | 0 | auto& body_name = bn->second; |
226 | 0 | auto bi = body_info.find(body_name); |
227 | 0 | ASSERT(bi != body_info.end()); |
228 | | |
229 | 0 | return bi->second.hash; |
230 | 0 | } |
231 | | |
232 | 0 | string CPPCompile::GenArgs(const RecordTypePtr& params, const Expr* e) { |
233 | 0 | const auto& exprs = e->AsListExpr()->Exprs(); |
234 | 0 | string gen; |
235 | |
|
236 | 0 | int n = exprs.size(); |
237 | |
|
238 | 0 | for ( auto i = 0; i < n; ++i ) { |
239 | 0 | auto e_i = exprs[i]; |
240 | 0 | auto gt = GEN_NATIVE; |
241 | |
|
242 | 0 | const auto& param_t = params->GetFieldType(i); |
243 | 0 | bool param_any = param_t->Tag() == TYPE_ANY; |
244 | 0 | bool arg_any = e_i->GetType()->Tag() == TYPE_ANY; |
245 | |
|
246 | 0 | if ( param_any && ! arg_any ) |
247 | 0 | gt = GEN_VAL_PTR; |
248 | |
|
249 | 0 | auto expr_gen = GenExpr(e_i, gt); |
250 | |
|
251 | 0 | if ( ! param_any && arg_any ) |
252 | 0 | expr_gen = GenericValPtrToGT(expr_gen, param_t, GEN_NATIVE); |
253 | |
|
254 | 0 | gen += expr_gen; |
255 | 0 | if ( i < n - 1 ) |
256 | 0 | gen += ", "; |
257 | 0 | } |
258 | |
|
259 | 0 | return gen; |
260 | 0 | } |
261 | | |
262 | | } // namespace zeek::detail |