GCC Code Coverage Report


Directory: ./
File: Svc/WasmSequencer/WasmSequencerHelpers.cpp
Date: 2026-09-23 21:11:01
Exec Total Coverage
Lines: 233 242 96.3%
Functions: 26 26 100.0%
Branches: 122 140 87.1%

Line Branch Exec Source
1 // ======================================================================
2 // \title WasmSequencerHelpers.cpp
3 // \author tumbar
4 // \brief cpp file for WasmSequencer component implementation class helpers
5 // ======================================================================
6
7 #include <cstddef>
8 #include "Fw/Types/Assert.hpp"
9 #include "Fw/Types/StringBase.hpp"
10 #include "Os/Console.hpp"
11 #include "Svc/WasmSequencer/WasmSequencer.hpp"
12 #include "Svc/WasmSequencer/WasmSequencer_MemoryGrowFailReasonEnumAc.hpp"
13 #include "Svc/WasmSequencer/fprime_spacewasm/include/fprime_spacewasm.h"
14 #include "config/FwAssertArgTypeAliasAc.h"
15 #include "config/FwSizeTypeAliasAc.h"
16 #include "config/WasmSequencerConfig.hpp"
17 #include "spacewasm.h"
18
19 namespace Svc {
20 // ----------------------------------------------------------------------
21 // Interpreter store and page-backed allocators
22 // ----------------------------------------------------------------------
23
24 339 U8* WasmSequencer ::globalAlloc(const U32 size, const U32 align) {
25 // The spacewasm PageAllocator only ever requests fixed-size pages of exactly
26 // SPACEWASM_PAGE_SIZE, aligned no more than the pool's alignment.
27 339 FW_ASSERT(size == Svc::WasmSequencerConfig::SPACEWASM_PAGE_SIZE, static_cast<FwAssertArgType>(size));
28 339 FW_ASSERT(align <= 8, static_cast<FwAssertArgType>(align));
29 339 FW_ASSERT(!this->m_heapPoisoned);
30
31
1/2
✓ Branch 8 taken 339 times.
✗ Branch 9 not taken.
339 if (this->m_heapPagesUsed < this->m_config.heapPages) {
32 339 auto page = this->m_heapPages[this->m_heapPagesUsed];
33 339 FW_ASSERT(page != nullptr);
34 339 this->m_heapPagesUsed += 1;
35 339 return page;
36 } else {
37 // Out of pages.
38 ✗ return nullptr;
39 }
40 }
41
42 339 void WasmSequencer ::globalDealloc(const U8* ptr) {
43
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 339 times.
339 if (ptr == nullptr) {
44 ✗ return;
45 }
46
47 // Make sure the pointer that was given back to us is ours
48 339 bool found = false;
49
1/2
✓ Branch 4 taken 339 times.
✗ Branch 5 not taken.
339 for (FwSizeType i = 0; i < this->m_config.heapPages; i++) {
50
1/2
✓ Branch 6 taken 339 times.
✗ Branch 7 not taken.
339 if (ptr == this->m_heapPages[i]) {
51 339 found = true;
52 339 break;
53 }
54 }
55
56 339 FW_ASSERT(found);
57
58 // Decrement the number of pages used.
59 // Deallocation only happens when the store is being destroyed.
60 // We will assert that the used page count drops to zero once store destruction completes
61 339 FW_ASSERT(this->m_heapPagesUsed > 0);
62 339 this->m_heapPagesUsed -= 1;
63 339 this->m_heapPoisoned = true;
64 }
65
66 202 U8* WasmSequencer ::guestAlloc(FwSizeType size, U32 align) {
67
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 202 times.
202 if (size == 0) {
68 ✗ return nullptr;
69 }
70
71 // Reject any request that cannot possibly fit the guest pool up front.
72
3/4
✓ Branch 4 taken 201 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 201 times.
202 if (size > this->m_config.guestMemorySize || align > SPACEWASM_MEMORY_ALIGNMENT) {
73 1 return nullptr;
74 }
75
76 // Round the current offset up to the requested alignment
77
1/2
✓ Branch 0 taken 201 times.
✗ Branch 1 not taken.
201 const FwSizeType a = (align < 1) ? 1 : static_cast<FwSizeType>(align);
78 201 const FwSizeType start = (this->m_guestPoolOffset + a - 1) & ~(a - 1);
79
80 // Compare against the pre-subtracted bound so `start + size` cannot overflow.
81 // `size <= this->m_config.guestMemorySize` (checked above) makes the subtraction non-negative.
82
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 201 times.
201 if (start > this->m_config.guestMemorySize - size) {
83 ✗ return nullptr;
84 }
85 201 this->m_guestPoolOffset = start + size;
86 201 return &this->m_guestPool[start];
87 }
88
89 8 U8* WasmSequencer ::guestRealloc(U8* ptr, FwSizeType oldSize, FwSizeType newSize, U32 align) {
90 // We have received a memory.grow request.
91 // The following MUST be true for us to actually give the guest it's memory (otherwise fail):
92 // 1. This linear memory is the final allocated memory in the guest bump allocator
93 // 2. We have enough space free to actually do this request
94 // 3. You buy me a donut.
95
96 8 std::ptrdiff_t moduleMemOffset = ptr - this->m_guestPool;
97
98 // The pointer must lie within our guest pool.
99 8 FW_ASSERT(moduleMemOffset >= 0, static_cast<FwAssertArgType>(moduleMemOffset));
100 8 const FwSizeType offset = static_cast<FwSizeType>(moduleMemOffset);
101
102 // Check 1. i.e. the pointer is what we expect given old_size and current guest offset
103
2/2
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 2 times.
8 if (offset + oldSize == this->m_guestPoolOffset) {
104 // Check 2. We have enough space in the guest pool to service this request
105
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
6 if (offset + newSize <= this->m_config.guestMemorySize) {
106 // ...now the donuts
107 // Allocate the new guest memory size
108 3 this->m_guestPoolOffset = offset + newSize;
109
110 // We return the same pointer since this is a strict grow and
111 // we already reserved the slot in front of this memory
112 3 return ptr;
113 }
114
115 // We are the last allocation, but the grown size does not fit the guest pool.
116
2/2
✓ Branch 6 taken 3 times.
✓ Branch 10 taken 3 times.
3 this->log_WARNING_LO_MemoryGrowRejected(WasmSequencer_MemoryGrowFailReason::INSUFFICIENT_POOL_SPACE,
117 static_cast<U64>(newSize));
118 3 return nullptr;
119 }
120
121 // We are not the last allocation in the bump pool, so we cannot grow in place.
122
2/2
✓ Branch 6 taken 2 times.
✓ Branch 10 taken 2 times.
2 this->log_WARNING_LO_MemoryGrowRejected(WasmSequencer_MemoryGrowFailReason::NOT_LAST_ALLOCATION,
123 static_cast<U64>(newSize));
124 2 return nullptr;
125 }
126
127 195 void WasmSequencer ::guestDealloc(const U8* ptr, const FwSizeType size) {
128 // Bump allocator: individual frees are no-ops. The whole guest pool is reset
129 // when a new store is created (destroyStore).
130 (void)ptr;
131 (void)size;
132 195 }
133
134 196 U8* WasmSequencer ::guestAllocCallback(void* userdata, size_t size, size_t align) {
135 196 FW_ASSERT(userdata != nullptr);
136 196 return static_cast<WasmSequencer*>(userdata)->guestAlloc(static_cast<FwSizeType>(size), static_cast<U32>(align));
137 }
138
139 4 U8* WasmSequencer ::guestReallocCallback(void* userdata, U8* ptr, size_t old_size, size_t new_size, size_t align) {
140 4 FW_ASSERT(userdata != nullptr);
141 // Do NOT narrow old_size/new_size to U32: a 4 GiB grow (new_size == 2^32) would alias to 0 and
142 // be accepted as a no-op grow. Pass full width; guestRealloc's fits-pool check rejects it.
143 4 return static_cast<WasmSequencer*>(userdata)->guestRealloc(
144 4 ptr, static_cast<FwSizeType>(old_size), static_cast<FwSizeType>(new_size), static_cast<U32>(align));
145 }
146
147 195 void WasmSequencer ::guestDeallocCallback(void* userdata, U8* ptr, size_t size, size_t align) {
148 195 FW_ASSERT(userdata != nullptr);
149 (void)align;
150 195 static_cast<WasmSequencer*>(userdata)->guestDealloc(ptr, static_cast<FwSizeType>(size));
151 195 }
152
153 339 void WasmSequencer ::createStore() {
154 339 FW_ASSERT(this->m_wasm == nullptr);
155
156
1/1
✓ Branch 4 taken 339 times.
339 this->takeAllocatorLock();
157
158 339 spacewasm_host_t host;
159
1/1
✓ Branch 1 taken 339 times.
339 spacewasm_status_t status = spacewasm_host_new(1, &host);
160 339 FW_ASSERT(status == SPACEWASM_OK, status);
161
162
1/1
✓ Branch 4 taken 339 times.
339 this->hostFprimeV1(&host);
163
164 339 spacewasm_compiler_options_t options;
165 339 options.allow_memory_grow = true; // implemented in a restricted way (see guestRealloc)
166 339 options.max_backpatch_iterations = Svc::WasmSequencerConfig::MAX_BACKPATCH_ITERATIONS;
167 339 options.max_code_pages = this->m_config.maxCodePages;
168
169
1/1
✓ Branch 11 taken 339 times.
339 status = spacewasm_new(&host, this->m_config.stackSize, this->m_config.maxGuestModules, options, &this->m_wasm);
170
171 678 this->m_guest_allocator =
172
1/1
✓ Branch 1 taken 339 times.
339 spacewasm_allocator_new(&WasmSequencer::guestAllocCallback, &WasmSequencer::guestReallocCallback,
173 &WasmSequencer::guestDeallocCallback, /* userdata */ this);
174
175
1/1
✓ Branch 4 taken 339 times.
339 this->releaseAllocatorLock();
176
177 // Make sure the store allocation succeeded.
178 // Failure means the heap memory is too small to host this number of modules + Wasm stack...
179 //
180 // If status == SPACEWASM_ERR_PAGE_TOO_SMALL:
181 // - Increase Svc::WasmSequencerConfig::SPACEWASM_PAGE_SIZE
182 // If SPACEWASM_ERR_OUT_OF_MEMORY / SPACEWASM_ERR_ALLOC_FAILED:
183 // - Increase heapPages in configure()
184 // - Lower maxGuestModules in configure()
185 // - Lower stackSize in configure()
186 339 FW_ASSERT(status == SPACEWASM_OK, status);
187
188 // Make sure the guest allocator creation succeeded
189 339 FW_ASSERT(this->m_guest_allocator != nullptr);
190
191
1/1
✓ Branch 9 taken 339 times.
339 this->log_DIAGNOSTIC_StoreAllocationSucceeded(this->m_config.maxGuestModules);
192 339 }
193
194 339 void WasmSequencer ::destroyStore() {
195 339 FW_ASSERT(this->m_wasm != nullptr);
196 339 FW_ASSERT(this->m_guest_allocator != nullptr);
197
198 339 this->takeAllocatorLock();
199 339 spacewasm_destroy(this->m_wasm);
200 339 spacewasm_allocator_destroy(this->m_guest_allocator);
201 339 this->releaseAllocatorLock();
202 339 this->m_wasm = nullptr;
203 339 this->m_guest_allocator = nullptr;
204
205 // Make sure we cleanly deallocated all the heap memory
206 339 FW_ASSERT(this->m_heapPagesUsed == 0, static_cast<FwAssertArgType>(this->m_heapPagesUsed));
207
208 // Reset the guest linear-memory bump allocator; all guest allocations were
209 // owned by the store that just went away.
210 339 this->m_guestPoolOffset = 0;
211 339 this->m_heapPoisoned = false;
212 339 }
213
214 156 spacewasm_status_t WasmSequencer ::validateModuleMain(WasmSequencer_ModuleIdx moduleIdx) const {
215 156 FW_ASSERT(this->m_wasm != nullptr);
216
217 156 U32 mainIndex;
218
1/1
✓ Branch 5 taken 156 times.
156 auto status = spacewasm_find_export_func(this->m_wasm, static_cast<U32>(moduleIdx), "main", &mainIndex);
219
220
2/2
✓ Branch 0 taken 150 times.
✓ Branch 1 taken 6 times.
156 if (status == SPACEWASM_OK) {
221 // We accept both the [] -> [] and [] -> i32 main signatures. Checking
222 // against "" first yields PARAM_LEN_MISMATCH (not BAD_SIGNATURE) for an
223 // i32-returning main -- BAD_SIGNATURE only flags a malformed signature
224 // *string* -- so fall back on any mismatch, not just BAD_SIGNATURE.
225
1/1
✓ Branch 5 taken 150 times.
150 status = spacewasm_check_func_signature(this->m_wasm, static_cast<U32>(moduleIdx), mainIndex, "", "");
226
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 149 times.
150 if (status != SPACEWASM_OK) {
227
1/1
✓ Branch 5 taken 1 times.
1 status = spacewasm_check_func_signature(this->m_wasm, static_cast<U32>(moduleIdx), mainIndex, "", "i");
228 }
229 }
230
231 156 return status;
232 }
233
234 22 U32 WasmSequencer ::makeCmdUid() const {
235 // cmdUid is formatted XXYY, where XX are the low 16 bits of m_sequencesStarted
236 // and YY are the low 16 bits of m_tlm.commandsDispatched. On the way back in via
237 // cmdResponseIn this lets us check A) that the response is from the current
238 // sequence (modulo 2^16) and B) that it is this exact command instance and not
239 // another dispatch of the same opcode.
240 22 return static_cast<U32>(((this->m_sequencesStarted & 0xFFFF) << 16) | (this->m_tlm.commandsDispatched & 0xFFFF));
241 }
242
243 227 void WasmSequencer ::respondToRequest(const Svc::WasmSequencer_RequestContext& value, const Fw::CmdResponse& response) {
244
2/3
✓ Branch 4 taken 215 times.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
227 switch (value.get_source()) {
245 215 case WasmSequencer_SignalSource::COMMAND_RUN:
246 case WasmSequencer_SignalSource::COMMAND_INVOKE:
247 case WasmSequencer_SignalSource::COMMAND_LOAD:
248 // The request originated from a command; answer it on cmdResponse.
249
2/2
✓ Branch 6 taken 215 times.
✓ Branch 25 taken 215 times.
215 this->cmdResponse_out(value.get_cmdCtx().get_opcode(), value.get_cmdCtx().get_cmdSeq(), response);
250 215 break;
251 12 case WasmSequencer_SignalSource::PORT_RUN:
252 case WasmSequencer_SignalSource::PORT_INVOKE:
253 // Port-driven requests have no command response to send.
254 12 break;
255 ✗ default:
256 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(value.get_source()));
257 ✗ break;
258 }
259 227 }
260
261 222 void WasmSequencer ::respondToWaiting(const Fw::CmdResponse& response) {
262 // Drain every WAIT command blocked on sequence completion, answering each.
263 222 WaitingCmd cmd{};
264
3/3
✓ Branch 4 taken 235 times.
✓ Branch 13 taken 13 times.
✓ Branch 14 taken 222 times.
235 while (this->m_waiting.dequeue(cmd) == Fw::Success::SUCCESS) {
265
2/2
✓ Branch 6 taken 13 times.
✓ Branch 9 taken 13 times.
13 this->cmdResponse_out(cmd.opCode, cmd.cmdSeq, response);
266 }
267 222 }
268
269 196 void WasmSequencer ::reportSeqDone(const Svc::WasmSequencer_RequestContext& value, const Fw::CmdResponse& response) {
270 // seqStart/seqDone are RUN-scoped: seqStartOut is only emitted for RUN sources
271 // (see reportModuleStarted), so only emit the matching seqDoneOut for those.
272 // A non-RUN completion (INVOKE / LOAD) reports neither, keeping the pair balanced.
273
6/6
✓ Branch 4 taken 66 times.
✓ Branch 5 taken 130 times.
✓ Branch 6 taken 54 times.
✓ Branch 7 taken 12 times.
✓ Branch 8 taken 54 times.
✓ Branch 9 taken 142 times.
262 if (value.get_source() != Svc::WasmSequencer_SignalSource::COMMAND_RUN &&
274 66 value.get_source() != Svc::WasmSequencer_SignalSource::PORT_RUN) {
275 54 return;
276 }
277
2/2
✓ Branch 5 taken 140 times.
✓ Branch 6 taken 2 times.
142 if (this->isConnected_seqDoneOut_OutputPort(0)) {
278 140 this->seqDoneOut_out(0, 0, 0, response);
279 }
280 }
281
282 38 void WasmSequencer ::reportSeqAborted(const Svc::WasmSequencer_RequestContext& value, const Fw::CmdResponse& response) {
283 // Respond to port invokers that the sequence exited
284
2/2
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 29 times.
38 if (value.get_source() == Svc::WasmSequencer_SignalSource::PORT_RUN) {
285 9 this->reportSeqDone(value, response);
286 }
287 38 }
288
289 18 spacewasm_status_t WasmSequencer ::setGlobal(const Fw::StringBase& moduleName,
290 const Fw::StringBase& name,
291 spacewasm_value_t value) {
292 18 U32 moduleIdx = 0;
293
2/2
✓ Branch 11 taken 18 times.
✓ Branch 18 taken 18 times.
18 spacewasm_status_t status = spacewasm_find_module(this->m_wasm, moduleName.toChar(), &moduleIdx);
294
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
18 if (status != SPACEWASM_OK) {
295 2 return status;
296 }
297
298 16 U32 globalIdx;
299
2/2
✓ Branch 11 taken 16 times.
✓ Branch 18 taken 16 times.
16 status = spacewasm_find_global(this->m_wasm, moduleIdx, name.toChar(), &globalIdx);
300
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
16 if (status != SPACEWASM_OK) {
301 1 return status;
302 }
303
304
1/1
✓ Branch 5 taken 15 times.
15 return spacewasm_set_global(this->m_wasm, moduleIdx, globalIdx, value);
305 }
306
307 25 spacewasm_status_t WasmSequencer ::getGlobal(const Fw::StringBase& moduleName,
308 const Fw::StringBase& name,
309 spacewasm_value_t& value) {
310 25 U32 moduleIdx = 0;
311
2/2
✓ Branch 11 taken 25 times.
✓ Branch 18 taken 25 times.
25 spacewasm_status_t status = spacewasm_find_module(this->m_wasm, moduleName.toChar(), &moduleIdx);
312
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 22 times.
25 if (status != SPACEWASM_OK) {
313 3 return status;
314 }
315
316 22 U32 globalIdx;
317
2/2
✓ Branch 11 taken 22 times.
✓ Branch 18 taken 22 times.
22 status = spacewasm_find_global(this->m_wasm, moduleIdx, name.toChar(), &globalIdx);
318
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
22 if (status != SPACEWASM_OK) {
319 1 return status;
320 }
321
322
1/1
✓ Branch 5 taken 21 times.
21 return spacewasm_get_global(this->m_wasm, moduleIdx, globalIdx, &value);
323 }
324
325 51 Svc::WasmSequencer_TrapReason::T WasmSequencer ::mapTrapReason(spacewasm_trap_t trap) {
326 // spacewasm_trap_t values 0..14 map 1:1 onto the TrapReason enum ordinals.
327
16/16
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 1 times.
51 switch (trap) {
328 14 case SPACEWASM_TRAP_UNREACHABLE:
329 14 return Svc::WasmSequencer_TrapReason::UNREACHABLE;
330 22 case SPACEWASM_TRAP_HOST:
331 22 return Svc::WasmSequencer_TrapReason::HOST;
332 2 case SPACEWASM_TRAP_DIVIDE_BY_ZERO:
333 2 return Svc::WasmSequencer_TrapReason::DIVIDE_BY_ZERO;
334 1 case SPACEWASM_TRAP_INVALID_TABLE_INDEX:
335 1 return Svc::WasmSequencer_TrapReason::INVALID_TABLE_INDEX;
336 1 case SPACEWASM_TRAP_INVALID_TABLE_FUNCTION_TYPE:
337 1 return Svc::WasmSequencer_TrapReason::INVALID_TABLE_FUNCTION_TYPE;
338 1 case SPACEWASM_TRAP_UNINITIALIZED_TABLE_ELEMENT:
339 1 return Svc::WasmSequencer_TrapReason::UNINITIALIZED_TABLE_ELEMENT;
340 1 case SPACEWASM_TRAP_GLOBAL_GET_FAILED:
341 1 return Svc::WasmSequencer_TrapReason::GLOBAL_GET_FAILED;
342 1 case SPACEWASM_TRAP_GLOBAL_SET_FAILED:
343 1 return Svc::WasmSequencer_TrapReason::GLOBAL_SET_FAILED;
344 1 case SPACEWASM_TRAP_OUT_OF_MEMORY:
345 1 return Svc::WasmSequencer_TrapReason::OUT_OF_MEMORY;
346 1 case SPACEWASM_TRAP_MEMORY_REF_NOT_UNIQUE:
347 1 return Svc::WasmSequencer_TrapReason::MEMORY_REF_NOT_UNIQUE;
348 1 case SPACEWASM_TRAP_MEMORY_OUT_OF_BOUNDS:
349 1 return Svc::WasmSequencer_TrapReason::MEMORY_OUT_OF_BOUNDS;
350 1 case SPACEWASM_TRAP_STACK_OVERFLOW:
351 1 return Svc::WasmSequencer_TrapReason::STACK_OVERFLOW;
352 1 case SPACEWASM_TRAP_UNREPRESENTABLE_RESULT:
353 1 return Svc::WasmSequencer_TrapReason::UNREPRESENTABLE_RESULT;
354 1 case SPACEWASM_TRAP_INTEGER_OVERFLOW:
355 1 return Svc::WasmSequencer_TrapReason::INTEGER_OVERFLOW;
356 1 case SPACEWASM_TRAP_BAD_CONVERSION_TO_INTEGER:
357 1 return Svc::WasmSequencer_TrapReason::BAD_CONVERSION_TO_INTEGER;
358 1 default:
359 1 return Svc::WasmSequencer_TrapReason::HOST;
360 }
361 }
362
363 210 void WasmSequencer ::setSequenceName(const Fw::StringBase& filePath, const Fw::StringBase& moduleName) {
364 // A non-empty module name was supplied to LOAD; use it verbatim.
365
3/3
✓ Branch 11 taken 210 times.
✓ Branch 13 taken 8 times.
✓ Branch 14 taken 202 times.
210 if (moduleName.length() > 0) {
366
1/1
✓ Branch 9 taken 8 times.
8 this->m_tlm.sequenceName = moduleName;
367 8 return;
368 }
369
370 // RUN / LOAD: derive from the file's basename with any ".wasm" suffix stripped.
371
1/1
✓ Branch 11 taken 202 times.
202 const char* const path = filePath.toChar();
372
1/1
✓ Branch 11 taken 202 times.
202 const FwSizeType len = static_cast<FwSizeType>(filePath.length());
373
374 // Find the start of the basename (character after the last '/').
375 202 FwSizeType nameLen = 0;
376
1/1
✓ Branch 1 taken 202 times.
202 const char* const base = WasmSequencer::pathBaseName(path, len, nameLen);
377
378 // Drop a trailing ".wasm" if present.
379 static const char suffix[] = ".wasm";
380 202 const FwSizeType suffixLen = static_cast<FwSizeType>(sizeof(suffix) - 1);
381
1/2
✓ Branch 0 taken 202 times.
✗ Branch 1 not taken.
202 if (nameLen >= suffixLen) {
382 202 bool match = true;
383
2/2
✓ Branch 0 taken 1010 times.
✓ Branch 1 taken 202 times.
1212 for (FwSizeType i = 0; i < suffixLen; i++) {
384
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 1010 times.
1010 if (base[nameLen - suffixLen + i] != suffix[i]) {
385 ✗ match = false;
386 ✗ break;
387 }
388 }
389
1/2
✓ Branch 0 taken 202 times.
✗ Branch 1 not taken.
202 if (match) {
390 202 nameLen -= suffixLen;
391 }
392 }
393
394 202 char name[FileNameStringSize];
395 202 FwSizeType n = 0;
396
3/4
✓ Branch 0 taken 1655 times.
✓ Branch 1 taken 202 times.
✓ Branch 2 taken 1655 times.
✗ Branch 3 not taken.
1857 for (FwSizeType i = 0; i < nameLen && n < static_cast<FwSizeType>(sizeof(name) - 1); i++) {
397 1655 name[n++] = base[i];
398 }
399 202 name[n] = '\0';
400
1/1
✓ Branch 6 taken 202 times.
202 this->m_tlm.sequenceName = name;
401 }
402
403 202 const char* WasmSequencer ::pathBaseName(const char* path, FwSizeType len, FwSizeType& outLen) {
404 202 FW_ASSERT(path != nullptr);
405 // Basename starts just after the last '/', or at the start if there is none.
406 202 FwSizeType start = 0;
407
2/2
✓ Branch 0 taken 2691 times.
✓ Branch 1 taken 202 times.
2893 for (FwSizeType i = 0; i < len; i++) {
408
2/2
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2688 times.
2691 if (path[i] == '/') {
409 3 start = i + 1;
410 }
411 }
412 202 outLen = len - start;
413 202 return path + start;
414 }
415
416 5 bool WasmSequencer ::pathHasParentTraversal(const Fw::StringBase& path) {
417 5 const char* const s = path.toChar();
418 5 const FwSizeType len = static_cast<FwSizeType>(path.length());
419
420 // Walk the '/'-delimited segments; reject if any segment is exactly "..".
421 5 FwSizeType segStart = 0;
422
2/2
✓ Branch 0 taken 267 times.
✓ Branch 1 taken 4 times.
271 for (FwSizeType i = 0; i <= len; i++) {
423
4/4
✓ Branch 0 taken 263 times.
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 262 times.
267 if (i == len || s[i] == '/') {
424 5 const FwSizeType segLen = i - segStart;
425
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
5 if (segLen == 2 && s[segStart] == '.' && s[segStart + 1] == '.') {
426 1 return true;
427 }
428 4 segStart = i + 1;
429 }
430 }
431 4 return false;
432 }
433
434 2656 Os::Mutex* WasmSequencer::getGlobalAllocatorLock() {
435 //! Process-wide lock serializing access to the spacewasm global-allocator registry.
436 //! Initialized by the first component instance's constructor
437
4/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2655 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
2656 static Os::Mutex s_globalAllocatorLock;
438 2656 return &s_globalAllocatorLock;
439 }
440
441 882 void WasmSequencer ::takeAllocatorLock() {
442 882 getGlobalAllocatorLock()->lock();
443
444 882 auto status = spacewasm_fprime_acquire_global_allocator(this);
445 882 FW_ASSERT(status == SPACEWASM_OK, status);
446 882 }
447
448 882 void WasmSequencer ::releaseAllocatorLock() {
449 882 auto status = spacewasm_fprime_release_global_allocator(this);
450 882 FW_ASSERT(status == SPACEWASM_OK, status);
451
452 882 getGlobalAllocatorLock()->unlock();
453 882 }
454
455 //! Panic hook the spacewasm interpreter calls on a fatal internal error.
456 // Must not return.
457 1 extern "C" void spacewasm_panic(const U8* filename,
458 std::size_t filename_len,
459 U32 line,
460 const U8* msg,
461 std::size_t len) {
462
1/1
✓ Branch 2 taken 1 times.
1 Fw::String fmtMsg;
463
1/1
✓ Branch 2 taken 1 times.
1 (void)fmtMsg.format("Rust panic %.*s:%d: %.*s\n", static_cast<int>(filename_len),
464 reinterpret_cast<const char*>(filename), static_cast<int>(line), static_cast<int>(len),
465 reinterpret_cast<const char*>(msg));
466
1/1
✓ Branch 1 taken 1 times.
1 Os::Console::write(fmtMsg);
467
468 // Rust panics map to FSW assertions
469 1 FW_ASSERT(false);
470 2 }
471
472 } // namespace Svc
473