|
| 1 | +// ============================================================================ |
| 2 | +// FRAMEWORK-BEHAVIOR · PIPELINES (a component registered into a framework pipeline) |
| 3 | +// |
| 4 | +// group.AddEndpointFilter<ClinicFilter>(); // ASP.NET runs InvokeAsync |
| 5 | +// services.AddGrpc(o => o.Interceptors.Add<Errors>()); // gRPC runs UnaryServerHandler |
| 6 | +// options.AddInterceptors(new AuditInterceptor()); // EF runs SavingChangesAsync |
| 7 | +// await db.SaveChangesAsync(); // ...here |
| 8 | +// |
| 9 | +// The registration names the component's TYPE (or hands over an instance) and never |
| 10 | +// its hook, so no call site names InvokeAsync, UnaryServerHandler or SavingChangesAsync. |
| 11 | +// entry-points.dl already says the framework calls them; nothing said WHO put them where |
| 12 | +// they are called, so `impact` on the hook had no caller and `path` from a save to the |
| 13 | +// interceptor found no chain. |
| 14 | +// |
| 15 | +// Three facts, each ADDED beside what the resolution phases derived: |
| 16 | +// 1. the registration site calls every hook of the registered type. A call_chain_edge |
| 17 | +// of tier `callback_registered` (a component handed over and run by whoever holds |
| 18 | +// it): consumers rank it below a resolved call and print it as `registered`. |
| 19 | +// 2. an EF save calls the save hooks of every interceptor the project registers with |
| 20 | +// AddInterceptors. Tier `event_dispatch`, as a mediator's Send reaching its handler |
| 21 | +// (dispatch.dl): same call stack, no call site names the hook. |
| 22 | +// 3. a filter or a server interceptor WRAPS the endpoints it is added to: the endpoint |
| 23 | +// depends on it (a request to the endpoint runs it first). framework_edge(Endpoint, |
| 24 | +// Hook, Mechanism, Detail, Confidence), the relation python/ emits for the same kind |
| 25 | +// of hop, which impact reads as a `framework` dependent of the hook. Not a call. |
| 26 | +// |
| 27 | +// Every framework name is a knob (knobs.dl: cs_pipeline_register, cs_pipeline_wraps, |
| 28 | +// cs_ef_*), over the cs_framework_callback table that names each base's hooks. |
| 29 | +// ============================================================================ |
| 30 | + |
| 31 | +// ── the registration sites ────────────────────────────────────────────────── |
| 32 | +pl_site(e, mn, rm) :- csite("client", e), call_callee_name("client", e, mn), cs_pipeline_register(mn, rm, _), |
| 33 | + rm = "*". |
| 34 | +pl_site(e, mn, rm) :- csite("client", e), call_callee_name("client", e, mn), cs_pipeline_register(mn, rm, _), |
| 35 | + rm != "*", call_receiver_expr("client", e, r), expr_kind("client", r, "MEMBER_ACCESS"), |
| 36 | + expr_member_name_child("client", r, nm), expr_written_name("client", nm, rm). |
| 37 | + |
| 38 | +// the registered type: the call's own type argument... |
| 39 | +pl_registers(e, gk) :- pl_site(e, mn, rm), cs_pipeline_register(mn, rm, "type"), |
| 40 | + type_ref_owner("client", tr, e, "EXPRESSION"), type_ref_context("client", tr, "METHOD_TYPE_ARGUMENT"), |
| 41 | + !type_ref_parent("client", tr, _, _, _), type_ref_resolves("client", tr, gk). |
| 42 | +// ...or the type of an argument: `new T(..)`, a typed local, parameter or field, and |
| 43 | +// `sp.GetRequiredService<T>()`, whose type is its type argument. |
| 44 | +pl_registers(e, gk) :- pl_site(e, mn, rm), cs_pipeline_register(mn, rm, "value"), http_arg(e, _, a), |
| 45 | + expr_type("client", a, gk). |
| 46 | +pl_registers(e, gk) :- pl_site(e, mn, rm), cs_pipeline_register(mn, rm, "value"), http_arg(e, _, a), |
| 47 | + csite("client", a), call_callee_name("client", a, f), cs_di_resolve(f), |
| 48 | + type_ref_owner("client", tr, a, "EXPRESSION"), type_ref_context("client", tr, "METHOD_TYPE_ARGUMENT"), |
| 49 | + !type_ref_parent("client", tr, _, _, _), type_ref_resolves("client", tr, gk). |
| 50 | + |
| 51 | +// ── the hooks of a registered type ────────────────────────────────────────── |
| 52 | +// A method the framework calls on the type because of the base it derives from |
| 53 | +// (entry-points.dl, cs_framework_type), declared on the type or, when the type does not |
| 54 | +// declare one of that name, inherited from the project's own base. |
| 55 | +pl_hook_name(gk, mn) :- pl_registers(_, gk), cs_framework_type(gk, bn), cs_framework_callback(bn, mn, _), |
| 56 | + !contains("*", mn), !contains("<", mn). |
| 57 | +pl_own_hook(gk, mn, m) :- pl_hook_name(gk, mn), method_in_type("client", gk, m), method_name("client", m, mn). |
| 58 | +pl_has_own(gk, mn) :- pl_own_hook(gk, mn, _). |
| 59 | +pl_hook(gk, m) :- pl_own_hook(gk, _, m). |
| 60 | +pl_hook(gk, m) :- pl_hook_name(gk, mn), !pl_has_own(gk, mn), type_ancestor("client", gk, agk), |
| 61 | + method_in_type("client", agk, m), method_name("client", m, mn). |
| 62 | + |
| 63 | +// 1. the registration site reaches the hooks |
| 64 | +call_chain_edge(e, from, "-", m, "client", "callback_registered", kind) :- pl_registers(e, gk), pl_hook(gk, m), |
| 65 | + call_from("client", e, from), from != m, invocation_site("client", e, kind). |
| 66 | + |
| 67 | +// ── 2. an EF save runs the registered save interceptors ───────────────────── |
| 68 | +pl_ef_interceptor(gk) :- pl_site(e, mn, _), cs_ef_interceptor_register(mn), pl_registers(e, gk). |
| 69 | +pl_async_name(n) :- cs_framework_save_hook(n), strlen(n) > 5, substr(n, strlen(n) - 5, 5) = "Async". |
| 70 | +pl_save_hook(m, "async") :- pl_ef_interceptor(gk), pl_hook(gk, m), method_name("client", m, n), |
| 71 | + cs_framework_save_hook(n), pl_async_name(n). |
| 72 | +pl_save_hook(m, "sync") :- pl_ef_interceptor(gk), pl_hook(gk, m), method_name("client", m, n), |
| 73 | + cs_framework_save_hook(n), !pl_async_name(n). |
| 74 | + |
| 75 | +// a context: a type deriving from a DbContext base, directly or through the project's own |
| 76 | +pl_context(gk) :- cs_framework_type(gk, bn), cs_ef_context_base(bn). |
| 77 | +// A save: SaveChanges(Async) on a context, `db.SaveChangesAsync()`, `SaveChanges()` |
| 78 | +// inside one, `base.SaveChangesAsync()` in its override... |
| 79 | +pl_save_call(e, how) :- call_callee_name("client", e, n), cs_ef_save_call(n, how), call_recv_type("client", e, gk), |
| 80 | + pl_context(gk). |
| 81 | +pl_save_call(e, how) :- call_callee_name("client", e, n), cs_ef_save_call(n, how), call_is_base("client", e), |
| 82 | + expr_ultimate_type_group("client", e, gk), pl_context(gk). |
| 83 | +// ...and through the project's own abstraction over it: `IUnitOfWork.SaveChangesAsync`, |
| 84 | +// which a context implements by inheriting DbContext's. The call resolves to the |
| 85 | +// abstraction's method, which is not a context's own: a context that overrides the save |
| 86 | +// is reached through the dispatch hop and its own `base.` call above. |
| 87 | +// (Read off the resolution relations, not call_chain_edge, which this adds to.) |
| 88 | +pl_save_target(e, mm) :- call_callee_name("client", e, n), cs_ef_save_call(n, _), client_calls_client("client", e, "client", mm). |
| 89 | +pl_save_target(e, mm) :- call_callee_name("client", e, n), cs_ef_save_call(n, _), call_names_bodiless(e, mm). |
| 90 | +pl_save_call(e, how) :- call_callee_name("client", e, n), cs_ef_save_call(n, how), pl_save_target(e, mm), |
| 91 | + method_name("client", mm, n), method_owner("client", ot, mm), type_group("client", ot, ogk), !pl_context(ogk), |
| 92 | + pl_context(cgk), type_ancestor("client", cgk, ogk). |
| 93 | + |
| 94 | +call_chain_edge(e, from, "-", m, "client", "event_dispatch", kind) :- pl_save_call(e, how), pl_save_hook(m, how), |
| 95 | + call_from("client", e, from), from != m, invocation_site("client", e, kind). |
| 96 | + |
| 97 | +// ── 3. what a filter or server interceptor wraps ──────────────────────────── |
| 98 | +pl_wrap_site(e, sc) :- pl_site(e, mn, rm), cs_pipeline_wraps(mn, rm, sc). |
| 99 | + |
| 100 | +// "route": walk back from a builder expression through locals and fluent calls to the |
| 101 | +// endpoint (MapGet..) or route group (MapGroup) it configures, as group_prefix does in |
| 102 | +// destinations.dl. |
| 103 | +pl_map_call(e) :- map_call(e, _). |
| 104 | +pl_up(x, x) :- pl_wrap_site(e, "route"), call_receiver_expr("client", e, x). |
| 105 | +pl_up(x, x) :- map_call(e, _), call_receiver_expr("client", e, x). |
| 106 | +pl_up(x, x) :- map_group_call(e), call_receiver_expr("client", e, x). |
| 107 | +pl_up(x, z) :- pl_up(x, y), ref_denotes("client", y, "LOCAL_VARIABLE", v), var_initializer("client", v, z). |
| 108 | +pl_up(x, z) :- pl_up(x, y), csite("client", y), !pl_map_call(y), !map_group_call(y), call_receiver_expr("client", y, z). |
| 109 | +pl_anchor(x, n) :- pl_up(x, n), map_call(n, _). |
| 110 | +pl_anchor(x, n) :- pl_up(x, n), map_group_call(n). |
| 111 | + |
| 112 | +// the endpoints of a group, and of the groups nested in it |
| 113 | +pl_in_group(e, g) :- map_call(e, _), call_receiver_expr("client", e, r), pl_anchor(r, g), map_group_call(g). |
| 114 | +pl_in_group(e, g) :- pl_in_group(e, g2), call_receiver_expr("client", g2, r), pl_anchor(r, g), map_group_call(g). |
| 115 | + |
| 116 | +pl_wraps(h, e) :- pl_wrap_site(e, "route"), call_receiver_expr("client", e, r), pl_anchor(r, n), map_call(n, _), |
| 117 | + map_handler(n, h). |
| 118 | +pl_wraps(h, e) :- pl_wrap_site(e, "route"), call_receiver_expr("client", e, r), pl_anchor(r, g), map_group_call(g), |
| 119 | + pl_in_group(n, g), map_handler(n, h). |
| 120 | + |
| 121 | +// "grpc_host": every rpc of every service mapped in the file that registers it |
| 122 | +pl_grpc_mapped(mod, gk) :- csite("client", e), call_callee_name("client", e, mn), cs_grpc_map_service(mn), |
| 123 | + call_module("client", e, mod), |
| 124 | + type_ref_owner("client", tr, e, "EXPRESSION"), type_ref_context("client", tr, "METHOD_TYPE_ARGUMENT"), |
| 125 | + !type_ref_parent("client", tr, _, _, _), type_ref_resolves("client", tr, gk). |
| 126 | +pl_wraps(h, e) :- pl_wrap_site(e, "grpc_host"), call_module("client", e, mod), pl_grpc_mapped(mod, gk), |
| 127 | + grpc_serves(h, _), method_owner("client", t, h), type_group("client", t, gk). |
| 128 | + |
| 129 | +// the detail names the registration as written: `AddEndpointFilter`, `Interceptors.Add` |
| 130 | +pl_wrap_detail(e, mn) :- pl_wrap_site(e, _), pl_site(e, mn, "*"). |
| 131 | +pl_wrap_detail(e, d) :- pl_wrap_site(e, _), pl_site(e, mn, rm), rm != "*", d = cat(rm, cat(".", mn)). |
| 132 | +framework_edge(h, m, "pipeline_wrap", d, "registered") :- pl_wraps(h, e), pl_registers(e, gk), pl_hook(gk, m), |
| 133 | + pl_wrap_detail(e, d), h != m. |
0 commit comments