// See the file "COPYING" in the main distribution directory for copyright. #include "zeek/script_opt/CPP/Compile.h" namespace zeek::detail { using namespace std; string CPPCompile::GenExprs(const Expr* e) { string gen; if ( e->Tag() == EXPR_LIST ) gen = GenListExpr(e, GEN_VAL_PTR, true); else gen = GenExpr(e, GEN_VAL_PTR); return string("{ ") + gen + " }"; } string CPPCompile::GenListExpr(const Expr* e, GenType gt, bool nested) { const auto& exprs = e->AsListExpr()->Exprs(); string gen; int n = exprs.size(); for ( auto i = 0; i < n; ++i ) { auto e_i = exprs[i]; auto gen_i = GenExpr(e_i, gt); if ( nested && e_i->Tag() == EXPR_LIST ) // These are table or set indices. gen_i = string("index_val__CPP({") + gen_i + "})"; gen += gen_i; if ( i < n - 1 ) gen += ", "; } return gen; } string CPPCompile::GenExpr(const Expr* e, GenType gt, bool top_level) { string gen; switch ( e->Tag() ) { case EXPR_NAME: return GenNameExpr(e->AsNameExpr(), gt); case EXPR_CONST: return GenConstExpr(e->AsConstExpr(), gt); case EXPR_CLONE: gen = GenExpr(e->GetOp1(), GEN_VAL_PTR) + "->Clone()"; return GenericValPtrToGT(gen, e->GetType(), gt); case EXPR_AGGR_ADD: return GenAggrAdd(e); case EXPR_AGGR_DEL: return GenAggrDel(e); case EXPR_INCR: case EXPR_DECR: return GenIncrExpr(e, gt, e->Tag() == EXPR_INCR, top_level); case EXPR_NOT: return GenUnary(e, gt, "!", "not"); case EXPR_COMPLEMENT: return GenUnary(e, gt, "~", "comp"); case EXPR_POSITIVE: return GenUnary(e, gt, "+", "pos"); case EXPR_NEGATE: return GenUnary(e, gt, "-", "neg"); case EXPR_ADD: return GenBinary(e, gt, "+", "add"); case EXPR_SUB: return GenBinary(e, gt, "-", "sub"); case EXPR_TIMES: return GenBinary(e, gt, "*", "mul"); case EXPR_DIVIDE: case EXPR_MASK: // later code will split into addr masking return GenBinary(e, gt, "/", "div"); case EXPR_MOD: return GenBinary(e, gt, "%", "mod"); case EXPR_AND: return GenBinary(e, gt, "&", "and"); case EXPR_OR: return GenBinary(e, gt, "|", "or"); case EXPR_XOR: return GenBinary(e, gt, "^", "xor"); case EXPR_LSHIFT: return GenBinary(e, gt, "<<", "lshift"); case EXPR_RSHIFT: return GenBinary(e, gt, ">>", "rshift"); case EXPR_AND_AND: return GenBinary(e, gt, "&&", "andand"); case EXPR_OR_OR: return GenBinary(e, gt, "||", "oror"); case EXPR_LT: return GenBinary(e, gt, "<", "lt"); case EXPR_LE: return GenBinary(e, gt, "<=", "le"); case EXPR_GE: return GenBinary(e, gt, ">=", "ge"); case EXPR_GT: return GenBinary(e, gt, ">", "gt"); case EXPR_EQ: return GenEQ(e, gt, "==", "eq"); case EXPR_NE: return GenEQ(e, gt, "!=", "ne"); case EXPR_COND: return GenCondExpr(e, gt); case EXPR_CALL: return GenCallExpr(e->AsCallExpr(), gt, top_level); case EXPR_LIST: return GenListExpr(e, gt, false); case EXPR_IN: return GenInExpr(e, gt); case EXPR_FIELD: return GenFieldExpr(e->AsFieldExpr(), gt); case EXPR_HAS_FIELD: return GenHasFieldExpr(e->AsHasFieldExpr(), gt); case EXPR_INDEX: return GenIndexExpr(e, gt); case EXPR_ASSIGN: return GenAssignExpr(e, gt, top_level); case EXPR_ADD_TO: return GenAddToExpr(e, gt, top_level); case EXPR_REMOVE_FROM: return GenRemoveFromExpr(e, gt, top_level); case EXPR_REF: return GenExpr(e->GetOp1(), gt); case EXPR_SIZE: return GenSizeExpr(e, gt); case EXPR_SCHEDULE: return GenScheduleExpr(e); case EXPR_LAMBDA: return GenLambdaExpr(e); case EXPR_IS: return GenIsExpr(e, gt); case EXPR_ARITH_COERCE: return GenArithCoerceExpr(e, gt); case EXPR_RECORD_COERCE: return GenRecordCoerceExpr(e); case EXPR_TABLE_COERCE: return GenTableCoerceExpr(e); case EXPR_VECTOR_COERCE: return GenVectorCoerceExpr(e); case EXPR_RECORD_CONSTRUCTOR: return GenRecordConstructorExpr(e); case EXPR_SET_CONSTRUCTOR: return GenSetConstructorExpr(e); case EXPR_TABLE_CONSTRUCTOR: return GenTableConstructorExpr(e); case EXPR_VECTOR_CONSTRUCTOR: return GenVectorConstructorExpr(e); case EXPR_EVENT: // These should not wind up being directly generated, // but instead deconstructed in the context of either // a "schedule" expression or an "event" statement. ASSERT(0); case EXPR_CAST: gen = string("cast_value_to_type__CPP(") + GenExpr(e->GetOp1(), GEN_VAL_PTR) + ", " + GenTypeName(e->GetType()) + ")"; return GenericValPtrToGT(gen, e->GetType(), gt); case EXPR_TO_ANY_COERCE: return GenExpr(e->GetOp1(), GEN_VAL_PTR); case EXPR_FROM_ANY_COERCE: gen = string("from_any__CPP(") + GenExpr(e->GetOp1(), GEN_VAL_PTR) + ", " + GenTypeName(e->GetType()) + ")"; return GenericValPtrToGT(gen, e->GetType(), gt); case EXPR_FROM_ANY_VEC_COERCE: gen = string("from_any_vec__CPP(") + GenExpr(e->GetOp1(), GEN_VAL_PTR) + ", " + GenTypeName(e->GetType()->Yield()) + ")"; return GenericValPtrToGT(gen, e->GetType(), gt); case EXPR_FIELD_ASSIGN: case EXPR_INDEX_SLICE_ASSIGN: case EXPR_INLINE: // These are only generated for reduced ASTs, which // we shouldn't be compiling. ASSERT(0); default: // Intended to catch errors in overlooking the possible // expressions that might appear. return string("EXPR"); } } string CPPCompile::GenNameExpr(const NameExpr* ne, GenType gt) { const auto& t = ne->GetType(); auto n = ne->Id(); bool is_global_var = global_vars.count(n) > 0; if ( t->Tag() == TYPE_FUNC && ! is_global_var ) { auto func = n->Name(); if ( globals.count(func) > 0 && pfs->BiFGlobals().count(n) == 0 ) return GenericValPtrToGT(IDNameStr(n), t, gt); } if ( is_global_var ) { string gen; if ( n->IsType() ) gen = string("make_intrusive(") + globals[n->Name()] + "->GetType(), true)"; else gen = globals[n->Name()] + "->GetVal()"; return GenericValPtrToGT(gen, t, gt); } return NativeToGT(IDNameStr(n), t, gt); } string CPPCompile::GenConstExpr(const ConstExpr* c, GenType gt) { const auto& t = c->GetType(); if ( ! IsNativeType(t) ) { auto v = c->ValuePtr(); int consts_offset; // ignored (void)RegisterConstant(v, consts_offset); return NativeToGT(const_vals[v.get()]->Name(), t, gt); } return NativeToGT(GenVal(c->ValuePtr()), t, gt); } string CPPCompile::GenAggrAdd(const Expr* e) { auto op = e->GetOp1(); auto aggr = GenExpr(op->GetOp1(), GEN_DONT_CARE); auto indices = GenExpr(op->GetOp2(), GEN_VAL_PTR); return "add_element__CPP(" + aggr + ", index_val__CPP({" + indices + "}))"; } string CPPCompile::GenAggrDel(const Expr* e) { auto op = e->GetOp1(); if ( op->Tag() == EXPR_NAME ) { auto aggr_gen = GenExpr(op, GEN_VAL_PTR); if ( op->GetType()->Tag() == TYPE_TABLE ) return aggr_gen + "->RemoveAll()"; else return aggr_gen + "->Resize(0)"; } auto aggr = op->GetOp1(); auto aggr_gen = GenExpr(aggr, GEN_VAL_PTR); if ( op->Tag() == EXPR_INDEX ) { auto indices = GenExpr(op->GetOp2(), GEN_VAL_PTR); return "remove_element__CPP(" + aggr_gen + ", index_val__CPP({" + indices + "}))"; } ASSERT(op->Tag() == EXPR_FIELD); auto field = GenField(aggr, op->AsFieldExpr()->Field()); return aggr_gen + "->Remove(" + field + ")"; } string CPPCompile::GenIncrExpr(const Expr* e, GenType gt, bool is_incr, bool top_level) { // For compound operands (table indexing, record fields), // Zeek's interpreter will actually evaluate the operand // twice, so easiest is to just transform this node // into the expanded equivalent. auto op = e->GetOp1(); auto one = e->GetType()->InternalType() == TYPE_INTERNAL_INT ? val_mgr->Int(1) : val_mgr->Count(1); auto one_e = make_intrusive(one); ExprPtr rhs; if ( is_incr ) rhs = make_intrusive(op, one_e); else rhs = make_intrusive(op, one_e); auto assign = make_intrusive(op, rhs, false, nullptr, nullptr, false); // Make sure any newly created types are known to // the profiler. (void)pfs->HashType(one_e->GetType()); (void)pfs->HashType(rhs->GetType()); (void)pfs->HashType(assign->GetType()); auto gen = GenExpr(assign, GEN_DONT_CARE, top_level); if ( ! top_level ) gen = "(" + gen + ", " + GenExpr(op, gt) + ")"; return gen; } string CPPCompile::GenCondExpr(const Expr* e, GenType gt) { auto op1 = e->GetOp1(); auto op2 = e->GetOp2(); auto op3 = e->GetOp3(); auto gen1 = GenExpr(op1, GEN_NATIVE); auto gen2 = GenExpr(op2, gt); auto gen3 = GenExpr(op3, gt); if ( op1->GetType()->Tag() == TYPE_VECTOR ) return string("vector_select__CPP(") + gen1 + ", " + gen2 + ", " + gen3 + ")"; return string("(") + gen1 + ") ? (" + gen2 + ") : (" + gen3 + ")"; } string CPPCompile::GenCallExpr(const CallExpr* c, GenType gt, bool top_level) { const auto& t = c->GetType(); auto f = c->Func(); auto args_l = c->Args(); bool is_async = c->IsInWhen(); auto gen = GenExpr(f, GEN_DONT_CARE); if ( f->Tag() == EXPR_NAME ) { auto f_id = f->AsNameExpr()->Id(); const auto& params = f_id->GetType()->AsFuncType()->Params(); auto id_name = f_id->Name(); auto nargs = args_l->Exprs().length(); bool is_compiled = compiled_simple_funcs.count(id_name) > 0; bool was_compiled = hashed_funcs.count(id_name) > 0; bool is_variadic = params->NumFields() == 1 && nargs != 1; if ( ! is_async && ! is_variadic && (is_compiled || was_compiled) ) { // Can call directly. string fname; if ( was_compiled ) fname = hashed_funcs[id_name]; else fname = compiled_simple_funcs[id_name]; if ( nargs > 0 ) gen = fname + "(" + GenArgs(params, args_l) + ", f__CPP)"; else gen = fname + "(f__CPP)"; return NativeToGT(gen, t, gt); } // If the function isn't a BiF, then it will have been // declared as a ValPtr (or a FuncValPtr, if a local), // and we need to convert it to a Func*. // // If it is a BiF *that's also a global variable*, then // we need to look up the BiF version of the global. if ( pfs->BiFGlobals().count(f_id) == 0 ) gen += +"->AsFunc()"; else if ( accessed_globals.count(f_id) > 0 ) // The BiF version has an extra "_", per AddBiF(..., true). gen = globals[string(id_name) + "_"]; } else // Indirect call. gen = string("(") + gen + ")->AsFunc()"; string invoke_func; if ( is_async ) invoke_func = "when_invoke__CPP"; else if ( top_level || t->Tag() == TYPE_VOID ) { ASSERT(top_level); invoke_func = "invoke_void__CPP"; } else invoke_func = "invoke__CPP"; auto args_list = string(", {") + GenExpr(args_l, GEN_VAL_PTR) + "}"; auto invoker = invoke_func + "(" + gen + args_list + ", f__CPP"; if ( is_async ) invoker += ", (void*) &" + body_name; invoker += ")"; if ( top_level ) // No need to use accessor. return invoker; if ( IsNativeType(t) && gt != GEN_VAL_PTR ) return invoker + NativeAccessor(t); return GenericValPtrToGT(invoker, t, gt); } string CPPCompile::GenInExpr(const Expr* e, GenType gt) { auto op1 = e->GetOp1(); auto op2 = e->GetOp2(); auto t1 = op1->GetType(); auto t2 = op2->GetType(); auto tag1 = t1->Tag(); auto tag2 = t2->Tag(); string gen; if ( tag1 == TYPE_STRING && tag2 == TYPE_TABLE && t2->AsTableType()->IsPatternIndex() ) gen = GenExpr(op2, GEN_DONT_CARE) + "->MatchPattern(" + GenExpr(op1, GEN_NATIVE) + ")"; else if ( tag1 == TYPE_PATTERN ) gen = string("(") + GenExpr(op1, GEN_DONT_CARE) + ")->MatchAnywhere(" + GenExpr(op2, GEN_DONT_CARE) + "->AsString())"; else if ( tag2 == TYPE_STRING ) gen = string("str_in__CPP(") + GenExpr(op1, GEN_DONT_CARE) + "->AsString(), " + GenExpr(op2, GEN_DONT_CARE) + "->AsString())"; else if ( tag1 == TYPE_ADDR && tag2 == TYPE_SUBNET ) gen = string("(") + GenExpr(op2, GEN_DONT_CARE) + ")->Contains(" + GenExpr(op1, GEN_VAL_PTR) + "->Get())"; else if ( tag2 == TYPE_VECTOR ) gen = GenExpr(op2, GEN_DONT_CARE) + "->Has(" + GenExpr(op1, GEN_NATIVE) + ")"; else gen = string("(") + GenExpr(op2, GEN_DONT_CARE) + "->Find(index_val__CPP({" + GenExpr(op1, GEN_VAL_PTR) + "})) ? true : false)"; return NativeToGT(gen, e->GetType(), gt); } string CPPCompile::GenFieldExpr(const FieldExpr* fe, GenType gt) { auto& t = fe->GetType(); auto r = fe->GetOp1(); auto f = fe->Field(); auto f_s = GenField(r, f); string gen; if ( IsNativeType(t) ) { auto nt = TypeName(t); gen = string("field_access_") + nt + "__CPP(" + GenExpr(r, GEN_VAL_PTR) + ", " + f_s + ")"; return NativeToGT(gen, t, gt); } switch ( t->Tag() ) { case TYPE_FILE: case TYPE_FUNC: case TYPE_VOID: gen = string("field_access__CPP(") + GenExpr(r, GEN_VAL_PTR) + ", " + f_s + ")"; return GenericValPtrToGT(gen, t, gt); default: { auto nt = TypeName(t); return string("field_access_") + nt + "__CPP(" + GenExpr(r, GEN_VAL_PTR) + ", " + f_s + ")"; } } } string CPPCompile::GenHasFieldExpr(const HasFieldExpr* hfe, GenType gt) { auto r = hfe->GetOp1(); auto f = hfe->Field(); auto f_s = GenField(r, f); auto gen = GenExpr(r, GEN_DONT_CARE) + "->HasField(" + f_s + ")"; return NativeToGT(gen, hfe->GetType(), gt); } string CPPCompile::GenIndexExpr(const Expr* e, GenType gt) { auto aggr = e->GetOp1(); const auto& aggr_t = aggr->GetType(); bool inside_when = e->AsIndexExpr()->IsInsideWhen(); string gen; string func; if ( aggr_t->Tag() == TYPE_TABLE ) { auto ind_expr = e->GetOp2()->AsListExpr()->Exprs()[0]; auto is_pat_str_ind = false; if ( aggr_t->AsTableType()->IsPatternIndex() && ind_expr->GetType()->Tag() == TYPE_STRING ) is_pat_str_ind = true; if ( inside_when ) { if ( is_pat_str_ind ) func = "when_index_patstr__CPP"; else func = "when_index_table__CPP"; } else { if ( is_pat_str_ind ) func = "index_patstr_table__CPP"; else func = "index_table__CPP"; } gen = func + "(" + GenExpr(aggr, GEN_NATIVE) + ", {" + GenExpr(e->GetOp2(), GEN_VAL_PTR) + "})"; } else if ( aggr_t->Tag() == TYPE_VECTOR ) { const auto& op2 = e->GetOp2(); const auto& t2 = op2->GetType(); ASSERT(t2->Tag() == TYPE_LIST); if ( t2->Tag() == TYPE_LIST && t2->AsTypeList()->GetTypes().size() == 2 ) { auto& inds = op2->AsListExpr()->Exprs(); auto first = inds[0]; auto last = inds[1]; func = inside_when ? "when_index_slice__CPP" : "index_slice"; gen = func + "(" + GenExpr(aggr, GEN_VAL_PTR) + ".get(), " + GenExpr(first, GEN_NATIVE) + ", " + GenExpr(last, GEN_NATIVE) + ")"; } else { func = inside_when ? "when_index_vec__CPP" : "index_vec__CPP"; gen = func + "(" + GenExpr(aggr, GEN_NATIVE) + ", " + GenExpr(e->GetOp2(), GEN_NATIVE) + ")"; } } else if ( aggr_t->Tag() == TYPE_STRING ) gen = string("index_string__CPP(") + GenExpr(aggr, GEN_NATIVE) + ", {" + GenExpr(e->GetOp2(), GEN_VAL_PTR) + "})"; return GenericValPtrToGT(gen, e->GetType(), gt); } string CPPCompile::GenAssignExpr(const Expr* e, GenType gt, bool top_level) { auto op1 = e->GetOp1()->AsRefExprPtr()->GetOp1(); auto op2 = e->GetOp2(); const auto& t1 = op1->GetType(); const auto& t2 = op2->GetType(); auto rhs_native = GenExpr(op2, GEN_NATIVE); auto rhs_val_ptr = GenExpr(op2, GEN_VAL_PTR); auto lhs_is_any = t1->Tag() == TYPE_ANY; auto rhs_is_any = t2->Tag() == TYPE_ANY; if ( lhs_is_any && ! rhs_is_any ) rhs_native = rhs_val_ptr; if ( rhs_is_any && ! lhs_is_any && t1->Tag() != TYPE_LIST ) rhs_native = rhs_val_ptr = GenericValPtrToGT(rhs_val_ptr, t1, GEN_NATIVE); auto gen = GenAssign(op1, op2, rhs_native, rhs_val_ptr, gt, top_level); auto av = e->AsAssignExpr()->AssignVal(); if ( av ) { auto av_e = make_intrusive(av); auto av_gen = GenExpr(av_e, gt, false); return string("(") + gen + ", " + av_gen + ")"; } else return gen; } string CPPCompile::GenAddToExpr(const Expr* e, GenType gt, bool top_level) { const auto& t = e->GetType(); auto lhs = e->GetOp1(); auto rhs = e->GetOp2(); std::string add_to_func; if ( t->Tag() == TYPE_VECTOR ) { auto& rt = rhs->GetType(); ASSERT(e->Tag() == EXPR_ADD_TO); if ( static_cast(e)->IsVectorElemAppend() ) add_to_func = "vector_append__CPP"; else add_to_func = "vector_vec_append__CPP"; } else if ( t->Tag() == TYPE_PATTERN ) add_to_func = "re_append__CPP"; else if ( t->Tag() == TYPE_TABLE ) add_to_func = "table_append__CPP"; if ( ! add_to_func.empty() ) { auto gen = add_to_func + "(" + GenExpr(lhs, GEN_VAL_PTR) + ", " + GenExpr(rhs, GEN_VAL_PTR) + ")"; return GenericValPtrToGT(gen, t, gt); } // Second GetOp1 is because if we get this far, LHS will be a RefExpr. lhs = lhs->GetOp1(); if ( t->Tag() == TYPE_STRING ) { auto rhs_native = GenBinaryString(e, GEN_NATIVE, "+="); auto rhs_val_ptr = GenBinaryString(e, GEN_VAL_PTR, "+="); return GenAssign(lhs, nullptr, rhs_native, rhs_val_ptr, gt, top_level); } if ( lhs->Tag() != EXPR_NAME || lhs->AsNameExpr()->Id()->IsGlobal() ) { // LHS is a compound, or a global (and thus doesn't // equate to a C++ variable); expand x += y to x = x + y rhs = make_intrusive(lhs, rhs); auto assign = make_intrusive(lhs, rhs, false, nullptr, nullptr, false); // Make sure any newly created types are known to // the profiler. (void)pfs->HashType(rhs->GetType()); (void)pfs->HashType(assign->GetType()); return GenExpr(assign, gt, top_level); } return GenBinary(e, gt, "+="); } string CPPCompile::GenRemoveFromExpr(const Expr* e, GenType gt, bool top_level) { const auto& t = e->GetType(); auto lhs = e->GetOp1(); auto rhs = e->GetOp2(); if ( t->Tag() == TYPE_TABLE && same_type(lhs->GetType(), rhs->GetType()) ) { auto gen = std::string("table_remove_from__CPP(") + GenExpr(lhs, GEN_VAL_PTR) + ", " + GenExpr(rhs, GEN_VAL_PTR) + ")"; return GenericValPtrToGT(gen, t, gt); } // Second GetOp1 is because if we get this far, LHS will be a RefExpr. lhs = lhs->GetOp1(); if ( lhs->Tag() != EXPR_NAME || lhs->AsNameExpr()->Id()->IsGlobal() ) { // LHS is a compound, or a global (and thus doesn't // equate to a C++ variable); expand x -= y to x = x - y rhs = make_intrusive(lhs, rhs); auto assign = make_intrusive(lhs, rhs, false, nullptr, nullptr, false); // Make sure any newly created types are known to // the profiler. (void)pfs->HashType(rhs->GetType()); (void)pfs->HashType(assign->GetType()); return GenExpr(assign, gt, top_level); } return GenBinary(e, gt, "-="); } string CPPCompile::GenSizeExpr(const Expr* e, GenType gt) { const auto& t = e->GetType(); const auto& t1 = e->GetOp1()->GetType(); auto it = t1->InternalType(); auto gen = GenExpr(e->GetOp1(), GEN_NATIVE); if ( t1->Tag() == TYPE_BOOL ) gen = string("((") + gen + ") ? 1 : 0)"; else if ( it == TYPE_INTERNAL_UNSIGNED ) // no-op ; else if ( it == TYPE_INTERNAL_INT ) gen = string("iabs__CPP(") + gen + ")"; else if ( it == TYPE_INTERNAL_DOUBLE ) gen = string("fabs__CPP(") + gen + ")"; else if ( gt == GEN_NATIVE && (t1->Tag() == TYPE_TABLE || t1->Tag() == TYPE_VECTOR) ) return gen + "->Size()"; else return GenericValPtrToGT(gen + "->SizeVal()", t, gt); return NativeToGT(gen, t, gt); } string CPPCompile::GenScheduleExpr(const Expr* e) { auto s = static_cast(e); auto when = s->When(); auto event = s->Event(); string event_name(event->Handler()->Name()); RegisterEvent(event_name); string when_s = GenExpr(when, GEN_NATIVE); if ( when->GetType()->Tag() == TYPE_INTERVAL ) when_s += " + run_state::network_time"; return string("schedule__CPP(") + when_s + ", " + globals[event_name] + "_ev, { " + GenExpr(event->Args(), GEN_VAL_PTR) + " })"; } string CPPCompile::GenLambdaExpr(const Expr* e) { auto l = static_cast(e); auto& body = l->Ingredients()->Body(); return GenLambdaExpr(e, GenLambdaClone(l, false)); } string CPPCompile::GenLambdaExpr(const Expr* e, string capture_args) { auto l = static_cast(e); auto name = Canonicalize(l->Name()) + "_lb_cl"; auto cl_args = string("\"") + name + "\"" + std::move(capture_args); auto body = string("make_intrusive<") + name + ">(" + cl_args + ")"; auto func = string("make_intrusive(\"") + l->Name() + "\", cast_intrusive(" + GenTypeName(l->GetType()) + "), " + body + ")"; return string("make_intrusive(") + func + ")"; } string CPPCompile::GenIsExpr(const Expr* e, GenType gt) { auto ie = static_cast(e); auto gen = string("can_cast_value_to_type(") + GenExpr(ie->GetOp1(), GEN_VAL_PTR) + ".get(), " + GenTypeName(ie->TestType()) + ".get())"; return NativeToGT(gen, ie->GetType(), gt); } string CPPCompile::GenArithCoerceExpr(const Expr* e, GenType gt) { const auto& t = e->GetType(); auto op = e->GetOp1(); if ( same_type(t, op->GetType()) ) return GenExpr(op, gt); if ( t->Tag() == TYPE_VECTOR ) return string("vector_coerce_to__CPP(") + GenExpr(op, GEN_NATIVE) + ", " + GenTypeName(t) + ")"; string cast_name; switch ( t->InternalType() ) { case TYPE_INTERNAL_INT: cast_name = "zeek_int_t"; break; case TYPE_INTERNAL_UNSIGNED: cast_name = "zeek_uint_t"; break; case TYPE_INTERNAL_DOUBLE: cast_name = "double"; break; default: reporter->InternalError("bad type in arithmetic coercion"); } return NativeToGT(cast_name + "(" + GenExpr(op, GEN_NATIVE) + ")", t, gt); } string CPPCompile::GenRecordCoerceExpr(const Expr* e) { auto rc = static_cast(e); auto op1 = rc->GetOp1(); const auto& from_type = op1->GetType(); const auto& to_type = rc->GetType(); if ( same_type(from_type, to_type) ) // Elide coercion. return GenExpr(op1, GEN_VAL_PTR); const auto& map = rc->Map(); auto type_var = GenTypeName(to_type); return string("coerce_to_record(cast_intrusive(") + type_var + "), " + GenExpr(op1, GEN_VAL_PTR) + ".get(), " + GenIntVector(map) + ")"; } string CPPCompile::GenTableCoerceExpr(const Expr* e) { auto tc = static_cast(e); const auto& t = tc->GetType(); auto op1 = tc->GetOp1(); return string("table_coerce__CPP(") + GenExpr(op1, GEN_VAL_PTR) + ", " + GenTypeName(t) + ")"; } string CPPCompile::GenVectorCoerceExpr(const Expr* e) { auto vc = static_cast(e); const auto& op = vc->GetOp1(); const auto& t = vc->GetType(); return string("vector_coerce__CPP(" + GenExpr(op, GEN_VAL_PTR) + ", " + GenTypeName(t) + ")"); } string CPPCompile::GenRecordConstructorExpr(const Expr* e) { auto rc = static_cast(e); const auto& t = rc->GetType(); const auto& exprs = rc->Op()->AsListExpr()->Exprs(); auto n = exprs.length(); string vals; for ( auto i = 0; i < n; ++i ) { const auto& expr = exprs[i]; ASSERT(expr->Tag() == EXPR_FIELD_ASSIGN); vals += GenExpr(expr->GetOp1(), GEN_VAL_PTR); if ( i < n - 1 ) vals += ", "; } vals = string("{") + vals + "}"; const auto& map = rc->Map(); if ( map ) { string map_vals; for ( auto m : *map ) { if ( ! map_vals.empty() ) map_vals += ", "; map_vals += to_string(m); } map_vals = string("{") + map_vals + "}"; return string("record_constructor_map__CPP(") + vals + ", " + map_vals + ", cast_intrusive(" + GenTypeName(t) + "))"; } else return string("record_constructor__CPP(") + vals + ", cast_intrusive(" + GenTypeName(t) + "))"; } string CPPCompile::GenSetConstructorExpr(const Expr* e) { auto sc = static_cast(e); const auto& t = sc->GetType(); const auto& attrs = sc->GetAttrs(); string attr_tags; string attr_vals; BuildAttrs(attrs, attr_tags, attr_vals); return string("set_constructor__CPP(") + GenExprs(sc->GetOp1().get()) + ", " + "cast_intrusive(" + GenTypeName(t) + "), " + attr_tags + ", " + attr_vals + ")"; } string CPPCompile::GenTableConstructorExpr(const Expr* e) { auto tc = static_cast(e); const auto& t = tc->GetType(); const auto& attrs = tc->GetAttrs(); string attr_tags; string attr_vals; BuildAttrs(attrs, attr_tags, attr_vals); string indices; string vals; const auto& exprs = tc->GetOp1()->AsListExpr()->Exprs(); auto n = exprs.length(); for ( auto i = 0; i < n; ++i ) { const auto& expr = exprs[i]; ASSERT(expr->Tag() == EXPR_ASSIGN); auto index = expr->GetOp1(); auto v = expr->GetOp2(); if ( index->Tag() == EXPR_LIST ) // Multiple indices. indices += "index_val__CPP({" + GenExpr(index, GEN_VAL_PTR) + "})"; else indices += GenExpr(index, GEN_VAL_PTR); vals += GenExpr(v, GEN_VAL_PTR); if ( i < n - 1 ) { indices += ", "; vals += ", "; } } return string("table_constructor__CPP({") + indices + "}, {" + vals + "}, " + "cast_intrusive(" + GenTypeName(t) + "), " + attr_tags + ", " + attr_vals + ")"; } string CPPCompile::GenVectorConstructorExpr(const Expr* e) { auto vc = static_cast(e); const auto& t = vc->GetType(); return string("vector_constructor__CPP({") + GenExpr(vc->GetOp1(), GEN_VAL_PTR) + "}, " + "cast_intrusive(" + GenTypeName(t) + "))"; } string CPPCompile::GenVal(const ValPtr& v) { const auto& t = v->GetType(); auto tag = t->Tag(); auto it = t->InternalType(); if ( tag == TYPE_BOOL ) return string(v->IsZero() ? "false" : "true"); if ( tag == TYPE_ENUM ) return GenEnum(t, v); if ( tag == TYPE_PORT ) return Fmt(int(v->AsCount())); if ( it == TYPE_INTERNAL_DOUBLE ) return Fmt(v->AsDouble()); ODesc d; d.SetQuotes(true); v->Describe(&d); return d.Description(); } string CPPCompile::GenUnary(const Expr* e, GenType gt, const char* op, const char* vec_op) { if ( e->GetType()->Tag() == TYPE_VECTOR ) return GenVectorOp(e, GenExpr(e->GetOp1(), GEN_NATIVE), vec_op); // Look for coercions that the interpreter does implicitly. auto op1 = e->GetOp1(); if ( op1->GetType()->Tag() == TYPE_COUNT && (e->Tag() == EXPR_POSITIVE || e->Tag() == EXPR_NEGATE) ) op1 = make_intrusive(op1, TYPE_INT); return NativeToGT(string(op) + "(" + GenExpr(op1, GEN_NATIVE) + ")", e->GetType(), gt); } string CPPCompile::GenBinary(const Expr* e, GenType gt, const char* op, const char* vec_op) { const auto& op1 = e->GetOp1(); const auto& op2 = e->GetOp2(); auto t = op1->GetType(); if ( e->GetType()->Tag() == TYPE_VECTOR ) { auto gen1 = GenExpr(op1, GEN_NATIVE); auto gen2 = GenExpr(op2, GEN_NATIVE); if ( t->Tag() == TYPE_VECTOR && t->Yield()->Tag() == TYPE_STRING && op2->GetType()->Tag() == TYPE_VECTOR ) return string("str_vec_op_") + vec_op + "__CPP(" + gen1 + ", " + gen2 + ")"; return GenVectorOp(e, gen1, gen2, vec_op); } if ( t->IsSet() ) return GenBinarySet(e, gt, op); // The following is only used for internal int/uint/double // operations. For those, it holds the prefix we use to // distinguish different instances of inlined functions // employed to support an operation. string flavor; switch ( t->InternalType() ) { case TYPE_INTERNAL_INT: flavor = "i"; break; case TYPE_INTERNAL_UNSIGNED: flavor = "u"; break; case TYPE_INTERNAL_DOUBLE: flavor = "f"; break; case TYPE_INTERNAL_STRING: return GenBinaryString(e, gt, op); case TYPE_INTERNAL_ADDR: return GenBinaryAddr(e, gt, op); case TYPE_INTERNAL_SUBNET: return GenBinarySubNet(e, gt, op); default: if ( t->Tag() == TYPE_PATTERN ) return GenBinaryPattern(e, gt, op); break; } auto g1 = GenExpr(e->GetOp1(), GEN_NATIVE); auto g2 = GenExpr(e->GetOp2(), GEN_NATIVE); string gen; if ( e->Tag() == EXPR_DIVIDE ) gen = flavor + "div__CPP(" + g1 + ", " + g2 + ")"; else if ( e->Tag() == EXPR_MOD ) gen = flavor + "mod__CPP(" + g1 + ", " + g2 + ")"; else gen = string("(") + g1 + ")" + op + "(" + g2 + ")"; return NativeToGT(gen, e->GetType(), gt); } string CPPCompile::GenBinarySet(const Expr* e, GenType gt, const char* op) { auto v1 = GenExpr(e->GetOp1(), GEN_DONT_CARE) + "->AsTableVal()"; auto v2 = GenExpr(e->GetOp2(), GEN_DONT_CARE) + "->AsTableVal()"; string res; switch ( e->Tag() ) { case EXPR_AND: res = v1 + "->Intersection(*" + v2 + ")"; break; case EXPR_OR: res = v1 + "->Union(" + v2 + ")"; break; case EXPR_SUB: res = v1 + "->TakeOut(" + v2 + ")"; break; case EXPR_EQ: res = v1 + "->EqualTo(*" + v2 + ")"; break; case EXPR_NE: res = string("! ") + v1 + "->EqualTo(*" + v2 + ")"; break; case EXPR_LE: res = v1 + "->IsSubsetOf(*" + v2 + ")"; break; case EXPR_LT: res = string("(") + v1 + "->IsSubsetOf(*" + v2 + ") &&" + v1 + "->Size() < " + v2 + "->Size())"; break; default: reporter->InternalError("bad type in CPPCompile::GenBinarySet"); } return NativeToGT(res, e->GetType(), gt); } string CPPCompile::GenBinaryString(const Expr* e, GenType gt, const char* op) { auto v1 = GenExpr(e->GetOp1(), GEN_DONT_CARE) + "->AsString()"; auto v2 = GenExpr(e->GetOp2(), GEN_DONT_CARE) + "->AsString()"; string res; if ( e->Tag() == EXPR_ADD || e->Tag() == EXPR_ADD_TO ) res = string("str_concat__CPP(") + v1 + ", " + v2 + ")"; else res = string("(Bstr_cmp(") + v1 + ", " + v2 + ") " + op + " 0)"; return NativeToGT(res, e->GetType(), gt); } string CPPCompile::GenBinaryPattern(const Expr* e, GenType gt, const char* op) { auto v1 = GenExpr(e->GetOp1(), GEN_DONT_CARE) + "->AsPattern()"; auto v2 = GenExpr(e->GetOp2(), GEN_DONT_CARE) + "->AsPattern()"; auto func = e->Tag() == EXPR_AND ? "RE_Matcher_conjunction" : "RE_Matcher_disjunction"; return NativeToGT(string("make_intrusive(") + func + "(" + v1 + ", " + v2 + "))", e->GetType(), gt); } string CPPCompile::GenBinaryAddr(const Expr* e, GenType gt, const char* op) { auto v1 = GenExpr(e->GetOp1(), GEN_DONT_CARE) + "->AsAddr()"; if ( e->Tag() == EXPR_MASK ) { auto gen = string("addr_mask__CPP(") + v1 + ", " + GenExpr(e->GetOp2(), GEN_NATIVE) + ")"; return NativeToGT(gen, e->GetType(), gt); } auto v2 = GenExpr(e->GetOp2(), GEN_DONT_CARE) + "->AsAddr()"; return NativeToGT(v1 + op + v2, e->GetType(), gt); } string CPPCompile::GenBinarySubNet(const Expr* e, GenType gt, const char* op) { auto v1 = GenExpr(e->GetOp1(), GEN_DONT_CARE) + "->AsSubNet()"; auto v2 = GenExpr(e->GetOp2(), GEN_DONT_CARE) + "->AsSubNet()"; return NativeToGT(v1 + op + v2, e->GetType(), gt); } string CPPCompile::GenEQ(const Expr* e, GenType gt, const char* op, const char* vec_op) { auto op1 = e->GetOp1(); auto op2 = e->GetOp2(); if ( e->GetType()->Tag() == TYPE_VECTOR ) { auto gen1 = GenExpr(op1, GEN_NATIVE); auto gen2 = GenExpr(op2, GEN_NATIVE); return GenVectorOp(e, gen1, gen2, vec_op); } auto tag = op1->GetType()->Tag(); string negated(e->Tag() == EXPR_EQ ? "" : "! "); if ( tag == TYPE_PATTERN ) { auto gen1 = GenExpr(op1, GEN_DONT_CARE); auto gen2 = GenExpr(op2, GEN_DONT_CARE); string gen; if ( op2->GetType()->Tag() == TYPE_PATTERN ) { gen1 += "->AsPattern()->PatternText()"; gen2 += "->AsPattern()->PatternText()"; gen = "(strcmp(" + gen1 + ", " + gen2 + ") == 0)"; } else gen = gen1 + "->MatchExactly(" + gen2 + "->AsString())"; return NativeToGT(negated + gen, e->GetType(), gt); } if ( tag == TYPE_FUNC ) { auto gen_f1 = GenExpr(op1, GEN_DONT_CARE); auto gen_f2 = GenExpr(op2, GEN_DONT_CARE); gen_f1 += "->AsFunc()"; gen_f2 += "->AsFunc()"; auto gen = string("(") + gen_f1 + "==" + gen_f2 + ")"; return NativeToGT(negated + gen, e->GetType(), gt); } return GenBinary(e, gt, op, vec_op); } string CPPCompile::GenAssign(const ExprPtr& lhs, const ExprPtr& rhs, const string& rhs_native, const string& rhs_val_ptr, GenType gt, bool top_level) { switch ( lhs->Tag() ) { case EXPR_NAME: return GenDirectAssign(lhs, rhs_native, rhs_val_ptr, gt, top_level); case EXPR_INDEX: return GenIndexAssign(lhs, rhs, rhs_val_ptr, gt, top_level); case EXPR_FIELD: return GenFieldAssign(lhs, rhs, rhs_native, rhs_val_ptr, gt, top_level); case EXPR_LIST: return GenListAssign(lhs, rhs); default: reporter->InternalError("bad assignment node in CPPCompile::GenExpr"); return "XXX"; } } string CPPCompile::GenDirectAssign(const ExprPtr& lhs, const string& rhs_native, const string& rhs_val_ptr, GenType gt, bool top_level) { auto n = lhs->AsNameExpr()->Id(); if ( n->IsBlank() ) return rhs_native; const auto& name = IDNameStr(n); string gen; if ( n->IsGlobal() ) { const auto& t = n->GetType(); const auto& gn = globals[n->Name()]; if ( t->Tag() == TYPE_FUNC && t->AsFuncType()->Flavor() == FUNC_FLAVOR_EVENT ) { gen = string("set_event__CPP(") + gn + ", " + rhs_val_ptr + ", " + gn + "_ev)"; if ( ! top_level ) gen = GenericValPtrToGT(gen, n->GetType(), gt); } else if ( top_level ) gen = gn + "->SetVal(" + rhs_val_ptr + ")"; else { gen = string("set_global__CPP(") + gn + ", " + rhs_val_ptr + ")"; gen = GenericValPtrToGT(gen, n->GetType(), gt); } } else { gen = name + " = " + rhs_native; if ( ! top_level ) gen = NativeToGT("(" + gen + ")", n->GetType(), gt); } return gen; } string CPPCompile::GenIndexAssign(const ExprPtr& lhs, const ExprPtr& rhs, const string& rhs_val_ptr, GenType gt, bool top_level) { auto gen = string("assign_to_index__CPP("); gen += GenExpr(lhs->GetOp1(), GEN_VAL_PTR) + ", " + "index_val__CPP({" + GenExpr(lhs->GetOp2(), GEN_VAL_PTR) + "}), " + rhs_val_ptr + ")"; if ( ! top_level ) gen = GenericValPtrToGT(gen, rhs->GetType(), gt); return gen; } string CPPCompile::GenFieldAssign(const ExprPtr& lhs, const ExprPtr& rhs, const string& rhs_native, const string& rhs_val_ptr, GenType gt, bool top_level) { auto rec = lhs->GetOp1(); auto rec_gen = GenExpr(rec, GEN_VAL_PTR); auto field = GenField(rec, lhs->AsFieldExpr()->Field()); if ( ! top_level ) { auto gen = string("assign_field__CPP(") + rec_gen + ", " + field + ", " + rhs_val_ptr + ")"; return GenericValPtrToGT(gen, rhs->GetType(), gt); } auto rt = rhs ? rhs->GetType() : nullptr; if ( rt && (IsNativeType(rt) || rt->Tag() == TYPE_STRING) ) return rec_gen + "->Assign(" + field + ", " + rhs_native + ")"; else return rec_gen + "->Assign(" + field + ", " + rhs_val_ptr + ")"; } string CPPCompile::GenListAssign(const ExprPtr& lhs, const ExprPtr& rhs) { if ( rhs->Tag() != EXPR_NAME ) reporter->InternalError("compound RHS expression in multi-assignment"); string gen; const auto& vars = lhs->AsListExpr()->Exprs(); auto n = vars.length(); for ( auto i = 0; i < n; ++i ) { const auto& var_i = vars[i]; if ( var_i->Tag() != EXPR_NAME ) reporter->InternalError("compound LHS expression in multi-assignment"); const auto& t_i = var_i->GetType(); auto var = var_i->AsNameExpr(); auto rhs_i_base = GenExpr(rhs, GEN_DONT_CARE); rhs_i_base += "->AsListVal()->Idx(" + Fmt(i) + ")"; auto rhs_i = GenericValPtrToGT(rhs_i_base, t_i, GEN_NATIVE); gen += IDNameStr(var->Id()) + " = " + rhs_i; if ( i < n - 1 ) gen += ", "; } return "(" + gen + ")"; } string CPPCompile::GenVectorOp(const Expr* e, string op, const char* vec_op) { auto t = e->GetType(); auto gen_t = GenTypeName(t); auto gen = string("vec_op_") + vec_op + "__CPP(" + op + ", " + gen_t + ")"; if ( ! IsArithmetic(t->Yield()->Tag()) ) gen = string("vector_coerce_to__CPP(") + gen + ", " + gen_t + ")"; return gen; } string CPPCompile::GenVectorOp(const Expr* e, string op1, string op2, const char* vec_op) { auto& op1_t = e->GetOp1()->GetType(); auto& op2_t = e->GetOp2()->GetType(); if ( op1_t->Tag() != TYPE_VECTOR || op2_t->Tag() != TYPE_VECTOR ) { // This is a deprecated mixed-scalar-and-vector operation. // We don't support these. Arrange for linking errors. reporter->Error("C++ generation does not support deprecated scalar-mixed-with-vector operations"); return "vec_scalar_mixed_with_vector()"; } auto invoke = string(vec_op) + "__CPP(" + op1 + ", " + op2 + ")"; auto tag2 = op2_t->Yield()->Tag(); if ( tag2 == TYPE_STRING ) return string("str_vec_op_") + invoke; if ( tag2 == TYPE_PATTERN ) return string("pat_vec_op_") + invoke; auto gen = string("vec_op_") + invoke; auto yt = e->GetType()->Yield()->Tag(); if ( ! IsArithmetic(yt) && yt != TYPE_STRING ) gen = string("vector_coerce_to__CPP(") + gen + ", " + GenTypeName(e->GetType()) + ")"; return gen; } string CPPCompile::GenLambdaClone(const LambdaExpr* l, bool all_deep) { auto& ids = l->OuterIDs(); const auto& captures = l->GetType()->GetCaptures(); string cl_args; for ( const auto& id : ids ) { const auto& id_t = id->GetType(); auto arg = CaptureName(id); if ( captures && ! IsNativeType(id_t) ) { for ( const auto& c : *captures ) if ( id == c.Id() && (c.IsDeepCopy() || all_deep) ) arg = string("cast_intrusive<") + TypeName(id_t) + ">(" + arg + "->Clone())"; } cl_args += ", " + arg; } return cl_args; } string CPPCompile::GenIntVector(const vector& vec) { string res("{ "); for ( auto i = 0u; i < vec.size(); ++i ) { res += Fmt(vec[i]); if ( i < vec.size() - 1 ) res += ", "; } return res + " }"; } string CPPCompile::GenField(const ExprPtr& rec, int field) { auto t = TypeRep(rec->GetType()); auto rt = t->AsRecordType(); if ( field < rt->NumOrigFields() ) // Can use direct access. return Fmt(field); // Need to dynamically map the field. int mapping_slot; auto rfm = record_field_mappings.find(rt); if ( rfm != record_field_mappings.end() && rfm->second.count(field) > 0 ) // We're already tracking this field. mapping_slot = rfm->second[field]; else { // New mapping. mapping_slot = num_rf_mappings++; auto pt = processed_types.find(rt); ASSERT(pt != processed_types.end()); auto rt_offset = pt->second->Offset(); field_decls.emplace_back(rt_offset, rt->FieldDecl(field)); if ( rfm != record_field_mappings.end() ) // We're already tracking this record. rfm->second[field] = mapping_slot; else { // Need to start tracking this record. unordered_map rt_mapping; rt_mapping[field] = mapping_slot; record_field_mappings[rt] = rt_mapping; } } return string("field_mapping[") + Fmt(mapping_slot) + "]"; } string CPPCompile::GenEnum(const TypePtr& t, const ValPtr& ev) { auto et = TypeRep(t)->AsEnumType(); auto v = ev->AsEnum(); if ( ! et->HasRedefs() ) // Can use direct access. return "zeek_int_t(" + std::to_string(v) + ")"; // Need to dynamically map the access. int mapping_slot; auto evm = enum_val_mappings.find(et); if ( evm != enum_val_mappings.end() && evm->second.count(v) > 0 ) // We're already tracking this value. mapping_slot = evm->second[v]; else { // New mapping. mapping_slot = num_ev_mappings++; string enum_name = et->Lookup(v); bool create_if_missing = standalone && obj_matches_opt_files(ev); enum_names.emplace_back(EnumMappingInfo{TypeOffset(t), std::move(enum_name), create_if_missing}); if ( evm != enum_val_mappings.end() ) { // We're already tracking this enum. evm->second[v] = mapping_slot; } else { // Need to start tracking this enum. unordered_map et_mapping; et_mapping[v] = mapping_slot; enum_val_mappings[et] = et_mapping; } } return string("enum_mapping[") + Fmt(mapping_slot) + "]"; } } // namespace zeek::detail