lib/choir/src/composition/loaded/dispatch.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const composition = @import("../root.zig");
 3 const loaded = @import("root.zig");
 4 
 5 const abi = composition.abi;
 6 const model = composition.module;
 7 const source = composition.source;
 8 
 9 pub fn invoke(context_optional: ?*abi.Context, call_site_id: u64, frame_optional: ?*abi.Frame) callconv(.c) i64 {
10     const context = context_optional orelse {
11         if (frame_optional) |frame| frame.setStatusIfCompatible(.incompatible_context);
12         return statusCode(.incompatible_context);
13     };
14     const frame = frame_optional orelse return statusCode(.incompatible_frame);
15     const context_status = context.validate();
16     if (context_status != .ok) {
17         frame.setStatusIfCompatible(context_status);
18         return statusCode(context_status);
19     }
20     const owner: *loaded.LoadedComposition = @ptrFromInt(context.owner_address);
21     if (owner.context.owner_cookie != context.owner_cookie or owner.context.owner_address != context.owner_address) {
22         frame.setStatusIfCompatible(.incompatible_context);
23         return statusCode(.incompatible_context);
24     }
25     const status = dispatch(owner, call_site_id, frame);
26     frame.setStatusIfCompatible(status);
27     return statusCode(status);
28 }
29 
30 pub fn dispatch(owner: *loaded.LoadedComposition, call_site_id: u64, frame: *abi.Frame) abi.Status {
31     if (owner.invocations.isClosing()) return .composition_closing;
32     const frame_status = frame.validate(&owner.context);
33     if (frame_status != .ok) return frame_status;
34     const selected_variant = owner.module.variant(owner.selected_variant) orelse return .unknown_call_site;
35     const call_site = selected_variant.callSite(.{ .value = call_site_id }) orelse return .unknown_call_site;
36     const resources_status = validateResources(owner, call_site, frame);
37     if (resources_status != .ok) return resources_status;
38     const fragment_value = owner.loadedFragment(call_site.callee) orelse return .missing_fragment;
39     const invoke_fn = fragment_value.vtable.invoke orelse return .missing_fragment;
40     return invoke_fn(fragment_value.state, call_site, &owner.context, frame);
41 }
42 
43 fn validateResources(owner: *const loaded.LoadedComposition, call_site: *const model.CallSite, frame: *const abi.Frame) abi.Status {
44     const resolved = frame.resources(&owner.context);
45     const resources = switch (resolved) {
46         .status => |status| return status,
47         .value => |values| values,
48     };
49     const expected_count = std.math.add(usize, call_site.inputs.len, call_site.outputs.len) catch return .invalid_resource;
50     if (resources.len != expected_count or expected_count > abi.max_resources) return .invalid_resource;
51     for (call_site.inputs, 0..) |boundary_id, index| {
52         const boundary = owner.module.source_module.boundary(boundary_id) orelse return .invalid_resource;
53         if (!resourceMatches(resources[index], boundary)) return .invalid_resource;
54     }
55     for (call_site.outputs, 0..) |boundary_id, index| {
56         const boundary = owner.module.source_module.boundary(boundary_id) orelse return .invalid_resource;
57         if (!resourceMatches(resources[call_site.inputs.len + index], boundary)) return .invalid_resource;
58     }
59     return .ok;
60 }
61 
62 fn statusCode(status: abi.Status) i64 {
63     return @intCast(@backingInt(status));
64 }
65 
66 fn resourceMatches(resource: abi.Resource, boundary: *const source.Boundary) bool {
67     const access = resource.accessValue() orelse return false;
68     return resource.byte_size == boundary.byte_size and access == boundary.access;
69 }