GCC Code Coverage Report


Directory: ./
File: Svc/FpySequencer/FpySequencerDirectives.cpp
Date: 2026-10-14 21:15:02
Exec Total Coverage
Lines: 985 1146 86.0%
Functions: 116 118 98.3%
Branches: 718 907 79.2%

Line Branch Exec Source
1 #include <algorithm>
2 #include <cmath>
3 #include <cstring>
4 #include <type_traits>
5 #include "Fw/Com/ComPacket.hpp"
6 #include "Svc/FpySequencer/FpySequencer.hpp"
7 #include "config/SerialPortIndexEnumAc.hpp"
8
9 namespace Svc {
10
11 2184 void FpySequencer::sendSignal(Signal signal) {
12
4/5
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2172 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
2184 switch (signal) {
13 4 case Signal::stmtResponse_beginSleep: {
14 4 this->sequencer_sendSignal_stmtResponse_beginSleep();
15 4 break;
16 }
17 2172 case Signal::stmtResponse_success: {
18 2172 this->sequencer_sendSignal_stmtResponse_success();
19 2172 break;
20 }
21 3 case Signal::stmtResponse_failure: {
22 3 this->sequencer_sendSignal_stmtResponse_failure();
23 3 break;
24 }
25 5 case Signal::stmtResponse_keepWaiting: {
26 5 this->sequencer_sendSignal_stmtResponse_keepWaiting();
27 5 break;
28 }
29 ✗ default: {
30 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(signal));
31 }
32 }
33 2184 }
34
35 // utility method for updating telemetry based on a directive error code
36 2184 void FpySequencer::handleDirectiveErrorCode(Fpy::DirectiveId id, DirectiveError err) {
37 2184 this->m_tlm.lastDirectiveError = err;
38
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 2181 times.
2184 if (err != DirectiveError::NO_ERROR) {
39 3 this->m_tlm.directiveErrorIndex = this->currentStatementIdx();
40 3 this->m_tlm.directiveErrorId = id;
41 }
42 2184 }
43
44 11 Fw::Success FpySequencer::sendCmd(FwOpcodeType opcode, const U8* argBuf, FwSizeType argBufSize) {
45
1/1
✓ Branch 2 taken 11 times.
11 Fw::ComBuffer cmdBuf;
46 Fw::SerializeStatus stat =
47
1/1
✓ Branch 2 taken 11 times.
11 cmdBuf.serializeFrom(static_cast<FwPacketDescriptorType>(Fw::ComPacketType::FW_PACKET_COMMAND));
48 // TODO should I assert here? this really shouldn't fail, I should just add a static assert
49 // on com buf size and then assert here
50
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (stat != Fw::SerializeStatus::FW_SERIALIZE_OK) {
51 ✗ return Fw::Success::FAILURE;
52 }
53
1/1
✓ Branch 2 taken 11 times.
11 stat = cmdBuf.serializeFrom(opcode);
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (stat != Fw::SerializeStatus::FW_SERIALIZE_OK) {
55 ✗ return Fw::Success::FAILURE;
56 }
57
1/1
✓ Branch 2 taken 11 times.
11 stat = cmdBuf.serializeFrom(argBuf, argBufSize, Fw::Serialization::OMIT_LENGTH);
58
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (stat != Fw::SerializeStatus::FW_SERIALIZE_OK) {
59 ✗ return Fw::Success::FAILURE;
60 }
61
62 // calculate the unique command identifier:
63 // cmd UID is formatted like XXYY, where XX are the first two bytes of the m_sequencesStarted counter
64 // and YY are the first two bytes of the m_statementsDispatched counter.
65 // this way, we know when we get a cmd back A) whether or not it's from this sequence (modulo 2^16) and B)
66 // whether or not it's this specific instance of the cmd in the sequence, and not another one with the same opcode
67 // somewhere else in the file.
68 // if we put this uid in the context we send to the cmdDisp, we will get it back when the cmd returns
69 U32 cmdUid =
70 11 static_cast<U32>(((this->m_sequencesStarted & 0xFFFF) << 16) | (this->m_statementsDispatched & 0xFFFF));
71
72
1/1
✓ Branch 5 taken 11 times.
11 this->cmdOut_out(0, cmdBuf, cmdUid);
73
74
1/1
✓ Branch 1 taken 11 times.
11 return Fw::Success::SUCCESS;
75 11 }
76
77 //! Internal interface handler for directive_waitRel
78 3 void FpySequencer::directive_waitRel_internalInterfaceHandler(const FpySequencer_WaitRelDirective& directive) {
79
1/1
✓ Branch 2 taken 3 times.
3 DirectiveError error = DirectiveError::NO_ERROR;
80
2/2
✓ Branch 7 taken 3 times.
✓ Branch 10 taken 3 times.
3 this->sendSignal(this->waitRel_directiveHandler(directive, error));
81
3/3
✓ Branch 5 taken 3 times.
✓ Branch 9 taken 3 times.
✓ Branch 12 taken 3 times.
3 handleDirectiveErrorCode(Fpy::DirectiveId::WAIT_REL, error);
82 6 }
83
84 //! Internal interface handler for directive_waitAbs
85 1 void FpySequencer::directive_waitAbs_internalInterfaceHandler(const FpySequencer_WaitAbsDirective& directive) {
86
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
87
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->waitAbs_directiveHandler(directive, error));
88
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::WAIT_ABS, error);
89 2 }
90
91 //! Internal interface handler for directive_goto
92 2 void FpySequencer::directive_goto_internalInterfaceHandler(const Svc::FpySequencer_GotoDirective& directive) {
93
1/1
✓ Branch 2 taken 2 times.
2 DirectiveError error = DirectiveError::NO_ERROR;
94
2/2
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
2 this->sendSignal(this->goto_directiveHandler(directive, error));
95
3/3
✓ Branch 5 taken 2 times.
✓ Branch 9 taken 2 times.
✓ Branch 12 taken 2 times.
2 handleDirectiveErrorCode(Fpy::DirectiveId::GOTO, error);
96 4 }
97
98 //! Internal interface handler for directive_if
99 7 void FpySequencer::directive_if_internalInterfaceHandler(const Svc::FpySequencer_IfDirective& directive) {
100
1/1
✓ Branch 2 taken 7 times.
7 DirectiveError error = DirectiveError::NO_ERROR;
101
2/2
✓ Branch 7 taken 7 times.
✓ Branch 10 taken 7 times.
7 this->sendSignal(this->if_directiveHandler(directive, error));
102
3/3
✓ Branch 5 taken 7 times.
✓ Branch 9 taken 7 times.
✓ Branch 12 taken 7 times.
7 handleDirectiveErrorCode(Fpy::DirectiveId::IF, error);
103 14 }
104
105 //! Internal interface handler for directive_noOp
106 2055 void FpySequencer::directive_noOp_internalInterfaceHandler(const Svc::FpySequencer_NoOpDirective& directive) {
107
1/1
✓ Branch 2 taken 2055 times.
2055 DirectiveError error = DirectiveError::NO_ERROR;
108
1/1
✓ Branch 8 taken 2055 times.
2055 this->sendSignal(this->noOp_directiveHandler(directive, error));
109
3/3
✓ Branch 5 taken 2055 times.
✓ Branch 9 taken 2055 times.
✓ Branch 12 taken 2055 times.
2055 handleDirectiveErrorCode(Fpy::DirectiveId::NO_OP, error);
110 4110 }
111
112 //! Internal interface handler for directive_pushTlmVal
113 6 void FpySequencer::directive_pushTlmVal_internalInterfaceHandler(
114 const Svc::FpySequencer_PushTlmValDirective& directive) {
115
1/1
✓ Branch 2 taken 6 times.
6 DirectiveError error = DirectiveError::NO_ERROR;
116
2/2
✓ Branch 7 taken 6 times.
✓ Branch 10 taken 6 times.
6 this->sendSignal(this->pushTlmVal_directiveHandler(directive, error));
117
3/3
✓ Branch 5 taken 6 times.
✓ Branch 9 taken 6 times.
✓ Branch 12 taken 6 times.
6 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TLM_VAL, error);
118 12 }
119
120 //! Internal interface handler for directive_pushTlmValAndTime
121 1 void FpySequencer::directive_pushTlmValAndTime_internalInterfaceHandler(
122 const Svc::FpySequencer_PushTlmValAndTimeDirective& directive) {
123
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
124
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->pushTlmValAndTime_directiveHandler(directive, error));
125
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TLM_VAL_AND_TIME, error);
126 2 }
127
128 //! Internal interface handler for directive_pushPrm
129 1 void FpySequencer::directive_pushPrm_internalInterfaceHandler(const Svc::FpySequencer_PushPrmDirective& directive) {
130
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
131
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->pushPrm_directiveHandler(directive, error));
132
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_PRM, error);
133 2 }
134
135 //! Internal interface handler for directive_constCmd
136 4 void FpySequencer::directive_constCmd_internalInterfaceHandler(const Svc::FpySequencer_ConstCmdDirective& directive) {
137
1/1
✓ Branch 2 taken 4 times.
4 DirectiveError error = DirectiveError::NO_ERROR;
138
2/2
✓ Branch 7 taken 4 times.
✓ Branch 10 taken 4 times.
4 this->sendSignal(this->constCmd_directiveHandler(directive, error));
139
3/3
✓ Branch 5 taken 4 times.
✓ Branch 9 taken 4 times.
✓ Branch 12 taken 4 times.
4 handleDirectiveErrorCode(Fpy::DirectiveId::CONST_CMD, error);
140 8 }
141
142 //! Internal interface handler for directive_stackOp
143 5 void FpySequencer::directive_stackOp_internalInterfaceHandler(const Svc::FpySequencer_StackOpDirective& directive) {
144
1/1
✓ Branch 2 taken 5 times.
5 DirectiveError error = DirectiveError::NO_ERROR;
145
2/2
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
5 this->sendSignal(this->stackOp_directiveHandler(directive, error));
146
3/3
✓ Branch 5 taken 5 times.
✓ Branch 13 taken 5 times.
✓ Branch 16 taken 5 times.
5 handleDirectiveErrorCode(directive.get__op(), error);
147 10 }
148
149 //! Internal interface handler for directive_exit
150 7 void FpySequencer::directive_exit_internalInterfaceHandler(const Svc::FpySequencer_ExitDirective& directive) {
151
1/1
✓ Branch 2 taken 7 times.
7 DirectiveError error = DirectiveError::NO_ERROR;
152
2/2
✓ Branch 7 taken 7 times.
✓ Branch 10 taken 7 times.
7 this->sendSignal(this->exit_directiveHandler(directive, error));
153
3/3
✓ Branch 5 taken 7 times.
✓ Branch 9 taken 7 times.
✓ Branch 12 taken 7 times.
7 handleDirectiveErrorCode(Fpy::DirectiveId::EXIT, error);
154 14 }
155
156 //! Internal interface handler for directive_allocate
157 5 void FpySequencer::directive_allocate_internalInterfaceHandler(const Svc::FpySequencer_AllocateDirective& directive) {
158
1/1
✓ Branch 2 taken 5 times.
5 DirectiveError error = DirectiveError::NO_ERROR;
159
2/2
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
5 this->sendSignal(this->allocate_directiveHandler(directive, error));
160
3/3
✓ Branch 5 taken 5 times.
✓ Branch 9 taken 5 times.
✓ Branch 12 taken 5 times.
5 handleDirectiveErrorCode(Fpy::DirectiveId::ALLOCATE, error);
161 10 }
162
163 //! Internal interface handler for directive_storeRelConstOffset
164 1 void FpySequencer::directive_storeRelConstOffset_internalInterfaceHandler(
165 const Svc::FpySequencer_StoreRelConstOffsetDirective& directive) {
166
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
167
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->storeRelConstOffset_directiveHandler(directive, error));
168
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::STORE_REL_CONST_OFFSET, error);
169 2 }
170
171 //! Internal interface handler for directive_pushVal
172 45 void FpySequencer::directive_pushVal_internalInterfaceHandler(const Svc::FpySequencer_PushValDirective& directive) {
173
1/1
✓ Branch 2 taken 45 times.
45 DirectiveError error = DirectiveError::NO_ERROR;
174
2/2
✓ Branch 7 taken 45 times.
✓ Branch 10 taken 45 times.
45 this->sendSignal(this->pushVal_directiveHandler(directive, error));
175
3/3
✓ Branch 5 taken 45 times.
✓ Branch 9 taken 45 times.
✓ Branch 12 taken 45 times.
45 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_VAL, error);
176 90 }
177
178 //! Internal interface handler for directive_loadRel
179 9 void FpySequencer::directive_loadRel_internalInterfaceHandler(const Svc::FpySequencer_LoadRelDirective& directive) {
180
1/1
✓ Branch 2 taken 9 times.
9 DirectiveError error = DirectiveError::NO_ERROR;
181
2/2
✓ Branch 7 taken 9 times.
✓ Branch 10 taken 9 times.
9 this->sendSignal(this->loadRel_directiveHandler(directive, error));
182
3/3
✓ Branch 5 taken 9 times.
✓ Branch 9 taken 9 times.
✓ Branch 12 taken 9 times.
9 handleDirectiveErrorCode(Fpy::DirectiveId::LOAD_REL, error);
183 18 }
184
185 //! Internal interface handler for directive_discard
186 18 void FpySequencer::directive_discard_internalInterfaceHandler(const Svc::FpySequencer_DiscardDirective& directive) {
187
1/1
✓ Branch 2 taken 18 times.
18 DirectiveError error = DirectiveError::NO_ERROR;
188
2/2
✓ Branch 7 taken 18 times.
✓ Branch 10 taken 18 times.
18 this->sendSignal(this->discard_directiveHandler(directive, error));
189
3/3
✓ Branch 5 taken 18 times.
✓ Branch 9 taken 18 times.
✓ Branch 12 taken 18 times.
18 handleDirectiveErrorCode(Fpy::DirectiveId::DISCARD, error);
190 36 }
191
192 //! Internal interface handler for directive_memCmp
193 1 void FpySequencer::directive_memCmp_internalInterfaceHandler(const Svc::FpySequencer_MemCmpDirective& directive) {
194
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
195
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->memCmp_directiveHandler(directive, error));
196
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::MEMCMP, error);
197 2 }
198
199 //! Internal interface handler for directive_stackCmd
200 1 void FpySequencer::directive_stackCmd_internalInterfaceHandler(const Svc::FpySequencer_StackCmdDirective& directive) {
201
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
202
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->stackCmd_directiveHandler(directive, error));
203
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::STACK_CMD, error);
204 2 }
205
206 //! Internal interface handler for directive_pushTime
207 1 void FpySequencer::directive_pushTime_internalInterfaceHandler(const Svc::FpySequencer_PushTimeDirective& directive) {
208
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
209
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->pushTime_directiveHandler(directive, error));
210
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_TIME, error);
211 2 }
212
213 //! Internal interface handler for directive_setSeed
214 1 void FpySequencer::directive_setSeed_internalInterfaceHandler(const Svc::FpySequencer_SetSeedDirective& directive) {
215
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
216
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->setSeed_directiveHandler(directive, error));
217
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::SET_SEED, error);
218 2 }
219
220 //! Internal interface handler for directive_pushRand
221 2 void FpySequencer::directive_pushRand_internalInterfaceHandler(const Svc::FpySequencer_PushRandDirective& directive) {
222
1/1
✓ Branch 2 taken 2 times.
2 DirectiveError error = DirectiveError::NO_ERROR;
223
2/2
✓ Branch 7 taken 2 times.
✓ Branch 10 taken 2 times.
2 this->sendSignal(this->pushRand_directiveHandler(directive, error));
224
3/3
✓ Branch 5 taken 2 times.
✓ Branch 9 taken 2 times.
✓ Branch 12 taken 2 times.
2 handleDirectiveErrorCode(Fpy::DirectiveId::PUSH_RAND, error);
225 4 }
226
227 //! Internal interface handler for directive_getField
228 1 void FpySequencer::directive_getField_internalInterfaceHandler(const Svc::FpySequencer_GetFieldDirective& directive) {
229
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
230
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->getField_directiveHandler(directive, error));
231
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::GET_FIELD, error);
232 2 }
233
234 //! Internal interface handler for directive_peek
235 1 void FpySequencer::directive_peek_internalInterfaceHandler(const Svc::FpySequencer_PeekDirective& directive) {
236
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
237
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->peek_directiveHandler(directive, error));
238
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::PEEK, error);
239 2 }
240
241 //! Internal interface handler for directive_storeRel
242 1 void FpySequencer::directive_storeRel_internalInterfaceHandler(const Svc::FpySequencer_StoreRelDirective& directive) {
243
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
244
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->storeRel_directiveHandler(directive, error));
245
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::STORE_REL, error);
246 2 }
247
248 //! Internal interface handler for directive_call
249 1 void FpySequencer::directive_call_internalInterfaceHandler(const Svc::FpySequencer_CallDirective& directive) {
250
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
251
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->call_directiveHandler(directive, error));
252
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::CALL, error);
253 2 }
254
255 //! Internal interface handler for directive_return
256 1 void FpySequencer::directive_return_internalInterfaceHandler(const Svc::FpySequencer_ReturnDirective& directive) {
257
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
258
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->return_directiveHandler(directive, error));
259
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::RETURN, error);
260 2 }
261
262 //! Internal interface handler for directive_loadAbs
263 1 void FpySequencer::directive_loadAbs_internalInterfaceHandler(const Svc::FpySequencer_LoadAbsDirective& directive) {
264
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
265
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->loadAbs_directiveHandler(directive, error));
266
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::LOAD_ABS, error);
267 2 }
268
269 //! Internal interface handler for directive_storeAbs
270 1 void FpySequencer::directive_storeAbs_internalInterfaceHandler(const Svc::FpySequencer_StoreAbsDirective& directive) {
271
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
272
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->storeAbs_directiveHandler(directive, error));
273
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::STORE_ABS, error);
274 2 }
275
276 //! Internal interface handler for directive_storeAbsConstOffset
277 1 void FpySequencer::directive_storeAbsConstOffset_internalInterfaceHandler(
278 const Svc::FpySequencer_StoreAbsConstOffsetDirective& directive) {
279
1/1
✓ Branch 2 taken 1 times.
1 DirectiveError error = DirectiveError::NO_ERROR;
280
2/2
✓ Branch 7 taken 1 times.
✓ Branch 10 taken 1 times.
1 this->sendSignal(this->storeAbsConstOffset_directiveHandler(directive, error));
281
3/3
✓ Branch 5 taken 1 times.
✓ Branch 9 taken 1 times.
✓ Branch 12 taken 1 times.
1 handleDirectiveErrorCode(Fpy::DirectiveId::STORE_ABS_CONST_OFFSET, error);
282 2 }
283
284 //! Internal interface handler for directive_popEvent
285 ✗ void FpySequencer::directive_popEvent_internalInterfaceHandler(const Svc::FpySequencer_PopEventDirective& directive) {
286 ✗ DirectiveError error = DirectiveError::NO_ERROR;
287 ✗ this->sendSignal(this->popEvent_directiveHandler(directive, error));
288 ✗ handleDirectiveErrorCode(Fpy::DirectiveId::POP_EVENT, error);
289 ✗ }
290
291 //! Internal interface handler for directive_popSerializable
292 ✗ void FpySequencer::directive_popSerializable_internalInterfaceHandler(
293 const Svc::FpySequencer_PopSerializableDirective& directive) {
294 ✗ DirectiveError error = DirectiveError::NO_ERROR;
295 ✗ this->sendSignal(this->popSerializable_directiveHandler(directive, error));
296 ✗ handleDirectiveErrorCode(Fpy::DirectiveId::POP_SERIALIZABLE, error);
297 ✗ }
298
299 //! Internal interface handler for directive_waitRel
300 5 Signal FpySequencer::waitRel_directiveHandler(const FpySequencer_WaitRelDirective& directive, DirectiveError& error) {
301
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
5 if (this->m_runtime.stack.size < 8) {
302 ✗ error = DirectiveError::STACK_UNDERFLOW;
303 ✗ return Signal::stmtResponse_failure;
304 }
305
306
1/1
✓ Branch 3 taken 5 times.
5 Fw::Time wakeupTime = this->getTime();
307
308
1/1
✓ Branch 4 taken 5 times.
5 U32 uSeconds = this->m_runtime.stack.pop<U32>();
309
1/1
✓ Branch 4 taken 5 times.
5 U32 seconds = this->m_runtime.stack.pop<U32>();
310
311
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (uSeconds >= 1000000) {
312
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::INVALID_ARG;
313 1 return Signal::stmtResponse_failure;
314 }
315
316
1/1
✓ Branch 2 taken 4 times.
4 wakeupTime.add(seconds, uSeconds);
317
1/1
✓ Branch 6 taken 4 times.
4 this->m_runtime.wakeupTime = wakeupTime;
318 4 return Signal::stmtResponse_beginSleep;
319 5 }
320
321 //! Internal interface handler for directive_waitAbs
322 3 Signal FpySequencer::waitAbs_directiveHandler(const FpySequencer_WaitAbsDirective& directive, DirectiveError& error) {
323
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (this->m_runtime.stack.size < 2 * sizeof(U32) + sizeof(FwTimeContextStoreType) + sizeof(FwTimeBaseStoreType)) {
324 ✗ error = DirectiveError::STACK_UNDERFLOW;
325 ✗ return Signal::stmtResponse_failure;
326 }
327
328 3 U32 uSeconds = this->m_runtime.stack.pop<U32>();
329 3 U32 seconds = this->m_runtime.stack.pop<U32>();
330 3 FwTimeContextStoreType ctx = this->m_runtime.stack.pop<FwTimeContextStoreType>();
331 3 FwTimeBaseStoreType base = this->m_runtime.stack.pop<FwTimeBaseStoreType>();
332
333
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (uSeconds >= 1000000) {
334 1 error = DirectiveError::INVALID_ARG;
335 1 return Signal::stmtResponse_failure;
336 }
337
338
3/3
✓ Branch 3 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 15 taken 2 times.
2 this->m_runtime.wakeupTime = Fw::Time(static_cast<TimeBase::T>(base), ctx, seconds, uSeconds);
339 2 return Signal::stmtResponse_beginSleep;
340 }
341
342 //! Internal interface handler for directive_goto
343 5 Signal FpySequencer::goto_directiveHandler(const FpySequencer_GotoDirective& directive, DirectiveError& error) {
344 // check within sequence bounds, or at EOF (we allow == case cuz this just ends the sequence)
345
2/2
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 4 times.
5 if (directive.get_statementIndex() > m_sequenceObj.get_header().get_statementCount()) {
346 1 error = DirectiveError::STMT_OUT_OF_BOUNDS;
347 1 return Signal::stmtResponse_failure;
348 }
349 4 m_runtime.nextStatementIndex = directive.get_statementIndex();
350 4 return Signal::stmtResponse_success;
351 }
352
353 //! Internal interface handler for directive_if
354 12 Signal FpySequencer::if_directiveHandler(const FpySequencer_IfDirective& directive, DirectiveError& error) {
355
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 11 times.
12 if (this->m_runtime.stack.size < 1) {
356 1 error = DirectiveError::STACK_UNDERFLOW;
357 1 return Signal::stmtResponse_failure;
358 }
359 // check within sequence bounds, or at EOF (we allow == case cuz this just ends the sequence)
360
2/2
✓ Branch 14 taken 1 times.
✓ Branch 15 taken 10 times.
11 if (directive.get_falseGotoStmtIndex() > m_sequenceObj.get_header().get_statementCount()) {
361 1 error = DirectiveError::STMT_OUT_OF_BOUNDS;
362 1 return Signal::stmtResponse_failure;
363 }
364
365
2/2
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 6 times.
10 if (this->m_runtime.stack.pop<U8>() != 0) {
366 // proceed to next instruction
367 4 return Signal::stmtResponse_success;
368 }
369
370 // conditional false case
371 6 this->m_runtime.nextStatementIndex = directive.get_falseGotoStmtIndex();
372 6 return Signal::stmtResponse_success;
373 }
374
375 2056 Signal FpySequencer::noOp_directiveHandler(const FpySequencer_NoOpDirective& directive, DirectiveError& error) {
376 2056 return Signal::stmtResponse_success;
377 }
378
379 10 Signal FpySequencer::pushTlmVal_directiveHandler(const FpySequencer_PushTlmValDirective& directive,
380 DirectiveError& error) {
381
2/3
✓ Branch 5 taken 10 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 10 times.
10 if (!this->isConnected_getTlmChan_OutputPort(0)) {
382 ✗ error = DirectiveError::TLM_GET_NOT_CONNECTED;
383 ✗ return Signal::stmtResponse_failure;
384 }
385
1/1
✓ Branch 2 taken 10 times.
10 Fw::Time tlmTime;
386
1/1
✓ Branch 2 taken 10 times.
10 Fw::TlmBuffer tlmValue;
387
1/1
✓ Branch 7 taken 10 times.
10 Fw::TlmValid valid = this->getTlmChan_out(0, directive.get_chanId(), tlmTime, tlmValue);
388
389
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 9 times.
10 if (valid != Fw::TlmValid::VALID) {
390 // could not find this tlm chan
391
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::TLM_CHAN_NOT_FOUND;
392 1 return Signal::stmtResponse_failure;
393 }
394
395
3/3
✓ Branch 2 taken 9 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 8 times.
9 if (tlmValue.getSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
396
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_OVERFLOW;
397 1 return Signal::stmtResponse_failure;
398 }
399
2/2
✓ Branch 5 taken 8 times.
✓ Branch 10 taken 8 times.
8 this->m_runtime.stack.push(tlmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(tlmValue.getSize()));
400 8 return Signal::stmtResponse_success;
401 10 }
402
403 6 Signal FpySequencer::pushTlmValAndTime_directiveHandler(const FpySequencer_PushTlmValAndTimeDirective& directive,
404 DirectiveError& error) {
405
2/3
✓ Branch 5 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
6 if (!this->isConnected_getTlmChan_OutputPort(0)) {
406 ✗ error = DirectiveError::TLM_GET_NOT_CONNECTED;
407 ✗ return Signal::stmtResponse_failure;
408 }
409
410
1/1
✓ Branch 2 taken 6 times.
6 Fw::Time tlmTime;
411
1/1
✓ Branch 2 taken 6 times.
6 Fw::TlmBuffer tlmValue;
412
1/1
✓ Branch 7 taken 6 times.
6 Fw::TlmValid valid = this->getTlmChan_out(0, directive.get_chanId(), tlmTime, tlmValue);
413
414
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5 times.
6 if (valid != Fw::TlmValid::VALID) {
415 // could not find this tlm chan
416
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::TLM_CHAN_NOT_FOUND;
417 1 return Signal::stmtResponse_failure;
418 }
419
420 5 U8 tlmTimeBuf[Fw::Time::SERIALIZED_SIZE] = {};
421
1/1
✓ Branch 2 taken 5 times.
5 Fw::ExternalSerializeBuffer timeEsb(tlmTimeBuf, Fw::Time::SERIALIZED_SIZE);
422
1/1
✓ Branch 2 taken 5 times.
5 Fw::SerializeStatus stat = timeEsb.serializeFrom(tlmTime);
423
424 // coding error if this failed, we should have enough space
425 5 FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
426
427 // check that our stack won't overflow if we put both val and time on it
428
4/4
✓ Branch 2 taken 5 times.
✓ Branch 6 taken 5 times.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 4 times.
5 if (tlmValue.getSize() + timeEsb.getSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
429
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_OVERFLOW;
430 1 return Signal::stmtResponse_failure;
431 }
432
433 // push tlm to end of stack
434
2/2
✓ Branch 5 taken 4 times.
✓ Branch 10 taken 4 times.
4 this->m_runtime.stack.push(tlmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(tlmValue.getSize()));
435 // now push time to end of stack
436
2/2
✓ Branch 5 taken 4 times.
✓ Branch 10 taken 4 times.
4 this->m_runtime.stack.push(timeEsb.getBuffAddr(), static_cast<Fpy::StackSizeType>(timeEsb.getSize()));
437 4 return Signal::stmtResponse_success;
438 6 }
439
440 6 Signal FpySequencer::pushPrm_directiveHandler(const FpySequencer_PushPrmDirective& directive, DirectiveError& error) {
441
3/3
✓ Branch 5 taken 6 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 5 times.
6 if (!this->isConnected_getParam_OutputPort(0)) {
442
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::PRM_GET_NOT_CONNECTED;
443 1 return Signal::stmtResponse_failure;
444 }
445
446
1/1
✓ Branch 2 taken 5 times.
5 Fw::ParamBuffer prmValue;
447
1/1
✓ Branch 7 taken 5 times.
5 Fw::ParamValid valid = this->getParam_out(0, directive.get_prmId(), prmValue);
448
449
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
5 if (valid != Fw::ParamValid::VALID) {
450 // could not find this prm in the DB
451
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::PRM_NOT_FOUND;
452 1 return Signal::stmtResponse_failure;
453 }
454
455
3/3
✓ Branch 2 taken 4 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 3 times.
4 if (prmValue.getSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
456
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_OVERFLOW;
457 1 return Signal::stmtResponse_failure;
458 }
459
460
2/2
✓ Branch 5 taken 3 times.
✓ Branch 10 taken 3 times.
3 this->m_runtime.stack.push(prmValue.getBuffAddr(), static_cast<Fpy::StackSizeType>(prmValue.getSize()));
461 3 return Signal::stmtResponse_success;
462 5 }
463
464 9 Signal FpySequencer::constCmd_directiveHandler(const FpySequencer_ConstCmdDirective& directive, DirectiveError& error) {
465 // the cmd response code will be pushed to the stack when it comes back, so make sure
466 // there is room for it now, before the cmd is dispatched
467
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 8 times.
9 if (sizeof(Fw::CmdResponse::SerialType) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
468 1 error = DirectiveError::STACK_OVERFLOW;
469 1 return Signal::stmtResponse_failure;
470 }
471
2/3
✓ Branch 14 taken 8 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 8 times.
8 if (this->sendCmd(directive.get_opCode(), directive.get_argBuf(), directive.get__argBufSize()) ==
472 Fw::Success::FAILURE) {
473 ✗ return Signal::stmtResponse_failure;
474 } else {
475 // now tell the SM to wait some more until we get the cmd response back
476 // if we've already got the response back this should be harmless
477 8 return Signal::stmtResponse_keepWaiting;
478 }
479 }
480
481 8 DirectiveError FpySequencer::op_or() {
482
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6 times.
8 if (this->m_runtime.stack.size < sizeof(U8) * 2) {
483 2 return DirectiveError::STACK_UNDERFLOW;
484 }
485 6 this->m_runtime.stack.push(static_cast<U8>(this->m_runtime.stack.pop<U8>() | this->m_runtime.stack.pop<U8>()));
486 6 return DirectiveError::NO_ERROR;
487 }
488 6 DirectiveError FpySequencer::op_and() {
489
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(U8) * 2) {
490 2 return DirectiveError::STACK_UNDERFLOW;
491 }
492 4 this->m_runtime.stack.push(static_cast<U8>(this->m_runtime.stack.pop<U8>() & this->m_runtime.stack.pop<U8>()));
493 4 return DirectiveError::NO_ERROR;
494 }
495 6 DirectiveError FpySequencer::op_ieq() {
496
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
6 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
497 3 return DirectiveError::STACK_UNDERFLOW;
498 }
499
2/2
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 1 times.
3 this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<I64>() == this->m_runtime.stack.pop<I64>())
500 ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
501 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
502 3 return DirectiveError::NO_ERROR;
503 }
504 5 DirectiveError FpySequencer::op_ine() {
505
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
506 2 return DirectiveError::STACK_UNDERFLOW;
507 }
508
2/2
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 1 times.
3 this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<I64>() != this->m_runtime.stack.pop<I64>())
509 ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
510 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
511 3 return DirectiveError::NO_ERROR;
512 }
513 6 DirectiveError FpySequencer::op_ult() {
514
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
515 2 return DirectiveError::STACK_UNDERFLOW;
516 }
517 4 U64 rhs = this->m_runtime.stack.pop<U64>();
518 4 U64 lhs = this->m_runtime.stack.pop<U64>();
519
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs < rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
520 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
521 4 return DirectiveError::NO_ERROR;
522 }
523 6 DirectiveError FpySequencer::op_ule() {
524
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
525 2 return DirectiveError::STACK_UNDERFLOW;
526 }
527 4 U64 rhs = this->m_runtime.stack.pop<U64>();
528 4 U64 lhs = this->m_runtime.stack.pop<U64>();
529
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs <= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
530 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
531 4 return DirectiveError::NO_ERROR;
532 }
533 6 DirectiveError FpySequencer::op_ugt() {
534
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
535 2 return DirectiveError::STACK_UNDERFLOW;
536 }
537 4 U64 rhs = this->m_runtime.stack.pop<U64>();
538 4 U64 lhs = this->m_runtime.stack.pop<U64>();
539
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs > rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
540 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
541 4 return DirectiveError::NO_ERROR;
542 }
543 8 DirectiveError FpySequencer::op_uge() {
544
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6 times.
8 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
545 2 return DirectiveError::STACK_UNDERFLOW;
546 }
547 6 U64 rhs = this->m_runtime.stack.pop<U64>();
548 6 U64 lhs = this->m_runtime.stack.pop<U64>();
549
2/2
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 2 times.
6 this->m_runtime.stack.push(static_cast<U8>((lhs >= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
550 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
551 6 return DirectiveError::NO_ERROR;
552 }
553 6 DirectiveError FpySequencer::op_slt() {
554
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
555 2 return DirectiveError::STACK_UNDERFLOW;
556 }
557 4 I64 rhs = this->m_runtime.stack.pop<I64>();
558 4 I64 lhs = this->m_runtime.stack.pop<I64>();
559
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs < rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
560 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
561 4 return DirectiveError::NO_ERROR;
562 }
563 5 DirectiveError FpySequencer::op_sle() {
564
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
565 2 return DirectiveError::STACK_UNDERFLOW;
566 }
567 3 I64 rhs = this->m_runtime.stack.pop<I64>();
568 3 I64 lhs = this->m_runtime.stack.pop<I64>();
569
2/2
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
3 this->m_runtime.stack.push(static_cast<U8>((lhs <= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
570 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
571 3 return DirectiveError::NO_ERROR;
572 }
573 5 DirectiveError FpySequencer::op_sgt() {
574
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
575 2 return DirectiveError::STACK_UNDERFLOW;
576 }
577 3 I64 rhs = this->m_runtime.stack.pop<I64>();
578 3 I64 lhs = this->m_runtime.stack.pop<I64>();
579
2/2
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
3 this->m_runtime.stack.push(static_cast<U8>((lhs > rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
580 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
581 3 return DirectiveError::NO_ERROR;
582 }
583 5 DirectiveError FpySequencer::op_sge() {
584
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
585 2 return DirectiveError::STACK_UNDERFLOW;
586 }
587 3 I64 rhs = this->m_runtime.stack.pop<I64>();
588 3 I64 lhs = this->m_runtime.stack.pop<I64>();
589
2/2
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
3 this->m_runtime.stack.push(static_cast<U8>((lhs >= rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
590 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
591 3 return DirectiveError::NO_ERROR;
592 }
593 6 DirectiveError FpySequencer::op_feq() {
594
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
595 2 return DirectiveError::STACK_UNDERFLOW;
596 }
597 4 F64 rhs = this->m_runtime.stack.pop<F64>();
598 4 F64 lhs = this->m_runtime.stack.pop<F64>();
599 // eq is true if they are equal and neither is nan
600
2/2
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 3 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs == rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
601 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
602 4 return DirectiveError::NO_ERROR;
603 }
604 6 DirectiveError FpySequencer::op_fne() {
605
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
606 2 return DirectiveError::STACK_UNDERFLOW;
607 }
608 4 F64 rhs = this->m_runtime.stack.pop<F64>();
609 4 F64 lhs = this->m_runtime.stack.pop<F64>();
610 // ne is true if they are not equal or either is nan
611
2/2
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
4 this->m_runtime.stack.push(static_cast<U8>((lhs != rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
612 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
613 4 return DirectiveError::NO_ERROR;
614 }
615 7 DirectiveError FpySequencer::op_flt() {
616
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 5 times.
7 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
617 2 return DirectiveError::STACK_UNDERFLOW;
618 }
619 5 F64 rhs = this->m_runtime.stack.pop<F64>();
620 5 F64 lhs = this->m_runtime.stack.pop<F64>();
621
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 this->m_runtime.stack.push(static_cast<U8>(std::isless(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
622 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
623 5 return DirectiveError::NO_ERROR;
624 }
625 6 DirectiveError FpySequencer::op_fle() {
626
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
627 2 return DirectiveError::STACK_UNDERFLOW;
628 }
629 4 F64 rhs = this->m_runtime.stack.pop<F64>();
630 4 F64 lhs = this->m_runtime.stack.pop<F64>();
631
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 this->m_runtime.stack.push(static_cast<U8>(std::islessequal(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
632 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
633 4 return DirectiveError::NO_ERROR;
634 }
635 6 DirectiveError FpySequencer::op_fgt() {
636
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
637 2 return DirectiveError::STACK_UNDERFLOW;
638 }
639 4 F64 rhs = this->m_runtime.stack.pop<F64>();
640 4 F64 lhs = this->m_runtime.stack.pop<F64>();
641
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 this->m_runtime.stack.push(static_cast<U8>(std::isgreater(lhs, rhs) ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
642 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
643 4 return DirectiveError::NO_ERROR;
644 }
645 7 DirectiveError FpySequencer::op_fge() {
646
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 5 times.
7 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
647 2 return DirectiveError::STACK_UNDERFLOW;
648 }
649 5 F64 rhs = this->m_runtime.stack.pop<F64>();
650 5 F64 lhs = this->m_runtime.stack.pop<F64>();
651
2/2
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 2 times.
5 this->m_runtime.stack.push(static_cast<U8>(std::isgreaterequal(lhs, rhs)
652 ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
653 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
654 5 return DirectiveError::NO_ERROR;
655 }
656 3 DirectiveError FpySequencer::op_not() {
657
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U8)) {
658 1 return DirectiveError::STACK_UNDERFLOW;
659 }
660
1/2
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
2 this->m_runtime.stack.push(static_cast<U8>((this->m_runtime.stack.pop<U8>() == 0)
661 ? static_cast<U8>(FW_SERIALIZE_TRUE_VALUE)
662 : static_cast<U8>(FW_SERIALIZE_FALSE_VALUE)));
663 2 return DirectiveError::NO_ERROR;
664 }
665 2 DirectiveError FpySequencer::op_fpext() {
666 // convert F32 to F64
667
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
2 if (this->m_runtime.stack.size < sizeof(F32)) {
668 1 return DirectiveError::STACK_UNDERFLOW;
669 }
670 1 this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<F32>()));
671 1 return DirectiveError::NO_ERROR;
672 }
673 2 DirectiveError FpySequencer::op_fptrunc() {
674 // convert F64 to F32
675
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
2 if (this->m_runtime.stack.size < sizeof(F64)) {
676 1 return DirectiveError::STACK_UNDERFLOW;
677 }
678 1 this->m_runtime.stack.push(static_cast<F32>(this->m_runtime.stack.pop<F64>()));
679 1 return DirectiveError::NO_ERROR;
680 }
681 8 DirectiveError FpySequencer::op_fptosi() {
682
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 7 times.
8 if (this->m_runtime.stack.size < sizeof(F64)) {
683 1 return DirectiveError::STACK_UNDERFLOW;
684 }
685 7 F64 val = this->m_runtime.stack.pop<F64>();
686 // NaN -> 0, out-of-range clamps, in-range truncates toward
687 // zero. The raw static_cast is UB for NaN and out-of-range values.
688 // 2^63 is exactly representable as F64 and is one past I64 max; -2^63
689 // is exactly I64 min and in range.
690 7 const F64 bound = std::ldexp(1.0, 63);
691 I64 result;
692
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
7 if (std::isnan(val)) {
693 1 result = 0;
694
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 } else if (val >= bound) {
695 2 result = std::numeric_limits<I64>::max();
696
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 } else if (val < -bound) {
697 2 result = std::numeric_limits<I64>::min();
698 } else {
699 2 result = static_cast<I64>(val);
700 }
701 7 this->m_runtime.stack.push(result);
702 7 return DirectiveError::NO_ERROR;
703 }
704 2 DirectiveError FpySequencer::op_sitofp() {
705
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
2 if (this->m_runtime.stack.size < sizeof(I64)) {
706 1 return DirectiveError::STACK_UNDERFLOW;
707 }
708 1 this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<I64>()));
709 1 return DirectiveError::NO_ERROR;
710 }
711 7 DirectiveError FpySequencer::op_fptoui() {
712
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
7 if (this->m_runtime.stack.size < sizeof(F64)) {
713 1 return DirectiveError::STACK_UNDERFLOW;
714 }
715 6 F64 val = this->m_runtime.stack.pop<F64>();
716 // NaN -> 0, negatives truncate to at most 0 and clamp there,
717 // 2^64 (one past U64 max) and above clamp to U64 max. The raw
718 // static_cast is UB for NaN and out-of-range values.
719 6 const F64 bound = std::ldexp(1.0, 64);
720 U64 result;
721
6/6
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 3 times.
6 if (std::isnan(val) || val < 0.0) {
722 3 result = 0;
723
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 } else if (val >= bound) {
724 2 result = std::numeric_limits<U64>::max();
725 } else {
726 1 result = static_cast<U64>(val);
727 }
728 6 this->m_runtime.stack.push(result);
729 6 return DirectiveError::NO_ERROR;
730 }
731 2 DirectiveError FpySequencer::op_uitofp() {
732
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
2 if (this->m_runtime.stack.size < sizeof(U64)) {
733 1 return DirectiveError::STACK_UNDERFLOW;
734 }
735 1 this->m_runtime.stack.push(static_cast<F64>(this->m_runtime.stack.pop<U64>()));
736 1 return DirectiveError::NO_ERROR;
737 }
738 5 DirectiveError FpySequencer::op_add() {
739
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
740 2 return DirectiveError::STACK_UNDERFLOW;
741 }
742 3 I64 rhs = this->m_runtime.stack.pop<I64>();
743 3 I64 lhs = this->m_runtime.stack.pop<I64>();
744 // Check for overflow and underflow and return the appropriate error code
745 // Overflow can only occur with both operands positive and occurs when one operand is greater than the maximum value
746 // less the other operand. If either operand is negative or zero, overflow cannot occur.
747
7/8
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 2 times.
3 if ((rhs > 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() - rhs) < lhs)) {
748 1 return DirectiveError::ARITHMETIC_OVERFLOW;
749 }
750 // Underflow can only occur with both operands negative and occurs when one operand is less than the minimum value
751 // minus the other operand. If either operand is positive or zero, underflow cannot occur.
752
6/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
2 else if ((rhs < 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() - rhs) > lhs)) {
753 1 return DirectiveError::ARITHMETIC_UNDERFLOW;
754 }
755 1 this->m_runtime.stack.push(static_cast<I64>(lhs + rhs));
756 1 return DirectiveError::NO_ERROR;
757 }
758 5 DirectiveError FpySequencer::op_sub() {
759
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
5 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
760 2 return DirectiveError::STACK_UNDERFLOW;
761 }
762 3 I64 rhs = this->m_runtime.stack.pop<I64>();
763 3 I64 lhs = this->m_runtime.stack.pop<I64>();
764 // Check for overflow and underflow and return the appropriate error code
765 // Overflow can only occur when the left operand is positive and the right operand is negative. It occurs when the
766 // left (positive) operand is greater than the maximum value plus the other (negative) operand. If the right
767 // operand is positive or zero, overflow cannot occur.
768
6/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 2 times.
3 if ((rhs < 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() + rhs) < lhs)) {
769 1 return DirectiveError::ARITHMETIC_OVERFLOW;
770 }
771 // Underflow can only occur when the left operand is negative and the right operand is positive. It occurs when the
772 // left (negative) operand is less than the minimum value plus the other (positive) operand. If the right operand
773 // is negative or zero, underflow cannot occur.
774
6/8
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
2 else if ((rhs > 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() + rhs) > lhs)) {
775 1 return DirectiveError::ARITHMETIC_UNDERFLOW;
776 }
777 1 this->m_runtime.stack.push(static_cast<I64>(lhs - rhs));
778 1 return DirectiveError::NO_ERROR;
779 }
780 15 DirectiveError FpySequencer::op_mul() {
781
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 13 times.
15 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
782 2 return DirectiveError::STACK_UNDERFLOW;
783 }
784 13 I64 rhs = this->m_runtime.stack.pop<I64>();
785 13 I64 lhs = this->m_runtime.stack.pop<I64>();
786 // Check for overflow and underflow and return the appropriate error code
787 // Overflow can only occur with operands of matching signs and occurs when one operand is greater (or less) than the
788 // maximum value divided by the other operand. Either operand being zero precludes overflow.
789 // Check the both positive case.
790
9/10
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 12 times.
13 if ((rhs > 0) && (lhs > 0) && ((std::numeric_limits<I64>::max() / rhs) < lhs)) {
791 1 return DirectiveError::ARITHMETIC_OVERFLOW;
792 }
793 // Check the both negative case. Compare without negation: negating a value of min is undefined behavior
794
9/10
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 7 times.
✓ Branch 9 taken 5 times.
12 else if ((rhs < 0) && (lhs < 0) && (lhs < (std::numeric_limits<I64>::max() / rhs))) {
795 7 return DirectiveError::ARITHMETIC_OVERFLOW;
796 }
797 // Underflow can occur with operands of differing signs and occurs when one operand is less than the minimum value
798 // divided by the other operand. Either operand being zero precludes underflow.
799 // Check the case where lhs is positive.
800
8/10
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 4 times.
5 else if ((rhs < 0) && (lhs > 0) && ((std::numeric_limits<I64>::min() / lhs) > rhs)) {
801 1 return DirectiveError::ARITHMETIC_UNDERFLOW;
802 }
803 // Check the case where rhs is positive.
804
8/10
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 3 times.
4 else if ((rhs > 0) && (lhs < 0) && ((std::numeric_limits<I64>::min() / rhs) > lhs)) {
805 1 return DirectiveError::ARITHMETIC_UNDERFLOW;
806 }
807 3 this->m_runtime.stack.push(static_cast<I64>(lhs * rhs));
808 3 return DirectiveError::NO_ERROR;
809 }
810 4 DirectiveError FpySequencer::op_udiv() {
811
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
812 2 return DirectiveError::STACK_UNDERFLOW;
813 }
814 2 U64 rhs = this->m_runtime.stack.pop<U64>();
815 2 U64 lhs = this->m_runtime.stack.pop<U64>();
816 // Prevent division by zero
817
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (rhs == 0) {
818 1 return DirectiveError::DOMAIN_ERROR;
819 }
820
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 this->m_runtime.stack.push(static_cast<U64>(lhs / rhs));
821 1 return DirectiveError::NO_ERROR;
822 }
823 9 DirectiveError FpySequencer::op_sdiv() {
824
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 7 times.
9 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
825 2 return DirectiveError::STACK_UNDERFLOW;
826 }
827
828 7 I64 rhs = this->m_runtime.stack.pop<I64>();
829 7 I64 lhs = this->m_runtime.stack.pop<I64>();
830 // Prevent division by zero
831
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (rhs == 0) {
832 1 return DirectiveError::DOMAIN_ERROR;
833 }
834 // The one signed division that can overflow: |I64 min / -1| = 2^63 is not
835 // representable (and the C++ expression is UB, SIGFPE on x86)
836
5/6
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 5 times.
6 if ((lhs == std::numeric_limits<I64>::min()) && (rhs == -1)) {
837 1 return DirectiveError::ARITHMETIC_OVERFLOW;
838 }
839 // C++ / truncates toward zero; adjust to match Python's floored division:
840 // an inexact quotient with differing operand signs floors one below the
841 // truncated result. This mirrors op_smod.
842
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 I64 quotient = lhs / rhs;
843
7/8
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 3 times.
5 if (((lhs % rhs) != 0) && ((lhs < 0) != (rhs < 0))) {
844 2 quotient -= 1;
845 }
846 5 this->m_runtime.stack.push(quotient);
847 5 return DirectiveError::NO_ERROR;
848 }
849 4 DirectiveError FpySequencer::op_umod() {
850
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 if (this->m_runtime.stack.size < sizeof(U64) * 2) {
851 2 return DirectiveError::STACK_UNDERFLOW;
852 }
853 2 U64 rhs = this->m_runtime.stack.pop<U64>();
854
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (rhs == 0) {
855 1 return DirectiveError::DOMAIN_ERROR;
856 }
857 1 U64 lhs = this->m_runtime.stack.pop<U64>();
858
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 this->m_runtime.stack.push(static_cast<U64>(lhs % rhs));
859 1 return DirectiveError::NO_ERROR;
860 }
861 7 DirectiveError FpySequencer::op_smod() {
862
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 5 times.
7 if (this->m_runtime.stack.size < sizeof(I64) * 2) {
863 2 return DirectiveError::STACK_UNDERFLOW;
864 }
865 5 I64 rhs = this->m_runtime.stack.pop<I64>();
866
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (rhs == 0) {
867 1 return DirectiveError::DOMAIN_ERROR;
868 }
869 4 I64 lhs = this->m_runtime.stack.pop<I64>();
870 // I64 min % -1 is 0, the mathematical remainder (matching wasm i64.rem_s),
871 // but the C++ expression is UB (SIGFPE on x86) so it must be special-cased
872
5/6
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 3 times.
4 if ((lhs == std::numeric_limits<I64>::min()) && (rhs == -1)) {
873 1 this->m_runtime.stack.push(static_cast<I64>(0));
874 1 return DirectiveError::NO_ERROR;
875 }
876
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 I64 res = static_cast<I64>(lhs % rhs);
877 // in order to match Python's behavior,
878 // if the signs of the remainder and divisor differ, adjust the result.
879 // this happens when the result should be positive but is negative, or vice-versa.
880 // credit Gemini 2.5 pro
881
6/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
3 if ((res > 0 && rhs < 0) || (res < 0 && rhs > 0)) {
882 2 res += rhs;
883 }
884 3 this->m_runtime.stack.push(res);
885 3 return DirectiveError::NO_ERROR;
886 }
887 3 DirectiveError FpySequencer::op_fadd() {
888
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
889 2 return DirectiveError::STACK_UNDERFLOW;
890 }
891 1 F64 rhs = this->m_runtime.stack.pop<F64>();
892 1 F64 lhs = this->m_runtime.stack.pop<F64>();
893 1 this->m_runtime.stack.push(static_cast<F64>(lhs + rhs));
894 1 return DirectiveError::NO_ERROR;
895 }
896 3 DirectiveError FpySequencer::op_fsub() {
897
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
898 2 return DirectiveError::STACK_UNDERFLOW;
899 }
900 1 F64 rhs = this->m_runtime.stack.pop<F64>();
901 1 F64 lhs = this->m_runtime.stack.pop<F64>();
902 1 this->m_runtime.stack.push(static_cast<F64>(lhs - rhs));
903 1 return DirectiveError::NO_ERROR;
904 }
905 3 DirectiveError FpySequencer::op_fmul() {
906
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
907 2 return DirectiveError::STACK_UNDERFLOW;
908 }
909 1 F64 rhs = this->m_runtime.stack.pop<F64>();
910 1 F64 lhs = this->m_runtime.stack.pop<F64>();
911 1 this->m_runtime.stack.push(static_cast<F64>(lhs * rhs));
912 1 return DirectiveError::NO_ERROR;
913 }
914 3 DirectiveError FpySequencer::op_fdiv() {
915
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
916 2 return DirectiveError::STACK_UNDERFLOW;
917 }
918 1 F64 rhs = this->m_runtime.stack.pop<F64>();
919 1 F64 lhs = this->m_runtime.stack.pop<F64>();
920 1 this->m_runtime.stack.push(static_cast<F64>(lhs / rhs));
921 1 return DirectiveError::NO_ERROR;
922 }
923 3 DirectiveError FpySequencer::op_fpow() {
924
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
925 2 return DirectiveError::STACK_UNDERFLOW;
926 }
927 1 F64 rhs = this->m_runtime.stack.pop<F64>();
928 1 F64 lhs = this->m_runtime.stack.pop<F64>();
929 1 this->m_runtime.stack.push(static_cast<F64>(pow(lhs, rhs)));
930 1 return DirectiveError::NO_ERROR;
931 }
932 4 DirectiveError FpySequencer::op_flog() {
933
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (this->m_runtime.stack.size < sizeof(F64)) {
934 1 return DirectiveError::STACK_UNDERFLOW;
935 }
936 3 F64 val = this->m_runtime.stack.pop<F64>();
937
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (val <= 0.0) {
938 2 return DirectiveError::DOMAIN_ERROR;
939 }
940 1 this->m_runtime.stack.push(static_cast<F64>(log(val)));
941 1 return DirectiveError::NO_ERROR;
942 }
943 8 DirectiveError FpySequencer::op_fmod() {
944
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 6 times.
8 if (this->m_runtime.stack.size < sizeof(F64) * 2) {
945 2 return DirectiveError::STACK_UNDERFLOW;
946 }
947 6 F64 rhs = this->m_runtime.stack.pop<F64>();
948 6 F64 lhs = this->m_runtime.stack.pop<F64>();
949 // std::fmod computes the exact truncated remainder (sign of lhs) with no
950 // intermediate rounding. A zero divisor yields NaN, matching Rust and C#.
951 6 F64 res = std::fmod(lhs, rhs);
952 // Adjust to match Python's floored-modulo semantics: if the signs of the
953 // remainder and divisor differ, add the divisor once. This mirrors op_smod
954 // and is the exact frem + fadd the VM model computes (at most one rounded add).
955
7/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
6 if ((res > 0 && rhs < 0) || (res < 0 && rhs > 0)) {
956 2 res += rhs;
957
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 } else if (res == 0) {
958 // Python normalizes an exact-multiple result so the zero carries the
959 // divisor's sign (CPython float_rem); fmod leaves the dividend's.
960 2 res = std::copysign(0.0, rhs);
961 }
962 6 this->m_runtime.stack.push(res);
963 6 return DirectiveError::NO_ERROR;
964 }
965 4 DirectiveError FpySequencer::op_siext_8_64() {
966
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (this->m_runtime.stack.size < sizeof(I8)) {
967 1 return DirectiveError::STACK_UNDERFLOW;
968 }
969 3 I8 src = this->m_runtime.stack.pop<I8>();
970 3 this->m_runtime.stack.push(static_cast<I64>(src));
971 3 return DirectiveError::NO_ERROR;
972 }
973 4 DirectiveError FpySequencer::op_siext_16_64() {
974
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (this->m_runtime.stack.size < sizeof(I16)) {
975 1 return DirectiveError::STACK_UNDERFLOW;
976 }
977 3 I16 src = this->m_runtime.stack.pop<I16>();
978 3 this->m_runtime.stack.push(static_cast<I64>(src));
979 3 return DirectiveError::NO_ERROR;
980 }
981 4 DirectiveError FpySequencer::op_siext_32_64() {
982
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (this->m_runtime.stack.size < sizeof(I32)) {
983 1 return DirectiveError::STACK_UNDERFLOW;
984 }
985 3 I32 src = this->m_runtime.stack.pop<I32>();
986 3 this->m_runtime.stack.push(static_cast<I64>(src));
987 3 return DirectiveError::NO_ERROR;
988 }
989 4 DirectiveError FpySequencer::op_ziext_8_64() {
990
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (this->m_runtime.stack.size < sizeof(U8)) {
991 1 return DirectiveError::STACK_UNDERFLOW;
992 }
993 3 U8 src = this->m_runtime.stack.pop<U8>();
994 3 this->m_runtime.stack.push(static_cast<U64>(src));
995 3 return DirectiveError::NO_ERROR;
996 }
997 3 DirectiveError FpySequencer::op_ziext_16_64() {
998
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U16)) {
999 1 return DirectiveError::STACK_UNDERFLOW;
1000 }
1001 2 U16 src = this->m_runtime.stack.pop<U16>();
1002 2 this->m_runtime.stack.push(static_cast<U64>(src));
1003 2 return DirectiveError::NO_ERROR;
1004 }
1005 3 DirectiveError FpySequencer::op_ziext_32_64() {
1006
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U32)) {
1007 1 return DirectiveError::STACK_UNDERFLOW;
1008 }
1009 2 U32 src = this->m_runtime.stack.pop<U32>();
1010 2 this->m_runtime.stack.push(static_cast<U64>(src));
1011 2 return DirectiveError::NO_ERROR;
1012 }
1013 3 DirectiveError FpySequencer::op_itrunc_64_8() {
1014
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U64)) {
1015 1 return DirectiveError::STACK_UNDERFLOW;
1016 }
1017 2 U64 src = this->m_runtime.stack.pop<U64>();
1018 2 this->m_runtime.stack.push(static_cast<U8>(src));
1019 2 return DirectiveError::NO_ERROR;
1020 }
1021 3 DirectiveError FpySequencer::op_itrunc_64_16() {
1022
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U64)) {
1023 1 return DirectiveError::STACK_UNDERFLOW;
1024 }
1025 2 U64 src = this->m_runtime.stack.pop<U64>();
1026 2 this->m_runtime.stack.push(static_cast<U16>(src));
1027 2 return DirectiveError::NO_ERROR;
1028 }
1029 3 DirectiveError FpySequencer::op_itrunc_64_32() {
1030
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U64)) {
1031 1 return DirectiveError::STACK_UNDERFLOW;
1032 }
1033 2 U64 src = this->m_runtime.stack.pop<U64>();
1034 2 this->m_runtime.stack.push(static_cast<U32>(src));
1035 2 return DirectiveError::NO_ERROR;
1036 }
1037 10 DirectiveError FpySequencer::op_ffloor() {
1038
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 8 times.
10 if (this->m_runtime.stack.size < sizeof(F64)) {
1039 2 return DirectiveError::STACK_UNDERFLOW;
1040 }
1041 8 F64 val = this->m_runtime.stack.pop<F64>();
1042 // std::floor implements IEEE 754 roundToIntegralTowardNegative: +-0, +-inf
1043 // and NaN pass through, and the sign of a zero is preserved.
1044 8 this->m_runtime.stack.push(std::floor(val));
1045 8 return DirectiveError::NO_ERROR;
1046 }
1047 6 DirectiveError FpySequencer::op_iabs() {
1048
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (this->m_runtime.stack.size < sizeof(I64)) {
1049 2 return DirectiveError::STACK_UNDERFLOW;
1050 }
1051 4 I64 val = this->m_runtime.stack.pop<I64>();
1052 // abs(I64 min) is not representable in I64 (and -val on it is UB)
1053
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
4 if (val == std::numeric_limits<I64>::min()) {
1054 1 return DirectiveError::ARITHMETIC_OVERFLOW;
1055 }
1056 3 this->m_runtime.stack.push(val < 0 ? -val : val);
1057 3 return DirectiveError::NO_ERROR;
1058 }
1059 7 DirectiveError FpySequencer::op_fabs() {
1060
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 5 times.
7 if (this->m_runtime.stack.size < sizeof(F64)) {
1061 2 return DirectiveError::STACK_UNDERFLOW;
1062 }
1063 5 F64 val = this->m_runtime.stack.pop<F64>();
1064 // IEEE 754 abs: clears the sign bit and changes nothing else, so NaN
1065 // payloads pass through.
1066 5 this->m_runtime.stack.push(std::fabs(val));
1067 5 return DirectiveError::NO_ERROR;
1068 }
1069 15 Signal FpySequencer::stackOp_directiveHandler(const FpySequencer_StackOpDirective& directive, DirectiveError& error) {
1070 // coding error, should not have gotten to this stack op handler
1071 15 FW_ASSERT((directive.get__op() >= Fpy::DirectiveId::OR && directive.get__op() <= Fpy::DirectiveId::ITRUNC_64_32) ||
1072 (directive.get__op() >= Fpy::DirectiveId::FFLOOR && directive.get__op() <= Fpy::DirectiveId::FABS),
1073 static_cast<FwAssertArgType>(directive.get__op()));
1074
1075
11/52
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 2 times.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 1 times.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
15 switch (directive.get__op()) {
1076 3 case Fpy::DirectiveId::OR:
1077
2/2
✓ Branch 2 taken 3 times.
✓ Branch 9 taken 3 times.
3 error = this->op_or();
1078 3 break;
1079 1 case Fpy::DirectiveId::AND:
1080
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_and();
1081 1 break;
1082 2 case Fpy::DirectiveId::IEQ:
1083
2/2
✓ Branch 2 taken 2 times.
✓ Branch 9 taken 2 times.
2 error = this->op_ieq();
1084 2 break;
1085 1 case Fpy::DirectiveId::INE:
1086
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_ine();
1087 1 break;
1088 1 case Fpy::DirectiveId::ULT:
1089
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_ult();
1090 1 break;
1091 ✗ case Fpy::DirectiveId::ULE:
1092 ✗ error = this->op_ule();
1093 ✗ break;
1094 1 case Fpy::DirectiveId::UGT:
1095
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_ugt();
1096 1 break;
1097 2 case Fpy::DirectiveId::UGE:
1098
2/2
✓ Branch 2 taken 2 times.
✓ Branch 9 taken 2 times.
2 error = this->op_uge();
1099 2 break;
1100 1 case Fpy::DirectiveId::SLT:
1101
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_slt();
1102 1 break;
1103 ✗ case Fpy::DirectiveId::SLE:
1104 ✗ error = this->op_sle();
1105 ✗ break;
1106 ✗ case Fpy::DirectiveId::SGT:
1107 ✗ error = this->op_sgt();
1108 ✗ break;
1109 ✗ case Fpy::DirectiveId::SGE:
1110 ✗ error = this->op_sge();
1111 ✗ break;
1112 ✗ case Fpy::DirectiveId::FEQ:
1113 ✗ error = this->op_feq();
1114 ✗ break;
1115 ✗ case Fpy::DirectiveId::FNE:
1116 ✗ error = this->op_fne();
1117 ✗ break;
1118 1 case Fpy::DirectiveId::FLT:
1119
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_flt();
1120 1 break;
1121 ✗ case Fpy::DirectiveId::FLE:
1122 ✗ error = this->op_fle();
1123 ✗ break;
1124 ✗ case Fpy::DirectiveId::FGT:
1125 ✗ error = this->op_fgt();
1126 ✗ break;
1127 1 case Fpy::DirectiveId::FGE:
1128
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_fge();
1129 1 break;
1130 1 case Fpy::DirectiveId::NOT:
1131
2/2
✓ Branch 2 taken 1 times.
✓ Branch 9 taken 1 times.
1 error = this->op_not();
1132 1 break;
1133 ✗ case Fpy::DirectiveId::FPEXT:
1134 ✗ error = this->op_fpext();
1135 ✗ break;
1136 ✗ case Fpy::DirectiveId::FPTRUNC:
1137 ✗ error = this->op_fptrunc();
1138 ✗ break;
1139 ✗ case Fpy::DirectiveId::FPTOSI:
1140 ✗ error = this->op_fptosi();
1141 ✗ break;
1142 ✗ case Fpy::DirectiveId::FPTOUI:
1143 ✗ error = this->op_fptoui();
1144 ✗ break;
1145 ✗ case Fpy::DirectiveId::SITOFP:
1146 ✗ error = this->op_sitofp();
1147 ✗ break;
1148 ✗ case Fpy::DirectiveId::UITOFP:
1149 ✗ error = this->op_uitofp();
1150 ✗ break;
1151 ✗ case Fpy::DirectiveId::ADD:
1152 ✗ error = this->op_add();
1153 ✗ break;
1154 ✗ case Fpy::DirectiveId::SUB:
1155 ✗ error = this->op_sub();
1156 ✗ break;
1157 ✗ case Fpy::DirectiveId::MUL:
1158 ✗ error = this->op_mul();
1159 ✗ break;
1160 ✗ case Fpy::DirectiveId::UDIV:
1161 ✗ error = this->op_udiv();
1162 ✗ break;
1163 ✗ case Fpy::DirectiveId::SDIV:
1164 ✗ error = this->op_sdiv();
1165 ✗ break;
1166 ✗ case Fpy::DirectiveId::UMOD:
1167 ✗ error = this->op_umod();
1168 ✗ break;
1169 ✗ case Fpy::DirectiveId::SMOD:
1170 ✗ error = this->op_smod();
1171 ✗ break;
1172 ✗ case Fpy::DirectiveId::FADD:
1173 ✗ error = this->op_fadd();
1174 ✗ break;
1175 ✗ case Fpy::DirectiveId::FSUB:
1176 ✗ error = this->op_fsub();
1177 ✗ break;
1178 ✗ case Fpy::DirectiveId::FMUL:
1179 ✗ error = this->op_fmul();
1180 ✗ break;
1181 ✗ case Fpy::DirectiveId::FDIV:
1182 ✗ error = this->op_fdiv();
1183 ✗ break;
1184 ✗ case Fpy::DirectiveId::FPOW:
1185 ✗ error = this->op_fpow();
1186 ✗ break;
1187 ✗ case Fpy::DirectiveId::FLOG:
1188 ✗ error = this->op_flog();
1189 ✗ break;
1190 ✗ case Fpy::DirectiveId::FMOD:
1191 ✗ error = this->op_fmod();
1192 ✗ break;
1193 ✗ case Fpy::DirectiveId::SIEXT_8_64:
1194 ✗ error = this->op_siext_8_64();
1195 ✗ break;
1196 ✗ case Fpy::DirectiveId::SIEXT_16_64:
1197 ✗ error = this->op_siext_16_64();
1198 ✗ break;
1199 ✗ case Fpy::DirectiveId::SIEXT_32_64:
1200 ✗ error = this->op_siext_32_64();
1201 ✗ break;
1202 ✗ case Fpy::DirectiveId::ZIEXT_8_64:
1203 ✗ error = this->op_ziext_8_64();
1204 ✗ break;
1205 ✗ case Fpy::DirectiveId::ZIEXT_16_64:
1206 ✗ error = this->op_ziext_16_64();
1207 ✗ break;
1208 ✗ case Fpy::DirectiveId::ZIEXT_32_64:
1209 ✗ error = this->op_ziext_32_64();
1210 ✗ break;
1211 ✗ case Fpy::DirectiveId::ITRUNC_64_8:
1212 ✗ error = this->op_itrunc_64_8();
1213 ✗ break;
1214 ✗ case Fpy::DirectiveId::ITRUNC_64_16:
1215 ✗ error = this->op_itrunc_64_16();
1216 ✗ break;
1217 ✗ case Fpy::DirectiveId::ITRUNC_64_32:
1218 ✗ error = this->op_itrunc_64_32();
1219 ✗ break;
1220 ✗ case Fpy::DirectiveId::FFLOOR:
1221 ✗ error = this->op_ffloor();
1222 ✗ break;
1223 ✗ case Fpy::DirectiveId::IABS:
1224 ✗ error = this->op_iabs();
1225 ✗ break;
1226 ✗ case Fpy::DirectiveId::FABS:
1227 ✗ error = this->op_fabs();
1228 ✗ break;
1229 ✗ default:
1230 ✗ FW_ASSERT(false, directive.get__op());
1231 ✗ break;
1232 }
1233
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 14 times.
15 if (error != DirectiveError::NO_ERROR) {
1234 1 return Signal::stmtResponse_failure;
1235 }
1236 14 return Signal::stmtResponse_success;
1237 }
1238
1239 10 Signal FpySequencer::exit_directiveHandler(const FpySequencer_ExitDirective& directive, DirectiveError& error) {
1240
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
10 if (this->m_runtime.stack.size < sizeof(I32)) {
1241 ✗ error = DirectiveError::STACK_UNDERFLOW;
1242 ✗ return Signal::stmtResponse_failure;
1243 }
1244 10 I32 errorCode = this->m_runtime.stack.pop<I32>();
1245 // exit(0), no error
1246
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 if (errorCode == 0) {
1247 // just goto the end of the sequence
1248 5 this->m_runtime.nextStatementIndex = this->m_sequenceObj.get_header().get_statementCount();
1249 5 return Signal::stmtResponse_success;
1250 }
1251 // otherwise, kill the sequence here
1252 // raise the user defined error code as an event
1253 5 this->log_WARNING_HI_SequenceExitedWithError(this->m_sequenceFilePath, errorCode);
1254 5 error = DirectiveError::EXIT_WITH_ERROR;
1255 5 return Signal::stmtResponse_failure;
1256 }
1257
1258 8 Signal FpySequencer::allocate_directiveHandler(const FpySequencer_AllocateDirective& directive, DirectiveError& error) {
1259
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 7 times.
8 if (directive.get_size() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1260 1 error = DirectiveError::STACK_OVERFLOW;
1261 1 return Signal::stmtResponse_failure;
1262 }
1263 7 this->m_runtime.stack.pushZeroes(directive.get_size());
1264 7 return Signal::stmtResponse_success;
1265 }
1266
1267 //! Helper to pop value from stack top and store at destOffset
1268 17 Signal FpySequencer::storeHelper(Fpy::StackSizeType destOffset, Fpy::StackSizeType size, DirectiveError& error) {
1269
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 15 times.
17 if (this->m_runtime.stack.size < size) {
1270 2 error = DirectiveError::STACK_UNDERFLOW;
1271 2 return Signal::stmtResponse_failure;
1272 }
1273 // After popping the value, would the write go out of bounds?
1274 15 Fpy::StackSizeType newStackSize = this->m_runtime.stack.size - size;
1275 // Overflow-safe check: destOffset + size > newStackSize
1276 // Rewritten as: check destOffset <= newStackSize first, then size > newStackSize - destOffset
1277
3/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
15 if (destOffset > newStackSize || size > newStackSize - destOffset) {
1278 4 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1279 4 return Signal::stmtResponse_failure;
1280 }
1281 // Copy value to the destination location
1282 11 this->m_runtime.stack.copy(destOffset, this->m_runtime.stack.size - size, size);
1283 11 this->m_runtime.stack.size = newStackSize;
1284 11 return Signal::stmtResponse_success;
1285 }
1286
1287 //! Helper to load value from srcOffset and push to stack top
1288 19 Signal FpySequencer::loadHelper(Fpy::StackSizeType srcOffset, Fpy::StackSizeType size, DirectiveError& error) {
1289
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 17 times.
19 if (size > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1290 2 error = DirectiveError::STACK_OVERFLOW;
1291 2 return Signal::stmtResponse_failure;
1292 }
1293 // Overflow-safe check: srcOffset + size > stack.size
1294 // Rewritten as: check srcOffset <= stack.size first, then size > stack.size - srcOffset
1295
4/4
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 2 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 14 times.
17 if (srcOffset > this->m_runtime.stack.size || size > this->m_runtime.stack.size - srcOffset) {
1296 3 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1297 3 return Signal::stmtResponse_failure;
1298 }
1299 // Copy from source location to top of stack
1300 14 this->m_runtime.stack.copy(this->m_runtime.stack.size, srcOffset, size);
1301 14 this->m_runtime.stack.size += size;
1302 14 return Signal::stmtResponse_success;
1303 }
1304
1305 7 Signal FpySequencer::storeRelConstOffset_directiveHandler(const FpySequencer_StoreRelConstOffsetDirective& directive,
1306 DirectiveError& error) {
1307 7 I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + directive.get_lvarOffset();
1308
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 5 times.
7 if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1309 2 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1310 2 return Signal::stmtResponse_failure;
1311 }
1312 5 return this->storeHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1313 }
1314
1315 16 Signal FpySequencer::loadRel_directiveHandler(const FpySequencer_LoadRelDirective& directive, DirectiveError& error) {
1316 16 I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + directive.get_lvarOffset();
1317
4/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 14 times.
16 if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1318 2 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1319 2 return Signal::stmtResponse_failure;
1320 }
1321 14 return this->loadHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1322 }
1323
1324 48 Signal FpySequencer::pushVal_directiveHandler(const FpySequencer_PushValDirective& directive, DirectiveError& error) {
1325
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 47 times.
48 if (directive.get__valSize() > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1326 1 error = DirectiveError::STACK_OVERFLOW;
1327 1 return Signal::stmtResponse_failure;
1328 }
1329 // copy from the bytearray in the directive to the stack, add to stack size.
1330 94 this->m_runtime.stack.push(const_cast<U8*>(directive.get_val()),
1331 47 static_cast<Fpy::StackSizeType>(directive.get__valSize()));
1332 47 return Signal::stmtResponse_success;
1333 }
1334
1335 20 Signal FpySequencer::discard_directiveHandler(const FpySequencer_DiscardDirective& directive, DirectiveError& error) {
1336
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 19 times.
20 if (this->m_runtime.stack.size < directive.get_size()) {
1337 1 error = DirectiveError::STACK_UNDERFLOW;
1338 1 return Signal::stmtResponse_failure;
1339 }
1340 // drop the specified amount of bytes off the stack. simple as.
1341 19 this->m_runtime.stack.size -= directive.get_size();
1342 19 return Signal::stmtResponse_success;
1343 }
1344
1345 5 Signal FpySequencer::memCmp_directiveHandler(const FpySequencer_MemCmpDirective& directive, DirectiveError& error) {
1346 // Overflow-safe check: we need size * 2 bytes on the stack
1347 // First check that size * 2 doesn't overflow: size > MAX/2 would overflow
1348 // MAX_STACK_SIZE is the upper bound for stack.size, so if size > MAX_STACK_SIZE/2, we definitely don't have enough
1349
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (directive.get_size() > Fpy::MAX_STACK_SIZE / 2) {
1350 1 error = DirectiveError::STACK_UNDERFLOW;
1351 1 return Signal::stmtResponse_failure;
1352 }
1353 // Now safe to compute size * 2
1354
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 3 times.
4 if (this->m_runtime.stack.size < directive.get_size() * 2) {
1355 1 error = DirectiveError::STACK_UNDERFLOW;
1356 1 return Signal::stmtResponse_failure;
1357 }
1358
1359 // find the starting offsets of the two byte arrays
1360 3 U64 lhsOffset = this->m_runtime.stack.size - directive.get_size() * 2;
1361 3 U64 rhsOffset = this->m_runtime.stack.size - directive.get_size();
1362
1363 // "officially" remove them from the stack
1364 // you have to do this before pushing to the stack, otherwise the result would get placed
1365 // after the byte arrays
1366 3 this->m_runtime.stack.size -= directive.get_size() * 2;
1367
1368 // memcmp the two byte arrays, push FW_SERIALIZE_TRUE_VALUE if they were equal, FW_SERIALIZE_FALSE_VALUE otherwise
1369
2/4
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
3 if (memcmp(this->m_runtime.stack.bytes + lhsOffset, this->m_runtime.stack.bytes + rhsOffset,
1370
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
6 directive.get_size()) == 0) {
1371 2 this->m_runtime.stack.push<U8>(static_cast<U8>(FW_SERIALIZE_TRUE_VALUE));
1372 } else {
1373 1 this->m_runtime.stack.push<U8>(static_cast<U8>(FW_SERIALIZE_FALSE_VALUE));
1374 }
1375 3 return Signal::stmtResponse_success;
1376 }
1377
1378 5 Signal FpySequencer::stackCmd_directiveHandler(const FpySequencer_StackCmdDirective& directive, DirectiveError& error) {
1379 // Overflow-safe check: need argsSize + sizeof(FwOpcodeType) bytes
1380 // Check stack.size >= sizeof(FwOpcodeType) first, then stack.size - sizeof(FwOpcodeType) >= argsSize
1381
4/4
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 3 times.
8 if (this->m_runtime.stack.size < sizeof(FwOpcodeType) ||
1382
1/2
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
3 this->m_runtime.stack.size - sizeof(FwOpcodeType) < directive.get_argsSize()) {
1383 2 error = DirectiveError::STACK_UNDERFLOW;
1384 2 return Signal::stmtResponse_failure;
1385 }
1386
1387 // pop the opcode of the cmd off the stack
1388 // note this means that, unlike the actual byte array that the dispatcher gets,
1389 // these cmds have opcode after the argument buffer
1390 3 FwOpcodeType opcode = this->m_runtime.stack.pop<FwOpcodeType>();
1391 3 U64 argBufOffset = this->m_runtime.stack.size - directive.get_argsSize();
1392
1393 // update the opcode of the cmd we will await
1394 3 this->m_runtime.currentCmdOpcode = opcode;
1395
1396 // also pop the args off the stack
1397 3 this->m_runtime.stack.size -= directive.get_argsSize();
1398
1399 // the cmd response code will be pushed to the stack when it comes back, so make sure
1400 // there is room for it now, before the cmd is dispatched. popping the opcode above
1401 // frees some room, but FwOpcodeType is configurable so it may not be enough
1402
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
3 if (sizeof(Fw::CmdResponse::SerialType) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1403 ✗ error = DirectiveError::STACK_OVERFLOW;
1404 ✗ return Signal::stmtResponse_failure;
1405 }
1406
1407
2/3
✓ Branch 9 taken 3 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 3 times.
3 if (this->sendCmd(opcode, this->m_runtime.stack.bytes + argBufOffset, directive.get_argsSize()) ==
1408 Fw::Success::FAILURE) {
1409 ✗ return Signal::stmtResponse_failure;
1410 } else {
1411 // now tell the SM to wait some more until we get the cmd response back
1412 // if we've already got the response back this should be harmless
1413 3 return Signal::stmtResponse_keepWaiting;
1414 }
1415
1416 return Signal::stmtResponse_success;
1417 }
1418
1419 4 Signal FpySequencer::pushTime_directiveHandler(const FpySequencer_PushTimeDirective& directive, DirectiveError& error) {
1420
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (Fw::Time::SERIALIZED_SIZE > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1421
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_OVERFLOW;
1422 1 return Signal::stmtResponse_failure;
1423 }
1424
1425
1/1
✓ Branch 3 taken 3 times.
3 Fw::Time currentTime = this->getTime();
1426
1427 3 U8 currentTimeBuf[Fw::Time::SERIALIZED_SIZE] = {};
1428
1/1
✓ Branch 2 taken 3 times.
3 Fw::ExternalSerializeBuffer timeEsb(currentTimeBuf, Fw::Time::SERIALIZED_SIZE);
1429
1/1
✓ Branch 2 taken 3 times.
3 Fw::SerializeStatus stat = timeEsb.serializeFrom(currentTime);
1430
1431 // coding error if this failed, we should have enough space
1432 3 FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
1433
1434 // push time to end of stack
1435
2/2
✓ Branch 5 taken 3 times.
✓ Branch 10 taken 3 times.
3 this->m_runtime.stack.push(timeEsb.getBuffAddr(), static_cast<Fpy::StackSizeType>(timeEsb.getSize()));
1436 3 return Signal::stmtResponse_success;
1437 3 }
1438
1439 3 Signal FpySequencer::setSeed_directiveHandler(const FpySequencer_SetSeedDirective& directive, DirectiveError& error) {
1440
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < sizeof(U32)) {
1441 1 error = DirectiveError::STACK_UNDERFLOW;
1442 1 return Signal::stmtResponse_failure;
1443 }
1444
1445 2 U32 seed = this->m_runtime.stack.pop<U32>();
1446 2 this->m_runtime.rng.seed(seed);
1447 2 this->m_runtime.rngSeeded = true;
1448 2 return Signal::stmtResponse_success;
1449 }
1450
1451 7 Signal FpySequencer::pushRand_directiveHandler(const FpySequencer_PushRandDirective& directive, DirectiveError& error) {
1452
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
7 if (sizeof(U32) > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1453 1 error = DirectiveError::STACK_OVERFLOW;
1454 1 return Signal::stmtResponse_failure;
1455 }
1456
1457
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 4 times.
6 if (!this->m_runtime.rngSeeded) {
1458
1/1
✓ Branch 3 taken 2 times.
2 Fw::Time currentTime = this->getTime();
1459
2/2
✓ Branch 3 taken 2 times.
✓ Branch 10 taken 2 times.
8 std::seed_seq seedSeq{static_cast<U32>(currentTime.getTimeBase()), static_cast<U32>(currentTime.getContext()),
1460
3/3
✓ Branch 4 taken 2 times.
✓ Branch 8 taken 2 times.
✓ Branch 11 taken 2 times.
8 currentTime.getSeconds(), currentTime.getUSeconds()};
1461
1/1
✓ Branch 4 taken 2 times.
2 this->m_runtime.rng.seed(seedSeq);
1462 2 this->m_runtime.rngSeeded = true;
1463 2 }
1464
1465 6 U32 randVal = static_cast<U32>(this->m_runtime.rng());
1466 6 this->m_runtime.stack.push(randVal);
1467 6 return Signal::stmtResponse_success;
1468 }
1469
1470 8 Signal FpySequencer::getField_directiveHandler(const FpySequencer_GetFieldDirective& directive, DirectiveError& error) {
1471 // Need sizeof(StackSizeType) for the offset AND parentSize for the parent data
1472 // Check we have enough for the offset first
1473
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 7 times.
8 if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType)) {
1474 1 error = DirectiveError::STACK_UNDERFLOW;
1475 1 return Signal::stmtResponse_failure;
1476 }
1477 // After popping the offset, we need at least parentSize bytes remaining
1478
2/2
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 5 times.
7 if (this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < directive.get_parentSize()) {
1479 2 error = DirectiveError::STACK_UNDERFLOW;
1480 2 return Signal::stmtResponse_failure;
1481 }
1482
1483 5 Fpy::StackSizeType offset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1484
1485 // Overflow-safe check: offset + memberSize > parentSize
1486 // Rewritten as: check offset <= parentSize first, then memberSize > parentSize - offset
1487
6/6
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
✓ Branch 14 taken 2 times.
✓ Branch 15 taken 2 times.
✓ Branch 16 taken 3 times.
✓ Branch 17 taken 2 times.
5 if (offset > directive.get_parentSize() || directive.get_memberSize() > directive.get_parentSize() - offset) {
1488 // i think it's somewhat ambiguous whether this is a stack access out of bounds
1489 // but there isn't really an error code that better reflects this, and i guess
1490 // it's technically true
1491 3 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1492 3 return Signal::stmtResponse_failure;
1493 }
1494
1495 // the resulting bytes should move to the start of the parent array
1496
1497 // Calculate the offset of the parent start in the stack
1498 2 Fpy::StackSizeType parentStartOffset = this->m_runtime.stack.size - directive.get_parentSize();
1499 // Overflow-safe: parentStartOffset + offset cannot overflow since offset <= parentSize
1500 // and parentStartOffset + parentSize == stack.size (which is bounded)
1501 2 this->m_runtime.stack.move(parentStartOffset, parentStartOffset + offset, directive.get_memberSize());
1502 // adjust stack size by the diff between the member and the parent
1503 2 this->m_runtime.stack.size -= (directive.get_parentSize() - directive.get_memberSize());
1504 2 return Signal::stmtResponse_success;
1505 }
1506
1507 9 Signal FpySequencer::peek_directiveHandler(const FpySequencer_PeekDirective& directive, DirectiveError& error) {
1508 // must have at least two StackSizeType on stack
1509
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 7 times.
9 if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) * 2) {
1510 2 error = DirectiveError::STACK_UNDERFLOW;
1511 2 return Signal::stmtResponse_failure;
1512 }
1513
1514 7 Fpy::StackSizeType offset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1515 7 Fpy::StackSizeType byteCount = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1516
1517 // Check offset doesn't exceed stack size (after both pops)
1518
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
7 if (offset > this->m_runtime.stack.size) {
1519 // would access past the bottom of the stack
1520 // note we allow the equals case because the byteCount might be 0
1521 1 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1522 1 return Signal::stmtResponse_failure;
1523 }
1524
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
6 if (byteCount > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1525 // we would overflow the stack if we pushed this many bytes to it
1526 2 error = DirectiveError::STACK_OVERFLOW;
1527 2 return Signal::stmtResponse_failure;
1528 }
1529 // Overflow-safe check: byteCount + offset > stack.size
1530 // Rewritten as: check offset <= stack.size (done above), then byteCount > stack.size - offset
1531
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if (byteCount > this->m_runtime.stack.size - offset) {
1532 // would access past the bottom of the stack
1533 ✗ error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1534 ✗ return Signal::stmtResponse_failure;
1535 }
1536 // start copying from the lowest byte of the src array
1537 4 U8* src = this->m_runtime.stack.top() - offset - byteCount;
1538 4 this->m_runtime.stack.push(src, byteCount);
1539 4 return Signal::stmtResponse_success;
1540 }
1541
1542 7 Signal FpySequencer::storeRel_directiveHandler(const FpySequencer_StoreRelDirective& directive, DirectiveError& error) {
1543 // Need enough bytes for the value and the offset (SignedStackSizeType = 4 bytes)
1544 // Overflow-safe: check stack.size >= sizeof(SignedStackSizeType) first, then stack.size -
1545 // sizeof(SignedStackSizeType) >= size
1546
4/4
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 5 times.
13 if (this->m_runtime.stack.size < sizeof(Fpy::SignedStackSizeType) ||
1547
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 5 times.
6 this->m_runtime.stack.size - sizeof(Fpy::SignedStackSizeType) < directive.get_size()) {
1548 2 error = DirectiveError::STACK_UNDERFLOW;
1549 2 return Signal::stmtResponse_failure;
1550 }
1551
1552 // Pop the signed offset from the stack
1553 5 Fpy::SignedStackSizeType lvarOffset = this->m_runtime.stack.pop<Fpy::SignedStackSizeType>();
1554
1555 5 I64 addr = static_cast<I64>(this->m_runtime.stack.currentFrameStart) + lvarOffset;
1556
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
5 if (addr < 0 || addr > Fpy::MAX_STACK_SIZE) {
1557 1 error = DirectiveError::STACK_ACCESS_OUT_OF_BOUNDS;
1558 1 return Signal::stmtResponse_failure;
1559 }
1560 4 return this->storeHelper(static_cast<Fpy::StackSizeType>(addr), directive.get_size(), error);
1561 }
1562
1563 5 Signal FpySequencer::call_directiveHandler(const FpySequencer_CallDirective& directive, DirectiveError& error) {
1564 // Need at least 4 bytes for the target address
1565
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (this->m_runtime.stack.size < sizeof(U32)) {
1566 1 error = DirectiveError::STACK_UNDERFLOW;
1567 1 return Signal::stmtResponse_failure;
1568 }
1569
1570 // Pop the target directive index from the stack
1571 4 U32 target = this->m_runtime.stack.pop<U32>();
1572
1573 // Check if we have space to push return address and saved frame pointer (8 bytes total)
1574
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 if (this->m_runtime.stack.size + sizeof(Fpy::StackSizeType) + sizeof(U32) > Fpy::MAX_STACK_SIZE) {
1575 1 error = DirectiveError::STACK_OVERFLOW;
1576 1 return Signal::stmtResponse_failure;
1577 }
1578
1579 // Check target is within bounds (will also be checked at execution time)
1580
2/2
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 2 times.
3 if (target > m_sequenceObj.get_header().get_statementCount()) {
1581 1 error = DirectiveError::STMT_OUT_OF_BOUNDS;
1582 1 return Signal::stmtResponse_failure;
1583 }
1584
1585 // Save the return address (next instruction after CALL)
1586 2 U32 returnAddr = this->m_runtime.nextStatementIndex;
1587
1588 // Set the next instruction to the target
1589 2 this->m_runtime.nextStatementIndex = target;
1590
1591 // Push the return address to the stack
1592 2 this->m_runtime.stack.push<U32>(returnAddr);
1593
1594 // Push the current frame pointer to the stack
1595 2 this->m_runtime.stack.push<Fpy::StackSizeType>(this->m_runtime.stack.currentFrameStart);
1596
1597 // Set the new frame pointer to the current top of stack
1598 2 this->m_runtime.stack.currentFrameStart = this->m_runtime.stack.size;
1599
1600 2 return Signal::stmtResponse_success;
1601 }
1602
1603 7 Signal FpySequencer::return_directiveHandler(const FpySequencer_ReturnDirective& directive, DirectiveError& error) {
1604 7 Fpy::StackSizeType returnValSize = directive.get_returnValSize();
1605 7 Fpy::StackSizeType callArgsSize = directive.get_callArgsSize();
1606
1607 // Check we have enough bytes for the return value
1608
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
7 if (this->m_runtime.stack.size < returnValSize) {
1609 1 error = DirectiveError::STACK_UNDERFLOW;
1610 1 return Signal::stmtResponse_failure;
1611 }
1612
1613 // Remember where the return value lives; it is moved down the stack below rather than copied
1614 // through a local buffer, which at Fpy::MAX_STACK_SIZE would not fit a typical task stack
1615 6 const Fpy::StackSizeType returnValOffset = this->m_runtime.stack.size - returnValSize;
1616
1617 // Truncate the stack to stack_frame_start (discard all local variables)
1618
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 5 times.
6 if (this->m_runtime.stack.currentFrameStart > this->m_runtime.stack.size) {
1619 1 error = DirectiveError::FRAME_START_OUT_OF_BOUNDS;
1620 1 return Signal::stmtResponse_failure;
1621 }
1622 5 this->m_runtime.stack.size = this->m_runtime.stack.currentFrameStart;
1623
1624 // Check we have enough bytes for saved frame pointer and return address
1625
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
5 if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) + sizeof(U32)) {
1626 ✗ error = DirectiveError::STACK_UNDERFLOW;
1627 ✗ return Signal::stmtResponse_failure;
1628 }
1629
1630 // Pop the saved frame pointer
1631 5 Fpy::StackSizeType savedFramePtr = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1632
1633 // Pop the return address
1634 5 U32 returnAddr = this->m_runtime.stack.pop<U32>();
1635
1636 // Restore the frame pointer
1637
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 4 times.
5 if (savedFramePtr > this->m_runtime.stack.size) {
1638 1 error = DirectiveError::FRAME_START_OUT_OF_BOUNDS;
1639 1 return Signal::stmtResponse_failure;
1640 }
1641 4 this->m_runtime.stack.currentFrameStart = savedFramePtr;
1642
1643 // Validate the return address is within bounds
1644
2/2
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 3 times.
4 if (returnAddr > m_sequenceObj.get_header().get_statementCount()) {
1645 1 error = DirectiveError::STMT_OUT_OF_BOUNDS;
1646 1 return Signal::stmtResponse_failure;
1647 }
1648
1649 // Set the next instruction to the return address
1650 3 this->m_runtime.nextStatementIndex = returnAddr;
1651
1652 // Check that we have enough bytes for the call arguments
1653
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 2 times.
3 if (this->m_runtime.stack.size < callArgsSize) {
1654 1 error = DirectiveError::STACK_UNDERFLOW;
1655 1 return Signal::stmtResponse_failure;
1656 }
1657 // Discard the function arguments
1658 2 this->m_runtime.stack.size -= callArgsSize;
1659
1660 // Push the return value
1661
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
2 if (returnValSize > Fpy::MAX_STACK_SIZE - this->m_runtime.stack.size) {
1662 ✗ error = DirectiveError::STACK_OVERFLOW;
1663 ✗ return Signal::stmtResponse_failure;
1664 }
1665
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (returnValSize > 0) {
1666 // Not Stack::move: the source region sits above the truncated stack size, which
1667 // Stack::move rejects. Both regions were bounds-checked above against MAX_STACK_SIZE.
1668
2/4
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
1 (void)memmove(this->m_runtime.stack.top(), &this->m_runtime.stack.bytes[returnValOffset], returnValSize);
1669 1 this->m_runtime.stack.size += returnValSize;
1670 }
1671
1672 2 return Signal::stmtResponse_success;
1673 }
1674
1675 5 Signal FpySequencer::loadAbs_directiveHandler(const FpySequencer_LoadAbsDirective& directive, DirectiveError& error) {
1676 5 return this->loadHelper(directive.get_globalOffset(), directive.get_size(), error);
1677 }
1678
1679 5 Signal FpySequencer::storeAbs_directiveHandler(const FpySequencer_StoreAbsDirective& directive, DirectiveError& error) {
1680 5 Fpy::StackSizeType size = directive.get_size();
1681
1682 // Need enough bytes for the value and the offset
1683 // Overflow-safe: check stack.size >= sizeof(StackSizeType) first, then stack.size - sizeof >= size
1684
2/2
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 1 times.
5 if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType) ||
1685
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 3 times.
4 this->m_runtime.stack.size - sizeof(Fpy::StackSizeType) < size) {
1686 2 error = DirectiveError::STACK_UNDERFLOW;
1687 2 return Signal::stmtResponse_failure;
1688 }
1689
1690 // Pop the global offset from the stack
1691 3 Fpy::StackSizeType globalOffset = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1692
1693 3 return this->storeHelper(globalOffset, size, error);
1694 }
1695
1696 5 Signal FpySequencer::storeAbsConstOffset_directiveHandler(const FpySequencer_StoreAbsConstOffsetDirective& directive,
1697 DirectiveError& error) {
1698 5 return this->storeHelper(directive.get_globalOffset(), directive.get_size(), error);
1699 }
1700
1701 12 Signal FpySequencer::popEvent_directiveHandler(const FpySequencer_PopEventDirective& directive, DirectiveError& error) {
1702 // Pop messageSize from the stack
1703
2/2
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 11 times.
12 if (this->m_runtime.stack.size < sizeof(Fpy::StackSizeType)) {
1704
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_UNDERFLOW;
1705 1 return Signal::stmtResponse_failure;
1706 }
1707
1/1
✓ Branch 5 taken 11 times.
11 Fpy::StackSizeType messageSize = this->m_runtime.stack.pop<Fpy::StackSizeType>();
1708
1709 11 const Fpy::StackSizeType severitySize = static_cast<Fpy::StackSizeType>(sizeof(Fw::LogSeverity::SerialType));
1710
1711 // Need message_size bytes + sizeof(LogSeverity serial type) for severity
1712
3/4
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 9 times.
11 if (this->m_runtime.stack.size < severitySize || this->m_runtime.stack.size - severitySize < messageSize) {
1713
1/1
✓ Branch 4 taken 2 times.
2 error = DirectiveError::STACK_UNDERFLOW;
1714 2 return Signal::stmtResponse_failure;
1715 }
1716
1717 // Pop message bytes first
1718 9 U8 messageBuf[FW_LOG_STRING_MAX_SIZE] = {};
1719 // don't read in more than (log string size) - 1 bytes
1720 9 Fpy::StackSizeType clampedSize = std::min(messageSize, static_cast<Fpy::StackSizeType>(FW_LOG_STRING_MAX_SIZE - 1));
1721 // If message is larger than buffer, discard the excess bytes first (from top of stack, which is the end of the
1722 // message)
1723
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (messageSize > clampedSize) {
1724 ✗ Fpy::StackSizeType excess = messageSize - clampedSize;
1725 ✗ FW_ASSERT(this->m_runtime.stack.size >= excess, static_cast<FwAssertArgType>(this->m_runtime.stack.size),
1726 static_cast<FwAssertArgType>(excess));
1727 ✗ this->m_runtime.stack.size -= excess;
1728 }
1729
1/1
✓ Branch 4 taken 9 times.
9 this->m_runtime.stack.pop(messageBuf, clampedSize);
1730 9 messageBuf[clampedSize] = '\0';
1731
1732 // Pop severity
1733
1/1
✓ Branch 4 taken 9 times.
9 Fw::LogSeverity::SerialType severity = this->m_runtime.stack.pop<Fw::LogSeverity::SerialType>();
1734
1735 // Construct the message string
1736
1/1
✓ Branch 2 taken 9 times.
9 Fw::String messageStr(reinterpret_cast<const char*>(messageBuf));
1737
1738 // Emit the appropriate event based on severity
1739
8/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
9 switch (severity) {
1740 1 case Fw::LogSeverity::FATAL:
1741
1/1
✓ Branch 8 taken 1 times.
1 this->log_FATAL_LogFatal(this->m_sequenceFilePath, messageStr);
1742 1 break;
1743 1 case Fw::LogSeverity::WARNING_HI:
1744
1/1
✓ Branch 8 taken 1 times.
1 this->log_WARNING_HI_LogWarningHi(this->m_sequenceFilePath, messageStr);
1745 1 break;
1746 1 case Fw::LogSeverity::WARNING_LO:
1747
1/1
✓ Branch 8 taken 1 times.
1 this->log_WARNING_LO_LogWarningLo(this->m_sequenceFilePath, messageStr);
1748 1 break;
1749 1 case Fw::LogSeverity::COMMAND:
1750
1/1
✓ Branch 8 taken 1 times.
1 this->log_COMMAND_LogCommand(this->m_sequenceFilePath, messageStr);
1751 1 break;
1752 2 case Fw::LogSeverity::ACTIVITY_HI:
1753
1/1
✓ Branch 8 taken 2 times.
2 this->log_ACTIVITY_HI_LogActivityHi(this->m_sequenceFilePath, messageStr);
1754 2 break;
1755 1 case Fw::LogSeverity::ACTIVITY_LO:
1756
1/1
✓ Branch 8 taken 1 times.
1 this->log_ACTIVITY_LO_LogActivityLo(this->m_sequenceFilePath, messageStr);
1757 1 break;
1758 1 case Fw::LogSeverity::DIAGNOSTIC:
1759
1/1
✓ Branch 8 taken 1 times.
1 this->log_DIAGNOSTIC_LogDiagnostic(this->m_sequenceFilePath, messageStr);
1760 1 break;
1761 1 default:
1762
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::INVALID_ARG;
1763 1 return Signal::stmtResponse_failure;
1764 }
1765
1766 8 return Signal::stmtResponse_success;
1767 9 }
1768
1769 10 Signal FpySequencer::popSerializable_directiveHandler(const FpySequencer_PopSerializableDirective& directive,
1770 DirectiveError& error) {
1771 // No size assertion here: an oversized size is untrusted sequence content and is rejected by
1772 // the stack check below, since the stack can never hold more than Fpy::MAX_STACK_SIZE bytes
1773
1774 // Validate port index is in range (using enum constant value)
1775 10 constexpr FwIndexType MAX_PORTS = static_cast<FwIndexType>(Svc::Fpy::SerialPortIndex::MAX_SERIAL_PORTS);
1776 10 const FwIndexType portIndex = directive.get_portIndex();
1777
1778 // Check for negative port index or out of bounds
1779
3/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 9 times.
10 if (portIndex < 0 || portIndex >= MAX_PORTS) {
1780
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::SERIAL_PORT_INVALID_INDEX;
1781 1 return Signal::stmtResponse_failure;
1782 }
1783
1784 // Check port is connected
1785
3/3
✓ Branch 5 taken 9 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 8 times.
9 if (!this->isConnected_serialOut_OutputPort(portIndex)) {
1786
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::SERIAL_PORT_NOT_CONNECTED;
1787 1 return Signal::stmtResponse_failure;
1788 }
1789
1790 // Validate data size on stack
1791
2/2
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 7 times.
8 if (this->m_runtime.stack.size < directive.get_size()) {
1792
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::STACK_UNDERFLOW;
1793 1 return Signal::stmtResponse_failure;
1794 }
1795
1796 // Create external buffer referencing stack data (no copy)
1797
1/1
✓ Branch 4 taken 7 times.
7 U8* dataPtr = this->m_runtime.stack.top() - directive.get_size();
1798
1/1
✓ Branch 6 taken 7 times.
7 Fw::ExternalSerializeBuffer buf(dataPtr, directive.get_size());
1799
1800 // Set buffer length and verify success
1801
1/1
✓ Branch 6 taken 7 times.
7 Fw::SerializeStatus stat = buf.setBuffLen(directive.get_size());
1802 7 FW_ASSERT(stat == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(stat));
1803
1804 // Call output port; a typed downstream port reports deserialize failures here, which is
1805 // untrusted sequence content (e.g. undersized payload), not an invariant to assert on
1806
1/1
✓ Branch 5 taken 7 times.
7 Fw::SerializeStatus portStatus = this->serialOut_out(portIndex, buf);
1807
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (portStatus != Fw::SerializeStatus::FW_SERIALIZE_OK) {
1808
1/1
✓ Branch 4 taken 1 times.
1 error = DirectiveError::SERIAL_PORT_DESERIALIZE_FAILURE;
1809 1 return Signal::stmtResponse_failure;
1810 }
1811
1812 // Pop data from stack
1813 6 this->m_runtime.stack.size -= directive.get_size();
1814
1815 6 return Signal::stmtResponse_success;
1816 7 }
1817
1818 } // namespace Svc
1819