1 /*
2 * *****************************************************************************
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 * *****************************************************************************
31 *
32 * Code to execute bc programs.
33 *
34 */
35
36 #include <assert.h>
37 #include <stdbool.h>
38 #include <string.h>
39
40 #include <setjmp.h>
41
42 #include <signal.h>
43
44 #include <time.h>
45
46 #include <read.h>
47 #include <parse.h>
48 #include <program.h>
49 #include <vm.h>
50
51 /**
52 * Does a type check for something that expects a number.
53 * @param r The result that will be checked.
54 * @param n The result's number.
55 */
56 static inline void
bc_program_type_num(BcResult * r,BcNum * n)57 bc_program_type_num(BcResult* r, BcNum* n)
58 {
59 #if BC_ENABLED
60
61 // This should have already been taken care of.
62 assert(r->t != BC_RESULT_VOID);
63
64 #endif // BC_ENABLED
65
66 if (BC_ERR(!BC_PROG_NUM(r, n))) bc_err(BC_ERR_EXEC_TYPE);
67 }
68
69 #if BC_ENABLED
70
71 /**
72 * Does a type check.
73 * @param r The result to check.
74 * @param t The type that the result should be.
75 */
76 static void
bc_program_type_match(BcResult * r,BcType t)77 bc_program_type_match(BcResult* r, BcType t)
78 {
79 if (BC_ERR((r->t != BC_RESULT_ARRAY) != (!t))) bc_err(BC_ERR_EXEC_TYPE);
80 }
81 #endif // BC_ENABLED
82
83 /**
84 * Pulls an index out of a bytecode vector and updates the index into the vector
85 * to point to the spot after the index. For more details on bytecode indices,
86 * see the development manual (manuals/development.md#bytecode-indices).
87 * @param code The bytecode vector.
88 * @param bgn An in/out parameter; the index into the vector that will be
89 * updated.
90 * @return The index at @a bgn in the bytecode vector.
91 */
92 static size_t
bc_program_index(const char * restrict code,size_t * restrict bgn)93 bc_program_index(const char* restrict code, size_t* restrict bgn)
94 {
95 uchar amt = (uchar) code[(*bgn)++], i = 0;
96 size_t res = 0;
97
98 for (; i < amt; ++i, ++(*bgn))
99 {
100 size_t temp = ((size_t) ((int) (uchar) code[*bgn]) & UCHAR_MAX);
101 res |= (temp << (i * CHAR_BIT));
102 }
103
104 return res;
105 }
106
107 /**
108 * Returns a string from a result and its number.
109 * @param p The program.
110 * @param n The number tied to the result.
111 * @return The string corresponding to the result and number.
112 */
113 static inline char*
bc_program_string(BcProgram * p,const BcNum * n)114 bc_program_string(BcProgram* p, const BcNum* n)
115 {
116 return *((char**) bc_vec_item(&p->strs, n->scale));
117 }
118
119 #if BC_ENABLED
120
121 /**
122 * Prepares the globals for a function call. This is only called when global
123 * stacks are on because it pushes a copy of the current globals onto each of
124 * their respective stacks.
125 * @param p The program.
126 */
127 static void
bc_program_prepGlobals(BcProgram * p)128 bc_program_prepGlobals(BcProgram* p)
129 {
130 size_t i;
131
132 for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i)
133 {
134 bc_vec_push(p->globals_v + i, p->globals + i);
135 }
136
137 #if BC_ENABLE_EXTRA_MATH
138 bc_rand_push(&p->rng);
139 #endif // BC_ENABLE_EXTRA_MATH
140 }
141
142 /**
143 * Pops globals stacks on returning from a function, or in the case of reset,
144 * pops all but one item on each global stack.
145 * @param p The program.
146 * @param reset True if all but one item on each stack should be popped, false
147 * otherwise.
148 */
149 static void
bc_program_popGlobals(BcProgram * p,bool reset)150 bc_program_popGlobals(BcProgram* p, bool reset)
151 {
152 size_t i;
153
154 BC_SIG_ASSERT_LOCKED;
155
156 for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i)
157 {
158 BcVec* v = p->globals_v + i;
159 bc_vec_npop(v, reset ? v->len - 1 : 1);
160 p->globals[i] = BC_PROG_GLOBAL(v);
161 }
162
163 #if BC_ENABLE_EXTRA_MATH
164 bc_rand_pop(&p->rng, reset);
165 #endif // BC_ENABLE_EXTRA_MATH
166 }
167
168 /**
169 * Derefeneces an array reference and returns a pointer to the real array.
170 * @param p The program.
171 * @param vec The reference vector.
172 * @return A pointer to the desired array.
173 */
174 static BcVec*
bc_program_dereference(const BcProgram * p,BcVec * vec)175 bc_program_dereference(const BcProgram* p, BcVec* vec)
176 {
177 BcVec* v;
178 size_t vidx, nidx, i = 0;
179
180 // We want to be sure we have a reference vector.
181 assert(vec->size == sizeof(uchar));
182
183 // Get the index of the vector in arrs, then the index of the original
184 // referenced vector.
185 vidx = bc_program_index(vec->v, &i);
186 nidx = bc_program_index(vec->v, &i);
187
188 v = bc_vec_item(bc_vec_item(&p->arrs, vidx), nidx);
189
190 // We want to be sure we do *not* have a reference vector.
191 assert(v->size != sizeof(uchar));
192
193 return v;
194 }
195
196 #endif // BC_ENABLED
197
198 /**
199 * Creates a BcNum from a BcBigDig and pushes onto the results stack. This is a
200 * convenience function.
201 * @param p The program.
202 * @param dig The BcBigDig to push onto the results stack.
203 * @param type The type that the pushed result should be.
204 */
205 static void
bc_program_pushBigdig(BcProgram * p,BcBigDig dig,BcResultType type)206 bc_program_pushBigdig(BcProgram* p, BcBigDig dig, BcResultType type)
207 {
208 BcResult res;
209
210 res.t = type;
211
212 BC_SIG_LOCK;
213
214 bc_num_createFromBigdig(&res.d.n, dig);
215 bc_vec_push(&p->results, &res);
216
217 BC_SIG_UNLOCK;
218 }
219
220 size_t
bc_program_addString(BcProgram * p,const char * str)221 bc_program_addString(BcProgram* p, const char* str)
222 {
223 size_t idx;
224
225 BC_SIG_ASSERT_LOCKED;
226
227 if (bc_map_insert(&p->str_map, str, p->strs.len, &idx))
228 {
229 char** str_ptr;
230 BcId* id = bc_vec_item(&p->str_map, idx);
231
232 // Get the index.
233 idx = id->idx;
234
235 // Push an empty string on the proper vector.
236 str_ptr = bc_vec_pushEmpty(&p->strs);
237
238 // We reuse the string in the ID (allocated by bc_map_insert()), because
239 // why not?
240 *str_ptr = id->name;
241 }
242 else
243 {
244 BcId* id = bc_vec_item(&p->str_map, idx);
245 idx = id->idx;
246 }
247
248 return idx;
249 }
250
251 size_t
bc_program_search(BcProgram * p,const char * name,bool var)252 bc_program_search(BcProgram* p, const char* name, bool var)
253 {
254 BcVec* v;
255 BcVec* map;
256 size_t i;
257
258 BC_SIG_ASSERT_LOCKED;
259
260 // Grab the right vector and map.
261 v = var ? &p->vars : &p->arrs;
262 map = var ? &p->var_map : &p->arr_map;
263
264 // We do an insert because the variable might not exist yet. This is because
265 // the parser calls this function. If the insert succeeds, we create a stack
266 // for the variable/array. But regardless, bc_map_insert() gives us the
267 // index of the item in i.
268 if (bc_map_insert(map, name, v->len, &i))
269 {
270 BcVec* temp = bc_vec_pushEmpty(v);
271 bc_array_init(temp, var);
272 }
273
274 return ((BcId*) bc_vec_item(map, i))->idx;
275 }
276
277 /**
278 * Returns the correct variable or array stack for the type.
279 * @param p The program.
280 * @param idx The index of the variable or array in the variable or array
281 * vector.
282 * @param type The type of vector to return.
283 * @return A pointer to the variable or array stack.
284 */
285 static inline BcVec*
bc_program_vec(const BcProgram * p,size_t idx,BcType type)286 bc_program_vec(const BcProgram* p, size_t idx, BcType type)
287 {
288 const BcVec* v = (type == BC_TYPE_VAR) ? &p->vars : &p->arrs;
289 return bc_vec_item(v, idx);
290 }
291
292 /**
293 * Returns a pointer to the BcNum corresponding to the result. There is one
294 * case, however, where this returns a pointer to a BcVec: if the type of the
295 * result is array. In that case, the pointer is casted to a pointer to BcNum,
296 * but is never used. The function that calls this expecting an array casts the
297 * pointer back. This function is called a lot and needs to be as fast as
298 * possible.
299 * @param p The program.
300 * @param r The result whose number will be returned.
301 * @return The BcNum corresponding to the result.
302 */
303 static BcNum*
bc_program_num(BcProgram * p,BcResult * r)304 bc_program_num(BcProgram* p, BcResult* r)
305 {
306 BcNum* n;
307
308 #ifdef _WIN32
309 // Windows made it an error to not initialize this, so shut it up.
310 // I don't want to do this on other platforms because this procedure
311 // is one of the most heavily-used, and eliminating the initialization
312 // is a performance win.
313 n = NULL;
314 #endif // _WIN32
315
316 switch (r->t)
317 {
318 case BC_RESULT_STR:
319 case BC_RESULT_TEMP:
320 case BC_RESULT_IBASE:
321 case BC_RESULT_SCALE:
322 case BC_RESULT_OBASE:
323 #if BC_ENABLE_EXTRA_MATH
324 case BC_RESULT_SEED:
325 #endif // BC_ENABLE_EXTRA_MATH
326 {
327 n = &r->d.n;
328 break;
329 }
330
331 case BC_RESULT_VAR:
332 case BC_RESULT_ARRAY:
333 case BC_RESULT_ARRAY_ELEM:
334 {
335 BcVec* v;
336 BcType type = (r->t == BC_RESULT_VAR) ? BC_TYPE_VAR : BC_TYPE_ARRAY;
337
338 // Get the correct variable or array vector.
339 v = bc_program_vec(p, r->d.loc.loc, type);
340
341 // Surprisingly enough, the hard case is *not* returning an array;
342 // it's returning an array element. This is because we have to dig
343 // deeper to get *to* the element. That's what the code inside this
344 // if statement does.
345 if (r->t == BC_RESULT_ARRAY_ELEM)
346 {
347 size_t idx = r->d.loc.idx;
348
349 v = bc_vec_item(v, r->d.loc.stack_idx);
350
351 #if BC_ENABLED
352 // If this is true, we have a reference vector, so dereference
353 // it. The reason we don't need to worry about it for returning
354 // a straight array is because we only care about references
355 // when we access elements of an array that is a reference. That
356 // is this code, so in essence, this line takes care of arrays
357 // as well.
358 if (v->size == sizeof(uchar)) v = bc_program_dereference(p, v);
359 #endif // BC_ENABLED
360
361 // We want to be sure we got a valid array of numbers.
362 assert(v->size == sizeof(BcNum));
363
364 // The bc spec says that if an element is accessed that does not
365 // exist, it should be preinitialized to 0. Well, if we access
366 // an element *way* out there, we have to preinitialize all
367 // elements between the current last element and the actual
368 // accessed element.
369 if (v->len <= idx)
370 {
371 BC_SIG_LOCK;
372 bc_array_expand(v, bc_vm_growSize(idx, 1));
373 BC_SIG_UNLOCK;
374 }
375
376 n = bc_vec_item(v, idx);
377 }
378 // This is either a number (for a var) or an array (for an array).
379 // Because bc_vec_top() and bc_vec_item() return a void*, we don't
380 // need to cast.
381 else
382 {
383 #if BC_ENABLED
384 if (BC_IS_BC)
385 {
386 n = bc_vec_item(v, r->d.loc.stack_idx);
387 }
388 else
389 #endif // BC_ENABLED
390 {
391 n = bc_vec_top(v);
392 }
393 }
394
395 break;
396 }
397
398 case BC_RESULT_ZERO:
399 {
400 n = &vm->zero;
401 break;
402 }
403
404 case BC_RESULT_ONE:
405 {
406 n = &vm->one;
407 break;
408 }
409
410 #if BC_ENABLED
411 // We should never get here; this is taken care of earlier because a
412 // result is expected.
413 case BC_RESULT_VOID:
414 #if BC_DEBUG
415 {
416 abort();
417 // Fallthrough
418 }
419 #endif // BC_DEBUG
420 case BC_RESULT_LAST:
421 {
422 n = &p->last;
423 break;
424 }
425 #endif // BC_ENABLED
426
427 #if BC_GCC
428 // This is here in GCC to quiet the "maybe-uninitialized" warning.
429 default:
430 {
431 abort();
432 }
433 #endif // BC_GCC
434 }
435
436 return n;
437 }
438
439 /**
440 * Prepares an operand for use.
441 * @param p The program.
442 * @param r An out parameter; this is set to the pointer to the result that
443 * we care about.
444 * @param n An out parameter; this is set to the pointer to the number that
445 * we care about.
446 * @param idx The index of the result from the top of the results stack.
447 */
448 static void
bc_program_operand(BcProgram * p,BcResult ** r,BcNum ** n,size_t idx)449 bc_program_operand(BcProgram* p, BcResult** r, BcNum** n, size_t idx)
450 {
451 *r = bc_vec_item_rev(&p->results, idx);
452
453 #if BC_ENABLED
454 if (BC_ERR((*r)->t == BC_RESULT_VOID)) bc_err(BC_ERR_EXEC_VOID_VAL);
455 #endif // BC_ENABLED
456
457 *n = bc_program_num(p, *r);
458 }
459
460 /**
461 * Prepares the operands of a binary operator.
462 * @param p The program.
463 * @param l An out parameter; this is set to the pointer to the result for
464 * the left operand.
465 * @param ln An out parameter; this is set to the pointer to the number for
466 * the left operand.
467 * @param r An out parameter; this is set to the pointer to the result for
468 * the right operand.
469 * @param rn An out parameter; this is set to the pointer to the number for
470 * the right operand.
471 * @param idx The starting index where the operands are in the results stack,
472 * starting from the top.
473 */
474 static void
bc_program_binPrep(BcProgram * p,BcResult ** l,BcNum ** ln,BcResult ** r,BcNum ** rn,size_t idx)475 bc_program_binPrep(BcProgram* p, BcResult** l, BcNum** ln, BcResult** r,
476 BcNum** rn, size_t idx)
477 {
478 BcResultType lt;
479
480 assert(p != NULL && l != NULL && ln != NULL && r != NULL && rn != NULL);
481
482 #ifndef BC_PROG_NO_STACK_CHECK
483 // Check the stack for dc.
484 if (BC_IS_DC)
485 {
486 if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 2)))
487 {
488 bc_err(BC_ERR_EXEC_STACK);
489 }
490 }
491 #endif // BC_PROG_NO_STACK_CHECK
492
493 assert(BC_PROG_STACK(&p->results, idx + 2));
494
495 // Get the operands.
496 bc_program_operand(p, l, ln, idx + 1);
497 bc_program_operand(p, r, rn, idx);
498
499 lt = (*l)->t;
500
501 #if BC_ENABLED
502 // bc_program_operand() checked these for us.
503 assert(lt != BC_RESULT_VOID && (*r)->t != BC_RESULT_VOID);
504 #endif // BC_ENABLED
505
506 // We run this again under these conditions in case any vector has been
507 // reallocated out from under the BcNums or arrays we had. In other words,
508 // this is to fix pointer invalidation.
509 if (lt == (*r)->t && (lt == BC_RESULT_VAR || lt == BC_RESULT_ARRAY_ELEM))
510 {
511 *ln = bc_program_num(p, *l);
512 }
513
514 if (BC_ERR(lt == BC_RESULT_STR)) bc_err(BC_ERR_EXEC_TYPE);
515 }
516
517 /**
518 * Prepares the operands of a binary operator and type checks them. This is
519 * separate from bc_program_binPrep() because some places want this, others want
520 * bc_program_binPrep().
521 * @param p The program.
522 * @param l An out parameter; this is set to the pointer to the result for
523 * the left operand.
524 * @param ln An out parameter; this is set to the pointer to the number for
525 * the left operand.
526 * @param r An out parameter; this is set to the pointer to the result for
527 * the right operand.
528 * @param rn An out parameter; this is set to the pointer to the number for
529 * the right operand.
530 * @param idx The starting index where the operands are in the results stack,
531 * starting from the top.
532 */
533 static void
bc_program_binOpPrep(BcProgram * p,BcResult ** l,BcNum ** ln,BcResult ** r,BcNum ** rn,size_t idx)534 bc_program_binOpPrep(BcProgram* p, BcResult** l, BcNum** ln, BcResult** r,
535 BcNum** rn, size_t idx)
536 {
537 bc_program_binPrep(p, l, ln, r, rn, idx);
538 bc_program_type_num(*l, *ln);
539 bc_program_type_num(*r, *rn);
540 }
541
542 /**
543 * Prepares the operands of an assignment operator.
544 * @param p The program.
545 * @param l An out parameter; this is set to the pointer to the result for the
546 * left operand.
547 * @param ln An out parameter; this is set to the pointer to the number for the
548 * left operand.
549 * @param r An out parameter; this is set to the pointer to the result for the
550 * right operand.
551 * @param rn An out parameter; this is set to the pointer to the number for the
552 * right operand.
553 */
554 static void
bc_program_assignPrep(BcProgram * p,BcResult ** l,BcNum ** ln,BcResult ** r,BcNum ** rn)555 bc_program_assignPrep(BcProgram* p, BcResult** l, BcNum** ln, BcResult** r,
556 BcNum** rn)
557 {
558 BcResultType lt, min;
559 bool good;
560
561 // This is the min non-allowable result type. dc allows strings.
562 min = BC_RESULT_TEMP - ((unsigned int) (BC_IS_BC));
563
564 // Prepare the operands.
565 bc_program_binPrep(p, l, ln, r, rn, 0);
566
567 lt = (*l)->t;
568
569 // Typecheck the left.
570 if (BC_ERR(lt >= min && lt <= BC_RESULT_ONE)) bc_err(BC_ERR_EXEC_TYPE);
571
572 // Strings can be assigned to variables. We are already good if we are
573 // assigning a string.
574 good = ((*r)->t == BC_RESULT_STR && lt <= BC_RESULT_ARRAY_ELEM);
575
576 assert(BC_PROG_STR(*rn) || (*r)->t != BC_RESULT_STR);
577
578 // If not, type check for a number.
579 if (!good) bc_program_type_num(*r, *rn);
580 }
581
582 /**
583 * Prepares a single operand and type checks it. This is separate from
584 * bc_program_operand() because different places want one or the other.
585 * @param p The program.
586 * @param r An out parameter; this is set to the pointer to the result that
587 * we care about.
588 * @param n An out parameter; this is set to the pointer to the number that
589 * we care about.
590 * @param idx The index of the result from the top of the results stack.
591 */
592 static void
bc_program_prep(BcProgram * p,BcResult ** r,BcNum ** n,size_t idx)593 bc_program_prep(BcProgram* p, BcResult** r, BcNum** n, size_t idx)
594 {
595 assert(p != NULL && r != NULL && n != NULL);
596
597 #ifndef BC_PROG_NO_STACK_CHECK
598 // Check the stack for dc.
599 if (BC_IS_DC)
600 {
601 if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 1)))
602 {
603 bc_err(BC_ERR_EXEC_STACK);
604 }
605 }
606 #endif // BC_PROG_NO_STACK_CHECK
607
608 assert(BC_PROG_STACK(&p->results, idx + 1));
609
610 bc_program_operand(p, r, n, idx);
611
612 // dc does not allow strings in this case.
613 bc_program_type_num(*r, *n);
614 }
615
616 /**
617 * Prepares and returns a clean result for the result of an operation.
618 * @param p The program.
619 * @return A clean result.
620 */
621 static BcResult*
bc_program_prepResult(BcProgram * p)622 bc_program_prepResult(BcProgram* p)
623 {
624 BcResult* res = bc_vec_pushEmpty(&p->results);
625
626 // Mark a result as not retired.
627 p->nresults += 1;
628
629 bc_result_clear(res);
630
631 return res;
632 }
633
634 /**
635 * Prepares a constant for use. This parses the constant into a number and then
636 * pushes that number onto the results stack.
637 * @param p The program.
638 * @param code The bytecode vector that we will pull the index of the constant
639 * from.
640 * @param bgn An in/out parameter; marks the start of the index in the
641 * bytecode vector and will be updated to point to after the index.
642 */
643 static void
bc_program_const(BcProgram * p,const char * code,size_t * bgn)644 bc_program_const(BcProgram* p, const char* code, size_t* bgn)
645 {
646 // I lied. I actually push the result first. I can do this because the
647 // result will be popped on error. I also get the constant itself.
648 BcResult* r = bc_program_prepResult(p);
649 BcConst* c = bc_vec_item(&p->consts, bc_program_index(code, bgn));
650 BcBigDig base = BC_PROG_IBASE(p);
651
652 assert(p->nresults == 1);
653
654 // Only reparse if the base changed.
655 if (c->base != base)
656 {
657 // Allocate if we haven't yet.
658 if (c->num.num == NULL)
659 {
660 // The plus 1 is in case of overflow with lack of clamping.
661 size_t len = strlen(c->val) + (BC_DIGIT_CLAMP == 0);
662
663 BC_SIG_LOCK;
664 bc_num_init(&c->num, BC_NUM_RDX(len));
665 BC_SIG_UNLOCK;
666 }
667 // We need to zero an already existing number.
668 else bc_num_zero(&c->num);
669
670 // bc_num_parse() should only do operations that cannot fail.
671 bc_num_parse(&c->num, c->val, base);
672
673 c->base = base;
674 }
675
676 BC_SIG_LOCK;
677
678 bc_num_createCopy(&r->d.n, &c->num);
679
680 BC_SIG_UNLOCK;
681
682 // XXX: Make sure to clear the number of results.
683 p->nresults -= 1;
684 }
685
686 /**
687 * Executes a binary operator operation.
688 * @param p The program.
689 * @param inst The instruction corresponding to the binary operator to execute.
690 */
691 static void
bc_program_op(BcProgram * p,uchar inst)692 bc_program_op(BcProgram* p, uchar inst)
693 {
694 BcResult* opd1;
695 BcResult* opd2;
696 BcResult* res;
697 BcNum* n1;
698 BcNum* n2;
699 size_t idx = inst - BC_INST_POWER;
700
701 res = bc_program_prepResult(p);
702
703 assert(p->nresults == 1);
704
705 bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 1);
706
707 BC_SIG_LOCK;
708
709 // Initialize the number with enough space, using the correct
710 // BcNumBinaryOpReq function. This looks weird because it is executing an
711 // item of an array. Rest assured that item is a function.
712 bc_num_init(&res->d.n, bc_program_opReqs[idx](n1, n2, BC_PROG_SCALE(p)));
713
714 BC_SIG_UNLOCK;
715
716 assert(BC_NUM_RDX_VALID(n1));
717 assert(BC_NUM_RDX_VALID(n2));
718
719 // Run the operation. This also executes an item of an array.
720 bc_program_ops[idx](n1, n2, &res->d.n, BC_PROG_SCALE(p));
721
722 bc_program_retire(p, 2);
723 }
724
725 /**
726 * Executes a read() or ? command.
727 * @param p The program.
728 */
729 static void
bc_program_read(BcProgram * p)730 bc_program_read(BcProgram* p)
731 {
732 BcStatus s;
733 BcInstPtr ip;
734 size_t i;
735 const char* file;
736 BcMode mode;
737 BcFunc* f = bc_vec_item(&p->fns, BC_PROG_READ);
738
739 // If we are already executing a read, that is an error. So look for a read
740 // and barf.
741 for (i = 0; i < p->stack.len; ++i)
742 {
743 BcInstPtr* ip_ptr = bc_vec_item(&p->stack, i);
744 if (ip_ptr->func == BC_PROG_READ) bc_err(BC_ERR_EXEC_REC_READ);
745 }
746
747 BC_SIG_LOCK;
748
749 // Save the filename because we are going to overwrite it.
750 file = vm->file;
751 mode = vm->mode;
752
753 // It is a parse error if there needs to be more than one line, so we unset
754 // this to tell the lexer to not request more. We set it back later.
755 vm->mode = BC_MODE_FILE;
756
757 if (!BC_PARSE_IS_INITED(&vm->read_prs, p))
758 {
759 // We need to parse, but we don't want to use the existing parser
760 // because it has state it needs to keep. (It could have a partial parse
761 // state.) So we create a new parser. This parser is in the BcVm struct
762 // so that it is not local, which means that a longjmp() could change
763 // it.
764 bc_parse_init(&vm->read_prs, p, BC_PROG_READ);
765
766 // We need a separate input buffer; that's why it is also in the BcVm
767 // struct.
768 bc_vec_init(&vm->read_buf, sizeof(char), BC_DTOR_NONE);
769 }
770 else
771 {
772 // This needs to be updated because the parser could have been used
773 // somewhere else.
774 bc_parse_updateFunc(&vm->read_prs, BC_PROG_READ);
775
776 // The read buffer also needs to be emptied or else it will still
777 // contain previous read expressions.
778 bc_vec_empty(&vm->read_buf);
779 }
780
781 BC_SETJMP_LOCKED(vm, exec_err);
782
783 BC_SIG_UNLOCK;
784
785 // Set up the lexer and the read function.
786 bc_lex_file(&vm->read_prs.l, bc_program_stdin_name);
787 bc_vec_popAll(&f->code);
788
789 // Read a line.
790 if (!BC_R) s = bc_read_line(&vm->read_buf, "");
791 else s = bc_read_line(&vm->read_buf, BC_VM_READ_PROMPT);
792
793 // We should *not* have run into EOF.
794 if (s == BC_STATUS_EOF) bc_err(BC_ERR_EXEC_READ_EXPR);
795
796 // Parse *one* expression, so mode should not be stdin.
797 bc_parse_text(&vm->read_prs, vm->read_buf.v, BC_MODE_FILE);
798 BC_SIG_LOCK;
799 vm->expr(&vm->read_prs, BC_PARSE_NOREAD | BC_PARSE_NEEDVAL);
800 BC_SIG_UNLOCK;
801
802 // We *must* have a valid expression. A semicolon cannot end an expression,
803 // although EOF can.
804 if (BC_ERR(vm->read_prs.l.t != BC_LEX_NLINE &&
805 vm->read_prs.l.t != BC_LEX_EOF))
806 {
807 bc_err(BC_ERR_EXEC_READ_EXPR);
808 }
809
810 #if BC_ENABLED
811 // Push on the globals stack if necessary.
812 if (BC_G) bc_program_prepGlobals(p);
813 #endif // BC_ENABLED
814
815 // Set up a new BcInstPtr.
816 ip.func = BC_PROG_READ;
817 ip.idx = 0;
818 ip.len = p->results.len;
819
820 // Update this pointer, just in case.
821 f = bc_vec_item(&p->fns, BC_PROG_READ);
822
823 // We want a return instruction to simplify things.
824 bc_vec_pushByte(&f->code, vm->read_ret);
825
826 // This lock is here to make sure dc's tail calls are the same length.
827 BC_SIG_LOCK;
828 bc_vec_push(&p->stack, &ip);
829
830 #if DC_ENABLED
831 // We need a new tail call entry for dc.
832 if (BC_IS_DC)
833 {
834 size_t temp = 0;
835 bc_vec_push(&p->tail_calls, &temp);
836 }
837 #endif // DC_ENABLED
838
839 exec_err:
840 BC_SIG_MAYLOCK;
841 vm->mode = (uchar) mode;
842 vm->file = file;
843 BC_LONGJMP_CONT(vm);
844 }
845
846 #if BC_ENABLE_EXTRA_MATH
847
848 /**
849 * Execute a rand().
850 * @param p The program.
851 */
852 static void
bc_program_rand(BcProgram * p)853 bc_program_rand(BcProgram* p)
854 {
855 BcRand rand = bc_rand_int(&p->rng);
856
857 bc_program_pushBigdig(p, (BcBigDig) rand, BC_RESULT_TEMP);
858
859 #if BC_DEBUG
860 // This is just to ensure that the generated number is correct. I also use
861 // braces because I declare every local at the top of the scope.
862 {
863 BcResult* r = bc_vec_top(&p->results);
864 assert(BC_NUM_RDX_VALID_NP(r->d.n));
865 }
866 #endif // BC_DEBUG
867 }
868 #endif // BC_ENABLE_EXTRA_MATH
869
870 /**
871 * Prints a series of characters, without escapes.
872 * @param str The string (series of characters).
873 */
874 static void
bc_program_printChars(const char * str)875 bc_program_printChars(const char* str)
876 {
877 const char* nl;
878 size_t len = vm->nchars + strlen(str);
879 sig_atomic_t lock;
880
881 BC_SIG_TRYLOCK(lock);
882
883 bc_file_puts(&vm->fout, bc_flush_save, str);
884
885 // We need to update the number of characters, so we find the last newline
886 // and set the characters accordingly.
887 nl = strrchr(str, '\n');
888
889 if (nl != NULL) len = strlen(nl + 1);
890
891 vm->nchars = len > UINT16_MAX ? UINT16_MAX : (uint16_t) len;
892
893 BC_SIG_TRYUNLOCK(lock);
894 }
895
896 /**
897 * Prints a string with escapes.
898 * @param str The string.
899 */
900 static void
bc_program_printString(const char * restrict str)901 bc_program_printString(const char* restrict str)
902 {
903 size_t i, len = strlen(str);
904
905 #if DC_ENABLED
906 // This is to ensure a nul byte is printed for dc's stream operation.
907 if (!len && BC_IS_DC)
908 {
909 bc_vm_putchar('\0', bc_flush_save);
910 return;
911 }
912 #endif // DC_ENABLED
913
914 // Loop over the characters, processing escapes and printing the rest.
915 for (i = 0; i < len; ++i)
916 {
917 int c = str[i];
918
919 // If we have an escape...
920 if (c == '\\' && i != len - 1)
921 {
922 const char* ptr;
923
924 // Get the escape character and its companion.
925 c = str[++i];
926 ptr = strchr(bc_program_esc_chars, c);
927
928 // If we have a companion character...
929 if (ptr != NULL)
930 {
931 // We need to specially handle a newline.
932 if (c == 'n')
933 {
934 BC_SIG_LOCK;
935 vm->nchars = UINT16_MAX;
936 BC_SIG_UNLOCK;
937 }
938
939 // Grab the actual character.
940 c = bc_program_esc_seqs[(size_t) (ptr - bc_program_esc_chars)];
941 }
942 else
943 {
944 // Just print the backslash if there is no companion character.
945 // The following character will be printed later after the outer
946 // if statement.
947 bc_vm_putchar('\\', bc_flush_save);
948 }
949 }
950
951 bc_vm_putchar(c, bc_flush_save);
952 }
953 }
954
955 /**
956 * Executes a print. This function handles all printing except streaming.
957 * @param p The program.
958 * @param inst The instruction for the type of print we are doing.
959 * @param idx The index of the result that we are printing.
960 */
961 static void
bc_program_print(BcProgram * p,uchar inst,size_t idx)962 bc_program_print(BcProgram* p, uchar inst, size_t idx)
963 {
964 BcResult* r;
965 char* str;
966 BcNum* n;
967 bool pop = (inst != BC_INST_PRINT);
968
969 assert(p != NULL);
970
971 #ifndef BC_PROG_NO_STACK_CHECK
972 if (BC_IS_DC)
973 {
974 if (BC_ERR(!BC_PROG_STACK(&p->results, idx + 1)))
975 {
976 bc_err(BC_ERR_EXEC_STACK);
977 }
978 }
979 #endif // BC_PROG_NO_STACK_CHECK
980
981 assert(BC_PROG_STACK(&p->results, idx + 1));
982
983 r = bc_vec_item_rev(&p->results, idx);
984
985 #if BC_ENABLED
986 // If we have a void value, that's not necessarily an error. It is if pop is
987 // true because that means that we are executing a print statement, but
988 // attempting to do a print on a lone void value is allowed because that's
989 // exactly how we want void values used.
990 if (r->t == BC_RESULT_VOID)
991 {
992 if (BC_ERR(pop)) bc_err(BC_ERR_EXEC_VOID_VAL);
993 bc_vec_pop(&p->results);
994 return;
995 }
996 #endif // BC_ENABLED
997
998 n = bc_program_num(p, r);
999
1000 // If we have a number...
1001 if (BC_PROG_NUM(r, n))
1002 {
1003 #if BC_ENABLED
1004 assert(inst != BC_INST_PRINT_STR);
1005 #endif // BC_ENABLED
1006
1007 // Print the number.
1008 bc_num_print(n, BC_PROG_OBASE(p), !pop);
1009
1010 #if BC_ENABLED
1011 // Need to store the number in last.
1012 if (BC_IS_BC) bc_num_copy(&p->last, n);
1013 #endif // BC_ENABLED
1014 }
1015 else
1016 {
1017 // We want to flush any stuff in the stdout buffer first.
1018 bc_file_flush(&vm->fout, bc_flush_save);
1019 str = bc_program_string(p, n);
1020
1021 #if BC_ENABLED
1022 if (inst == BC_INST_PRINT_STR) bc_program_printChars(str);
1023 else
1024 #endif // BC_ENABLED
1025 {
1026 bc_program_printString(str);
1027
1028 // Need to print a newline only in this case.
1029 if (inst == BC_INST_PRINT) bc_vm_putchar('\n', bc_flush_err);
1030 }
1031 }
1032
1033 // bc always pops. This macro makes sure that happens.
1034 if (BC_PROGRAM_POP(pop)) bc_vec_pop(&p->results);
1035 }
1036
1037 void
bc_program_negate(BcResult * r,BcNum * n)1038 bc_program_negate(BcResult* r, BcNum* n)
1039 {
1040 bc_num_copy(&r->d.n, n);
1041 if (BC_NUM_NONZERO(&r->d.n)) BC_NUM_NEG_TGL_NP(r->d.n);
1042 }
1043
1044 void
bc_program_not(BcResult * r,BcNum * n)1045 bc_program_not(BcResult* r, BcNum* n)
1046 {
1047 if (!bc_num_cmpZero(n)) bc_num_one(&r->d.n);
1048 }
1049
1050 #if BC_ENABLE_EXTRA_MATH
1051 void
bc_program_trunc(BcResult * r,BcNum * n)1052 bc_program_trunc(BcResult* r, BcNum* n)
1053 {
1054 bc_num_copy(&r->d.n, n);
1055 bc_num_truncate(&r->d.n, n->scale);
1056 }
1057 #endif // BC_ENABLE_EXTRA_MATH
1058
1059 /**
1060 * Runs a unary operation.
1061 * @param p The program.
1062 * @param inst The unary operation.
1063 */
1064 static void
bc_program_unary(BcProgram * p,uchar inst)1065 bc_program_unary(BcProgram* p, uchar inst)
1066 {
1067 BcResult* res;
1068 BcResult* ptr;
1069 BcNum* num;
1070
1071 res = bc_program_prepResult(p);
1072
1073 assert(p->nresults == 1);
1074
1075 bc_program_prep(p, &ptr, &num, 1);
1076
1077 BC_SIG_LOCK;
1078
1079 bc_num_init(&res->d.n, num->len);
1080
1081 BC_SIG_UNLOCK;
1082
1083 // This calls a function that is in an array.
1084 bc_program_unarys[inst - BC_INST_NEG](res, num);
1085 bc_program_retire(p, 1);
1086 }
1087
1088 /**
1089 * Executes a logical operator.
1090 * @param p The program.
1091 * @param inst The operator.
1092 */
1093 static void
bc_program_logical(BcProgram * p,uchar inst)1094 bc_program_logical(BcProgram* p, uchar inst)
1095 {
1096 BcResult* opd1;
1097 BcResult* opd2;
1098 BcResult* res;
1099 BcNum* n1;
1100 BcNum* n2;
1101 bool cond = 0;
1102 ssize_t cmp;
1103
1104 res = bc_program_prepResult(p);
1105
1106 assert(p->nresults == 1);
1107
1108 // All logical operators (except boolean not, which is taken care of by
1109 // bc_program_unary()), are binary operators.
1110 bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 1);
1111
1112 // Boolean and and or are not short circuiting. This is why; they can be
1113 // implemented much easier this way.
1114 if (inst == BC_INST_BOOL_AND)
1115 {
1116 cond = (bc_num_cmpZero(n1) && bc_num_cmpZero(n2));
1117 }
1118 else if (inst == BC_INST_BOOL_OR)
1119 {
1120 cond = (bc_num_cmpZero(n1) || bc_num_cmpZero(n2));
1121 }
1122 else
1123 {
1124 // We have a relational operator, so do a comparison.
1125 cmp = bc_num_cmp(n1, n2);
1126
1127 switch (inst)
1128 {
1129 case BC_INST_REL_EQ:
1130 {
1131 cond = (cmp == 0);
1132 break;
1133 }
1134
1135 case BC_INST_REL_LE:
1136 {
1137 cond = (cmp <= 0);
1138 break;
1139 }
1140
1141 case BC_INST_REL_GE:
1142 {
1143 cond = (cmp >= 0);
1144 break;
1145 }
1146
1147 case BC_INST_REL_NE:
1148 {
1149 cond = (cmp != 0);
1150 break;
1151 }
1152
1153 case BC_INST_REL_LT:
1154 {
1155 cond = (cmp < 0);
1156 break;
1157 }
1158
1159 case BC_INST_REL_GT:
1160 {
1161 cond = (cmp > 0);
1162 break;
1163 }
1164 #if BC_DEBUG
1165 default:
1166 {
1167 // There is a bug if we get here.
1168 abort();
1169 }
1170 #endif // BC_DEBUG
1171 }
1172 }
1173
1174 BC_SIG_LOCK;
1175
1176 bc_num_init(&res->d.n, BC_NUM_DEF_SIZE);
1177
1178 BC_SIG_UNLOCK;
1179
1180 if (cond) bc_num_one(&res->d.n);
1181
1182 bc_program_retire(p, 2);
1183 }
1184
1185 /**
1186 * Assigns a string to a variable.
1187 * @param p The program.
1188 * @param num The location of the string as a BcNum.
1189 * @param v The stack for the variable.
1190 * @param push Whether to push the string or not. To push means to move the
1191 * string from the results stack and push it onto the variable
1192 * stack.
1193 */
1194 static void
bc_program_assignStr(BcProgram * p,BcNum * num,BcVec * v,bool push)1195 bc_program_assignStr(BcProgram* p, BcNum* num, BcVec* v, bool push)
1196 {
1197 BcNum* n;
1198
1199 assert(BC_PROG_STACK(&p->results, 1 + !push));
1200 assert(num != NULL && num->num == NULL && num->cap == 0);
1201
1202 // If we are not pushing onto the variable stack, we need to replace the
1203 // top of the variable stack.
1204 if (!push) bc_vec_pop(v);
1205
1206 bc_vec_npop(&p->results, 1 + !push);
1207
1208 n = bc_vec_pushEmpty(v);
1209
1210 // We can just copy because the num should not have allocated anything.
1211 // NOLINTNEXTLINE
1212 memcpy(n, num, sizeof(BcNum));
1213 }
1214
1215 /**
1216 * Copies a value to a variable. This is used for storing in dc as well as to
1217 * set function parameters to arguments in bc.
1218 * @param p The program.
1219 * @param idx The index of the variable or array to copy to.
1220 * @param t The type to copy to. This could be a variable or an array.
1221 */
1222 static void
bc_program_copyToVar(BcProgram * p,size_t idx,BcType t)1223 bc_program_copyToVar(BcProgram* p, size_t idx, BcType t)
1224 {
1225 BcResult *ptr = NULL, r;
1226 BcVec* vec;
1227 BcNum* n = NULL;
1228 bool var = (t == BC_TYPE_VAR);
1229
1230 #if DC_ENABLED
1231 // Check the stack for dc.
1232 if (BC_IS_DC)
1233 {
1234 if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);
1235 }
1236 #endif
1237
1238 assert(BC_PROG_STACK(&p->results, 1));
1239
1240 bc_program_operand(p, &ptr, &n, 0);
1241
1242 #if BC_ENABLED
1243 // Get the variable for a bc function call.
1244 if (BC_IS_BC)
1245 {
1246 // Type match the result.
1247 bc_program_type_match(ptr, t);
1248 }
1249 #endif // BC_ENABLED
1250
1251 vec = bc_program_vec(p, idx, t);
1252
1253 // We can shortcut in dc if it's assigning a string by using
1254 // bc_program_assignStr().
1255 if (ptr->t == BC_RESULT_STR)
1256 {
1257 assert(BC_PROG_STR(n));
1258
1259 if (BC_ERR(!var)) bc_err(BC_ERR_EXEC_TYPE);
1260
1261 bc_program_assignStr(p, n, vec, true);
1262
1263 return;
1264 }
1265
1266 BC_SIG_LOCK;
1267
1268 // Just create and copy for a normal variable.
1269 if (var)
1270 {
1271 if (BC_PROG_STR(n))
1272 {
1273 // NOLINTNEXTLINE
1274 memcpy(&r.d.n, n, sizeof(BcNum));
1275 }
1276 else bc_num_createCopy(&r.d.n, n);
1277 }
1278 else
1279 {
1280 // If we get here, we are handling an array. This is one place we need
1281 // to cast the number from bc_program_num() to a vector.
1282 BcVec* v = (BcVec*) n;
1283 BcVec* rv = &r.d.v;
1284
1285 #if BC_ENABLED
1286
1287 if (BC_IS_BC)
1288 {
1289 bool ref, ref_size;
1290
1291 // True if we are using a reference.
1292 ref = (v->size == sizeof(BcNum) && t == BC_TYPE_REF);
1293
1294 // True if we already have a reference vector. This is slightly
1295 // (okay, a lot; it just doesn't look that way) different from
1296 // above. The above means that we need to construct a reference
1297 // vector, whereas this means that we have one and we might have to
1298 // *dereference* it.
1299 ref_size = (v->size == sizeof(uchar));
1300
1301 // If we *should* have a reference.
1302 if (ref || (ref_size && t == BC_TYPE_REF))
1303 {
1304 // Create a new reference vector.
1305 bc_vec_init(rv, sizeof(uchar), BC_DTOR_NONE);
1306
1307 // If this is true, then we need to construct a reference.
1308 if (ref)
1309 {
1310 // Make sure the pointer was not invalidated.
1311 vec = bc_program_vec(p, idx, t);
1312
1313 // Push the indices onto the reference vector. This takes
1314 // care of last; it ensures the reference goes to the right
1315 // place.
1316 bc_vec_pushIndex(rv, ptr->d.loc.loc);
1317 bc_vec_pushIndex(rv, ptr->d.loc.stack_idx);
1318 }
1319 // If we get here, we are copying a ref to a ref. Just push a
1320 // copy of all of the bytes.
1321 else bc_vec_npush(rv, v->len * sizeof(uchar), v->v);
1322
1323 // Push the reference vector onto the array stack and pop the
1324 // source.
1325 bc_vec_push(vec, &r.d);
1326 bc_vec_pop(&p->results);
1327
1328 // We need to return early to avoid executing code that we must
1329 // not touch.
1330 BC_SIG_UNLOCK;
1331 return;
1332 }
1333 // If we get here, we have a reference, but we need an array, so
1334 // dereference the array.
1335 else if (ref_size && t != BC_TYPE_REF)
1336 {
1337 v = bc_program_dereference(p, v);
1338 }
1339 }
1340 #endif // BC_ENABLED
1341
1342 // If we get here, we need to copy the array because in bc, all
1343 // arguments are passed by value. Yes, this is expensive.
1344 bc_array_init(rv, true);
1345 bc_array_copy(rv, v);
1346 }
1347
1348 // Push the vector onto the array stack and pop the source.
1349 bc_vec_push(vec, &r.d);
1350 bc_vec_pop(&p->results);
1351
1352 BC_SIG_UNLOCK;
1353 }
1354
1355 void
bc_program_assignBuiltin(BcProgram * p,bool scale,bool obase,BcBigDig val)1356 bc_program_assignBuiltin(BcProgram* p, bool scale, bool obase, BcBigDig val)
1357 {
1358 BcBigDig* ptr_t;
1359 BcBigDig max, min;
1360 #if BC_ENABLED
1361 BcVec* v;
1362 BcBigDig* ptr;
1363 #endif // BC_ENABLED
1364
1365 assert(!scale || !obase);
1366
1367 // Scale needs handling separate from ibase and obase.
1368 if (scale)
1369 {
1370 // Set the min and max.
1371 min = 0;
1372 max = vm->maxes[BC_PROG_GLOBALS_SCALE];
1373
1374 #if BC_ENABLED
1375 // Get a pointer to the stack.
1376 v = p->globals_v + BC_PROG_GLOBALS_SCALE;
1377 #endif // BC_ENABLED
1378
1379 // Get a pointer to the current value.
1380 ptr_t = p->globals + BC_PROG_GLOBALS_SCALE;
1381 }
1382 else
1383 {
1384 // Set the min and max.
1385 min = BC_NUM_MIN_BASE;
1386 if (BC_ENABLE_EXTRA_MATH && obase && (BC_IS_DC || !BC_IS_POSIX))
1387 {
1388 min = 0;
1389 }
1390 max = vm->maxes[obase + BC_PROG_GLOBALS_IBASE];
1391
1392 #if BC_ENABLED
1393 // Get a pointer to the stack.
1394 v = p->globals_v + BC_PROG_GLOBALS_IBASE + obase;
1395 #endif // BC_ENABLED
1396
1397 // Get a pointer to the current value.
1398 ptr_t = p->globals + BC_PROG_GLOBALS_IBASE + obase;
1399 }
1400
1401 // Check for error.
1402 if (BC_ERR(val > max || val < min))
1403 {
1404 BcErr e;
1405
1406 // This grabs the right error.
1407 if (scale) e = BC_ERR_EXEC_SCALE;
1408 else if (obase) e = BC_ERR_EXEC_OBASE;
1409 else e = BC_ERR_EXEC_IBASE;
1410
1411 bc_verr(e, min, max);
1412 }
1413
1414 #if BC_ENABLED
1415 // Set the top of the stack.
1416 ptr = bc_vec_top(v);
1417 *ptr = val;
1418 #endif // BC_ENABLED
1419
1420 // Set the actual global variable.
1421 *ptr_t = val;
1422 }
1423
1424 #if BC_ENABLE_EXTRA_MATH
1425 void
bc_program_assignSeed(BcProgram * p,BcNum * val)1426 bc_program_assignSeed(BcProgram* p, BcNum* val)
1427 {
1428 bc_num_rng(val, &p->rng);
1429 }
1430 #endif // BC_ENABLE_EXTRA_MATH
1431
1432 /**
1433 * Executes an assignment operator.
1434 * @param p The program.
1435 * @param inst The assignment operator to execute.
1436 */
1437 static void
bc_program_assign(BcProgram * p,uchar inst)1438 bc_program_assign(BcProgram* p, uchar inst)
1439 {
1440 // The local use_val is true when the assigned value needs to be copied.
1441 BcResult* left;
1442 BcResult* right;
1443 BcResult res;
1444 BcNum* l;
1445 BcNum* r;
1446 bool ob, sc, use_val = BC_INST_USE_VAL(inst);
1447
1448 bc_program_assignPrep(p, &left, &l, &right, &r);
1449
1450 // Assigning to a string should be impossible simply because of the parse.
1451 assert(left->t != BC_RESULT_STR);
1452
1453 // If we are assigning a string...
1454 if (right->t == BC_RESULT_STR)
1455 {
1456 assert(BC_PROG_STR(r));
1457
1458 #if BC_ENABLED
1459 if (inst != BC_INST_ASSIGN && inst != BC_INST_ASSIGN_NO_VAL)
1460 {
1461 bc_err(BC_ERR_EXEC_TYPE);
1462 }
1463 #endif // BC_ENABLED
1464
1465 // If we are assigning to an array element...
1466 if (left->t == BC_RESULT_ARRAY_ELEM)
1467 {
1468 BC_SIG_LOCK;
1469
1470 // We need to free the number and clear it.
1471 bc_num_free(l);
1472
1473 // NOLINTNEXTLINE
1474 memcpy(l, r, sizeof(BcNum));
1475
1476 // Now we can pop the results.
1477 bc_vec_npop(&p->results, 2);
1478
1479 BC_SIG_UNLOCK;
1480 }
1481 else
1482 {
1483 // If we get here, we are assigning to a variable, which we can use
1484 // bc_program_assignStr() for.
1485 BcVec* v = bc_program_vec(p, left->d.loc.loc, BC_TYPE_VAR);
1486 bc_program_assignStr(p, r, v, false);
1487 }
1488
1489 #if BC_ENABLED
1490
1491 // If this is true, the value is going to be used again, so we want to
1492 // push a temporary with the string.
1493 if (inst == BC_INST_ASSIGN)
1494 {
1495 res.t = BC_RESULT_STR;
1496 // NOLINTNEXTLINE
1497 memcpy(&res.d.n, r, sizeof(BcNum));
1498 bc_vec_push(&p->results, &res);
1499 }
1500
1501 #endif // BC_ENABLED
1502
1503 // By using bc_program_assignStr(), we short-circuited this, so return.
1504 return;
1505 }
1506
1507 // If we have a normal assignment operator, not a math one...
1508 if (BC_INST_IS_ASSIGN(inst))
1509 {
1510 // Assigning to a variable that has a string here is fine because there
1511 // is no math done on it.
1512
1513 // BC_RESULT_TEMP, BC_RESULT_IBASE, BC_RESULT_OBASE, BC_RESULT_SCALE,
1514 // and BC_RESULT_SEED all have temporary copies. Because that's the
1515 // case, we can free the left and just move the value over. We set the
1516 // type of right to BC_RESULT_ZERO in order to prevent it from being
1517 // freed. We also don't have to worry about BC_RESULT_STR because it's
1518 // take care of above.
1519 if (right->t == BC_RESULT_TEMP || right->t >= BC_RESULT_IBASE)
1520 {
1521 BC_SIG_LOCK;
1522
1523 bc_num_free(l);
1524 // NOLINTNEXTLINE
1525 memcpy(l, r, sizeof(BcNum));
1526 right->t = BC_RESULT_ZERO;
1527
1528 BC_SIG_UNLOCK;
1529 }
1530 // Copy over.
1531 else bc_num_copy(l, r);
1532 }
1533 #if BC_ENABLED
1534 else
1535 {
1536 // If we get here, we are doing a math assignment (+=, -=, etc.). So
1537 // we need to prepare for a binary operator.
1538 BcBigDig scale = BC_PROG_SCALE(p);
1539
1540 // At this point, the left side could still be a string because it could
1541 // be a variable that has the string. If that's the case, we have a type
1542 // error.
1543 if (BC_PROG_STR(l)) bc_err(BC_ERR_EXEC_TYPE);
1544
1545 // Get the right type of assignment operator, whether val is used or
1546 // NO_VAL for performance.
1547 if (!use_val)
1548 {
1549 inst -= (BC_INST_ASSIGN_POWER_NO_VAL - BC_INST_ASSIGN_POWER);
1550 }
1551
1552 assert(BC_NUM_RDX_VALID(l));
1553 assert(BC_NUM_RDX_VALID(r));
1554
1555 // Run the actual operation. We do not need worry about reallocating l
1556 // because bc_num_binary() does that behind the scenes for us.
1557 bc_program_ops[inst - BC_INST_ASSIGN_POWER](l, r, l, scale);
1558 }
1559 #endif // BC_ENABLED
1560
1561 ob = (left->t == BC_RESULT_OBASE);
1562 sc = (left->t == BC_RESULT_SCALE);
1563
1564 // The globals need special handling, especially the non-seed ones. The
1565 // first part of the if statement handles them.
1566 if (ob || sc || left->t == BC_RESULT_IBASE)
1567 {
1568 // Get the actual value.
1569 BcBigDig val = bc_num_bigdig(l);
1570
1571 bc_program_assignBuiltin(p, sc, ob, val);
1572 }
1573 #if BC_ENABLE_EXTRA_MATH
1574 // To assign to steed, let bc_num_rng() do its magic.
1575 else if (left->t == BC_RESULT_SEED) bc_program_assignSeed(p, l);
1576 #endif // BC_ENABLE_EXTRA_MATH
1577
1578 BC_SIG_LOCK;
1579
1580 // If we needed to use the value, then we need to copy it. Otherwise, we can
1581 // pop indiscriminately. Oh, and the copy should be a BC_RESULT_TEMP.
1582 if (use_val)
1583 {
1584 bc_num_createCopy(&res.d.n, l);
1585 res.t = BC_RESULT_TEMP;
1586 bc_vec_npop(&p->results, 2);
1587 bc_vec_push(&p->results, &res);
1588 }
1589 else bc_vec_npop(&p->results, 2);
1590
1591 BC_SIG_UNLOCK;
1592 }
1593
1594 /**
1595 * Pushes a variable's value onto the results stack.
1596 * @param p The program.
1597 * @param code The bytecode vector to pull the variable's index out of.
1598 * @param bgn An in/out parameter; the start of the index in the bytecode
1599 * vector, and will be updated to point after the index on return.
1600 * @param pop True if the variable's value should be popped off its stack.
1601 * This is only used in dc.
1602 * @param copy True if the variable's value should be copied to the results
1603 * stack. This is only used in dc.
1604 */
1605 static void
bc_program_pushVar(BcProgram * p,const char * restrict code,size_t * restrict bgn,bool pop,bool copy)1606 bc_program_pushVar(BcProgram* p, const char* restrict code,
1607 size_t* restrict bgn, bool pop, bool copy)
1608 {
1609 BcResult r;
1610 size_t idx = bc_program_index(code, bgn);
1611 BcVec* v;
1612
1613 // Set the result appropriately.
1614 r.t = BC_RESULT_VAR;
1615 r.d.loc.loc = idx;
1616
1617 // Get the stack for the variable. This is used in both bc and dc.
1618 v = bc_program_vec(p, idx, BC_TYPE_VAR);
1619 r.d.loc.stack_idx = v->len - 1;
1620
1621 #if DC_ENABLED
1622 // If this condition is true, then we have the hard case, where we have to
1623 // adjust dc registers.
1624 if (BC_IS_DC && (pop || copy))
1625 {
1626 // Get the number at the top at the top of the stack.
1627 BcNum* num = bc_vec_top(v);
1628
1629 // Ensure there are enough elements on the stack.
1630 if (BC_ERR(!BC_PROG_STACK(v, 2 - copy)))
1631 {
1632 const char* name = bc_map_name(&p->var_map, idx);
1633 bc_verr(BC_ERR_EXEC_STACK_REGISTER, name);
1634 }
1635
1636 assert(BC_PROG_STACK(v, 2 - copy));
1637
1638 // If the top of the stack is actually a number...
1639 if (!BC_PROG_STR(num))
1640 {
1641 BC_SIG_LOCK;
1642
1643 // Create a copy to go onto the results stack as appropriate.
1644 r.t = BC_RESULT_TEMP;
1645 bc_num_createCopy(&r.d.n, num);
1646
1647 // If we are not actually copying, we need to do a replace, so pop.
1648 if (!copy) bc_vec_pop(v);
1649
1650 bc_vec_push(&p->results, &r);
1651
1652 BC_SIG_UNLOCK;
1653
1654 return;
1655 }
1656 else
1657 {
1658 // Set the string result. We can just memcpy because all of the
1659 // fields in the num should be cleared.
1660 // NOLINTNEXTLINE
1661 memcpy(&r.d.n, num, sizeof(BcNum));
1662 r.t = BC_RESULT_STR;
1663 }
1664
1665 // If we are not actually copying, we need to do a replace, so pop.
1666 if (!copy) bc_vec_pop(v);
1667 }
1668 #endif // DC_ENABLED
1669
1670 bc_vec_push(&p->results, &r);
1671 }
1672
1673 /**
1674 * Pushes an array or an array element onto the results stack.
1675 * @param p The program.
1676 * @param code The bytecode vector to pull the variable's index out of.
1677 * @param bgn An in/out parameter; the start of the index in the bytecode
1678 * vector, and will be updated to point after the index on return.
1679 * @param inst The instruction; whether to push an array or an array element.
1680 */
1681 static void
bc_program_pushArray(BcProgram * p,const char * restrict code,size_t * restrict bgn,uchar inst)1682 bc_program_pushArray(BcProgram* p, const char* restrict code,
1683 size_t* restrict bgn, uchar inst)
1684 {
1685 BcResult r;
1686 BcResult* operand;
1687 BcNum* num;
1688 BcBigDig temp;
1689 BcVec* v;
1690
1691 // Get the index of the array.
1692 r.d.loc.loc = bc_program_index(code, bgn);
1693
1694 // We need the array to get its length.
1695 v = bc_program_vec(p, r.d.loc.loc, BC_TYPE_ARRAY);
1696 assert(v != NULL);
1697
1698 r.d.loc.stack_idx = v->len - 1;
1699
1700 // Doing an array is easy; just set the result type and finish.
1701 if (inst == BC_INST_ARRAY)
1702 {
1703 r.t = BC_RESULT_ARRAY;
1704 bc_vec_push(&p->results, &r);
1705 return;
1706 }
1707
1708 // Grab the top element of the results stack for the array index.
1709 bc_program_prep(p, &operand, &num, 0);
1710 temp = bc_num_bigdig(num);
1711
1712 // Set the result.
1713 r.t = BC_RESULT_ARRAY_ELEM;
1714 r.d.loc.idx = (size_t) temp;
1715
1716 BC_SIG_LOCK;
1717
1718 // Pop the index and push the element.
1719 bc_vec_pop(&p->results);
1720 bc_vec_push(&p->results, &r);
1721
1722 BC_SIG_UNLOCK;
1723 }
1724
1725 #if BC_ENABLED
1726
1727 /**
1728 * Executes an increment or decrement operator. This only handles postfix
1729 * inc/dec because the parser translates prefix inc/dec into an assignment where
1730 * the value is used.
1731 * @param p The program.
1732 * @param inst The instruction; whether to do an increment or decrement.
1733 */
1734 static void
bc_program_incdec(BcProgram * p,uchar inst)1735 bc_program_incdec(BcProgram* p, uchar inst)
1736 {
1737 BcResult *ptr, res, copy;
1738 BcNum* num;
1739 uchar inst2;
1740
1741 bc_program_prep(p, &ptr, &num, 0);
1742
1743 BC_SIG_LOCK;
1744
1745 // We need a copy from *before* the operation.
1746 copy.t = BC_RESULT_TEMP;
1747 bc_num_createCopy(©.d.n, num);
1748
1749 BC_SETJMP_LOCKED(vm, exit);
1750
1751 BC_SIG_UNLOCK;
1752
1753 // Create the proper assignment.
1754 res.t = BC_RESULT_ONE;
1755 inst2 = BC_INST_ASSIGN_PLUS_NO_VAL + (inst & 0x01);
1756
1757 bc_vec_push(&p->results, &res);
1758 bc_program_assign(p, inst2);
1759
1760 BC_SIG_LOCK;
1761
1762 bc_vec_push(&p->results, ©);
1763
1764 BC_UNSETJMP(vm);
1765
1766 BC_SIG_UNLOCK;
1767
1768 // No need to free the copy here because we pushed it onto the stack.
1769 return;
1770
1771 exit:
1772 BC_SIG_MAYLOCK;
1773 bc_num_free(©.d.n);
1774 BC_LONGJMP_CONT(vm);
1775 }
1776
1777 /**
1778 * Executes a function call for bc.
1779 * @param p The program.
1780 * @param code The bytecode vector to pull the number of arguments and the
1781 * function index out of.
1782 * @param bgn An in/out parameter; the start of the indices in the bytecode
1783 * vector, and will be updated to point after the indices on
1784 * return.
1785 */
1786 static void
bc_program_call(BcProgram * p,const char * restrict code,size_t * restrict bgn)1787 bc_program_call(BcProgram* p, const char* restrict code, size_t* restrict bgn)
1788 {
1789 BcInstPtr ip;
1790 size_t i, nargs;
1791 BcFunc* f;
1792 BcVec* v;
1793 BcAuto* a;
1794 BcResult* arg;
1795
1796 // Pull the number of arguments out of the bytecode vector.
1797 nargs = bc_program_index(code, bgn);
1798
1799 // Set up instruction pointer.
1800 ip.idx = 0;
1801 ip.func = bc_program_index(code, bgn);
1802 f = bc_vec_item(&p->fns, ip.func);
1803
1804 // Error checking.
1805 if (BC_ERR(!f->code.len)) bc_verr(BC_ERR_EXEC_UNDEF_FUNC, f->name);
1806 if (BC_ERR(nargs != f->nparams))
1807 {
1808 bc_verr(BC_ERR_EXEC_PARAMS, f->nparams, nargs);
1809 }
1810
1811 // Set the length of the results stack. We discount the argument, of course.
1812 ip.len = p->results.len - nargs;
1813
1814 assert(BC_PROG_STACK(&p->results, nargs));
1815
1816 // Prepare the globals' stacks.
1817 if (BC_G) bc_program_prepGlobals(p);
1818
1819 // Push the arguments onto the stacks of their respective parameters.
1820 for (i = 0; i < nargs; ++i)
1821 {
1822 arg = bc_vec_top(&p->results);
1823 if (BC_ERR(arg->t == BC_RESULT_VOID)) bc_err(BC_ERR_EXEC_VOID_VAL);
1824
1825 // Get the corresponding parameter.
1826 a = bc_vec_item(&f->autos, nargs - 1 - i);
1827
1828 // Actually push the value onto the parameter's stack.
1829 bc_program_copyToVar(p, a->idx, a->type);
1830 }
1831
1832 BC_SIG_LOCK;
1833
1834 // Push zeroes onto the stacks of the auto variables.
1835 for (; i < f->autos.len; ++i)
1836 {
1837 // Get the auto and its stack.
1838 a = bc_vec_item(&f->autos, i);
1839 v = bc_program_vec(p, a->idx, a->type);
1840
1841 // If a variable, just push a 0; otherwise, push an array.
1842 if (a->type == BC_TYPE_VAR)
1843 {
1844 BcNum* n = bc_vec_pushEmpty(v);
1845 bc_num_init(n, BC_NUM_DEF_SIZE);
1846 }
1847 else
1848 {
1849 BcVec* v2;
1850
1851 assert(a->type == BC_TYPE_ARRAY);
1852
1853 v2 = bc_vec_pushEmpty(v);
1854 bc_array_init(v2, true);
1855 }
1856 }
1857
1858 // Push the instruction pointer onto the execution stack.
1859 bc_vec_push(&p->stack, &ip);
1860
1861 BC_SIG_UNLOCK;
1862 }
1863
1864 /**
1865 * Executes a return instruction.
1866 * @param p The program.
1867 * @param inst The return instruction. bc can return void, and we need to know
1868 * if it is.
1869 */
1870 static void
bc_program_return(BcProgram * p,uchar inst)1871 bc_program_return(BcProgram* p, uchar inst)
1872 {
1873 BcResult* res;
1874 BcFunc* f;
1875 BcInstPtr* ip;
1876 size_t i, nresults;
1877
1878 // Get the instruction pointer.
1879 ip = bc_vec_top(&p->stack);
1880
1881 // Get the difference between the actual number of results and the number of
1882 // results the caller expects.
1883 nresults = p->results.len - ip->len;
1884
1885 // If this isn't true, there was a missing call somewhere.
1886 assert(BC_PROG_STACK(&p->stack, 2));
1887
1888 // If this isn't true, the parser screwed by giving us no value when we
1889 // expected one, or giving us a value when we expected none.
1890 assert(BC_PROG_STACK(&p->results, ip->len + (inst == BC_INST_RET)));
1891
1892 // Get the function we are returning from.
1893 f = bc_vec_item(&p->fns, ip->func);
1894
1895 res = bc_program_prepResult(p);
1896
1897 assert(p->nresults == 1);
1898
1899 // If we are returning normally...
1900 if (inst == BC_INST_RET)
1901 {
1902 BcNum* num;
1903 BcResult* operand;
1904
1905 // Prepare and copy the return value.
1906 bc_program_operand(p, &operand, &num, 1);
1907
1908 if (BC_PROG_STR(num))
1909 {
1910 // We need to set this because otherwise, it will be a
1911 // BC_RESULT_TEMP, and BC_RESULT_TEMP needs an actual number to make
1912 // it easier to do type checking.
1913 res->t = BC_RESULT_STR;
1914
1915 // NOLINTNEXTLINE
1916 memcpy(&res->d.n, num, sizeof(BcNum));
1917 }
1918 else
1919 {
1920 BC_SIG_LOCK;
1921
1922 bc_num_createCopy(&res->d.n, num);
1923 }
1924 }
1925 // Void is easy; set the result.
1926 else if (inst == BC_INST_RET_VOID) res->t = BC_RESULT_VOID;
1927 else
1928 {
1929 BC_SIG_LOCK;
1930
1931 // If we get here, the instruction is for returning a zero, so do that.
1932 bc_num_init(&res->d.n, BC_NUM_DEF_SIZE);
1933 }
1934
1935 BC_SIG_MAYUNLOCK;
1936
1937 // We need to pop items off of the stacks of arguments and autos as well.
1938 for (i = 0; i < f->autos.len; ++i)
1939 {
1940 BcAuto* a = bc_vec_item(&f->autos, i);
1941 BcVec* v = bc_program_vec(p, a->idx, a->type);
1942
1943 bc_vec_pop(v);
1944 }
1945
1946 BC_SIG_LOCK;
1947
1948 // When we retire, pop all of the unused results.
1949 bc_program_retire(p, nresults);
1950
1951 // Pop the globals, if necessary.
1952 if (BC_G) bc_program_popGlobals(p, false);
1953
1954 // Pop the stack. This is what causes the function to actually "return."
1955 bc_vec_pop(&p->stack);
1956
1957 BC_SIG_UNLOCK;
1958 }
1959 #endif // BC_ENABLED
1960
1961 /**
1962 * Executes a builtin function.
1963 * @param p The program.
1964 * @param inst The builtin to execute.
1965 */
1966 static void
bc_program_builtin(BcProgram * p,uchar inst)1967 bc_program_builtin(BcProgram* p, uchar inst)
1968 {
1969 BcResult* opd;
1970 BcResult* res;
1971 BcNum* num;
1972 bool len = (inst == BC_INST_LENGTH);
1973
1974 // Ensure we have a valid builtin.
1975 #if BC_ENABLE_EXTRA_MATH
1976 assert(inst >= BC_INST_LENGTH && inst <= BC_INST_IRAND);
1977 #else // BC_ENABLE_EXTRA_MATH
1978 assert(inst >= BC_INST_LENGTH && inst <= BC_INST_IS_STRING);
1979 #endif // BC_ENABLE_EXTRA_MATH
1980
1981 #ifndef BC_PROG_NO_STACK_CHECK
1982 // Check stack for dc.
1983 if (BC_IS_DC && BC_ERR(!BC_PROG_STACK(&p->results, 1)))
1984 {
1985 bc_err(BC_ERR_EXEC_STACK);
1986 }
1987 #endif // BC_PROG_NO_STACK_CHECK
1988
1989 assert(BC_PROG_STACK(&p->results, 1));
1990
1991 res = bc_program_prepResult(p);
1992
1993 assert(p->nresults == 1);
1994
1995 bc_program_operand(p, &opd, &num, 1);
1996
1997 assert(num != NULL);
1998
1999 // We need to ensure that strings and arrays aren't passed to most builtins.
2000 // The scale function can take strings in dc.
2001 if (!len && (inst != BC_INST_SCALE_FUNC || BC_IS_BC) &&
2002 inst != BC_INST_IS_NUMBER && inst != BC_INST_IS_STRING)
2003 {
2004 bc_program_type_num(opd, num);
2005 }
2006
2007 // Square root is easy.
2008 if (inst == BC_INST_SQRT) bc_num_sqrt(num, &res->d.n, BC_PROG_SCALE(p));
2009
2010 // Absolute value is easy.
2011 else if (inst == BC_INST_ABS)
2012 {
2013 BC_SIG_LOCK;
2014
2015 bc_num_createCopy(&res->d.n, num);
2016
2017 BC_SIG_UNLOCK;
2018
2019 BC_NUM_NEG_CLR_NP(res->d.n);
2020 }
2021
2022 // Testing for number or string is easy.
2023 else if (inst == BC_INST_IS_NUMBER || inst == BC_INST_IS_STRING)
2024 {
2025 bool cond;
2026 bool is_str;
2027
2028 BC_SIG_LOCK;
2029
2030 bc_num_init(&res->d.n, BC_NUM_DEF_SIZE);
2031
2032 BC_SIG_UNLOCK;
2033
2034 // Test if the number is a string.
2035 is_str = BC_PROG_STR(num);
2036
2037 // This confusing condition simply means that the instruction must be
2038 // true if is_str is, or it must be false if is_str is. Otherwise, the
2039 // returned value is false (0).
2040 cond = ((inst == BC_INST_IS_STRING) == is_str);
2041 if (cond) bc_num_one(&res->d.n);
2042 }
2043
2044 #if BC_ENABLE_EXTRA_MATH
2045
2046 // irand() is easy.
2047 else if (inst == BC_INST_IRAND)
2048 {
2049 BC_SIG_LOCK;
2050
2051 bc_num_init(&res->d.n, num->len - BC_NUM_RDX_VAL(num));
2052
2053 BC_SIG_UNLOCK;
2054
2055 bc_num_irand(num, &res->d.n, &p->rng);
2056 }
2057
2058 #endif // BC_ENABLE_EXTRA_MATH
2059
2060 // Everything else is...not easy.
2061 else
2062 {
2063 BcBigDig val = 0;
2064
2065 // Well, scale() is easy, but length() is not.
2066 if (len)
2067 {
2068 // If we are bc and we have an array...
2069 if (opd->t == BC_RESULT_ARRAY)
2070 {
2071 // Yes, this is one place where we need to cast the number from
2072 // bc_program_num() to a vector.
2073 BcVec* v = (BcVec*) num;
2074
2075 // XXX: If this is changed, you should also change the similar
2076 // code in bc_program_asciify().
2077
2078 #if BC_ENABLED
2079 // Dereference the array, if necessary.
2080 if (BC_IS_BC && v->size == sizeof(uchar))
2081 {
2082 v = bc_program_dereference(p, v);
2083 }
2084 #endif // BC_ENABLED
2085
2086 assert(v->size == sizeof(BcNum));
2087
2088 val = (BcBigDig) v->len;
2089 }
2090 else
2091 {
2092 // If the item is a string...
2093 if (!BC_PROG_NUM(opd, num))
2094 {
2095 char* str;
2096
2097 // Get the string, then get the length.
2098 str = bc_program_string(p, num);
2099 val = (BcBigDig) strlen(str);
2100 }
2101 else
2102 {
2103 // Calculate the length of the number.
2104 val = (BcBigDig) bc_num_len(num);
2105 }
2106 }
2107 }
2108 // Like I said; scale() is actually easy. It just also needs the integer
2109 // conversion that length() does.
2110 else if (BC_IS_BC || BC_PROG_NUM(opd, num))
2111 {
2112 val = (BcBigDig) bc_num_scale(num);
2113 }
2114
2115 BC_SIG_LOCK;
2116
2117 // Create the result.
2118 bc_num_createFromBigdig(&res->d.n, val);
2119
2120 BC_SIG_UNLOCK;
2121 }
2122
2123 bc_program_retire(p, 1);
2124 }
2125
2126 /**
2127 * Executes a divmod.
2128 * @param p The program.
2129 */
2130 static void
bc_program_divmod(BcProgram * p)2131 bc_program_divmod(BcProgram* p)
2132 {
2133 BcResult* opd1;
2134 BcResult* opd2;
2135 BcResult* res;
2136 BcResult* res2;
2137 BcNum* n1;
2138 BcNum* n2;
2139 size_t req;
2140
2141 // We grow first to avoid pointer invalidation.
2142 bc_vec_grow(&p->results, 2);
2143
2144 // We don't need to update the pointer because
2145 // the capacity is enough due to the line above.
2146 res2 = bc_program_prepResult(p);
2147 res = bc_program_prepResult(p);
2148 assert(p->nresults == 2);
2149
2150 // Prepare the operands.
2151 bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 2);
2152
2153 req = bc_num_mulReq(n1, n2, BC_PROG_SCALE(p));
2154
2155 BC_SIG_LOCK;
2156
2157 // Initialize the results.
2158 bc_num_init(&res->d.n, req);
2159 bc_num_init(&res2->d.n, req);
2160
2161 BC_SIG_UNLOCK;
2162
2163 // Execute.
2164 bc_num_divmod(n1, n2, &res2->d.n, &res->d.n, BC_PROG_SCALE(p));
2165
2166 bc_program_retire(p, 2);
2167 }
2168
2169 /**
2170 * Executes modular exponentiation.
2171 * @param p The program.
2172 */
2173 static void
bc_program_modexp(BcProgram * p)2174 bc_program_modexp(BcProgram* p)
2175 {
2176 BcResult* r1;
2177 BcResult* r2;
2178 BcResult* r3;
2179 BcResult* res;
2180 BcNum* n1;
2181 BcNum* n2;
2182 BcNum* n3;
2183
2184 #if DC_ENABLED
2185
2186 // Check the stack.
2187 if (BC_IS_DC && BC_ERR(!BC_PROG_STACK(&p->results, 3)))
2188 {
2189 bc_err(BC_ERR_EXEC_STACK);
2190 }
2191
2192 #endif // DC_ENABLED
2193
2194 assert(BC_PROG_STACK(&p->results, 3));
2195
2196 res = bc_program_prepResult(p);
2197
2198 assert(p->nresults == 1);
2199
2200 // Get the first operand and typecheck.
2201 bc_program_operand(p, &r1, &n1, 3);
2202 bc_program_type_num(r1, n1);
2203
2204 // Get the last two operands.
2205 bc_program_binOpPrep(p, &r2, &n2, &r3, &n3, 1);
2206
2207 // Make sure that the values have their pointers updated, if necessary.
2208 // Only array elements are possible because this is dc.
2209 if (r1->t == BC_RESULT_ARRAY_ELEM && (r1->t == r2->t || r1->t == r3->t))
2210 {
2211 n1 = bc_program_num(p, r1);
2212 }
2213
2214 BC_SIG_LOCK;
2215
2216 bc_num_init(&res->d.n, n3->len);
2217
2218 BC_SIG_UNLOCK;
2219
2220 bc_num_modexp(n1, n2, n3, &res->d.n);
2221
2222 bc_program_retire(p, 3);
2223 }
2224
2225 /**
2226 * Asciifies a number for dc. This is a helper for bc_program_asciify().
2227 * @param p The program.
2228 * @param n The number to asciify.
2229 */
2230 static uchar
bc_program_asciifyNum(BcProgram * p,BcNum * n)2231 bc_program_asciifyNum(BcProgram* p, BcNum* n)
2232 {
2233 bc_num_copy(&p->asciify, n);
2234
2235 // We want to clear the scale and sign for easy mod later.
2236 bc_num_truncate(&p->asciify, p->asciify.scale);
2237 BC_NUM_NEG_CLR(&p->asciify);
2238
2239 // This is guaranteed to not have a divide by 0
2240 // because strmb is equal to 256.
2241 bc_num_mod(&p->asciify, &p->strmb, &p->asciify, 0);
2242
2243 // This is also guaranteed to not error because num is in the range
2244 // [0, UCHAR_MAX], which is definitely in range for a BcBigDig. And
2245 // it is not negative.
2246 return (uchar) bc_num_bigdig2(&p->asciify);
2247 }
2248
2249 /**
2250 * Executes the "asciify" command in bc and dc.
2251 * @param p The program.
2252 */
2253 static void
bc_program_asciify(BcProgram * p)2254 bc_program_asciify(BcProgram* p)
2255 {
2256 BcResult *r, res;
2257 BcNum* n;
2258 uchar c;
2259 size_t idx;
2260 #if BC_ENABLED
2261 // This is in the outer scope because it has to be freed after a jump.
2262 char* temp_str;
2263 #endif // BC_ENABLED
2264
2265 // Check the stack.
2266 if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);
2267
2268 assert(BC_PROG_STACK(&p->results, 1));
2269
2270 // Get the top of the results stack.
2271 bc_program_operand(p, &r, &n, 0);
2272
2273 assert(n != NULL);
2274 assert(BC_IS_BC || r->t != BC_RESULT_ARRAY);
2275
2276 #if BC_ENABLED
2277 // Handle arrays in bc specially.
2278 if (r->t == BC_RESULT_ARRAY)
2279 {
2280 // Yes, this is one place where we need to cast the number from
2281 // bc_program_num() to a vector.
2282 BcVec* v = (BcVec*) n;
2283 size_t i;
2284
2285 // XXX: If this is changed, you should also change the similar code in
2286 // bc_program_builtin().
2287
2288 // Dereference the array, if necessary.
2289 if (v->size == sizeof(uchar))
2290 {
2291 v = bc_program_dereference(p, v);
2292 }
2293
2294 assert(v->size == sizeof(BcNum));
2295
2296 // Allocate the string and set the jump for it.
2297 BC_SIG_LOCK;
2298 temp_str = bc_vm_malloc(v->len + 1);
2299 BC_SETJMP_LOCKED(vm, exit);
2300 BC_SIG_UNLOCK;
2301
2302 // Convert the array.
2303 for (i = 0; i < v->len; ++i)
2304 {
2305 BcNum* num = (BcNum*) bc_vec_item(v, i);
2306
2307 if (BC_PROG_STR(num))
2308 {
2309 temp_str[i] = (bc_program_string(p, num))[0];
2310 }
2311 else
2312 {
2313 temp_str[i] = (char) bc_program_asciifyNum(p, num);
2314 }
2315 }
2316
2317 temp_str[v->len] = '\0';
2318
2319 // Store the string in the slab and map, and free the temp string.
2320 BC_SIG_LOCK;
2321 idx = bc_program_addString(p, temp_str);
2322 free(temp_str);
2323 BC_UNSETJMP(vm);
2324 BC_SIG_UNLOCK;
2325 }
2326 else
2327 #endif // BC_ENABLED
2328 {
2329 char str[2];
2330 char* str2;
2331
2332 // Asciify.
2333 if (BC_PROG_NUM(r, n)) c = bc_program_asciifyNum(p, n);
2334 else
2335 {
2336 // Get the string itself, then the first character.
2337 str2 = bc_program_string(p, n);
2338 c = (uchar) str2[0];
2339 }
2340
2341 // Fill the resulting string.
2342 str[0] = (char) c;
2343 str[1] = '\0';
2344
2345 // Add the string to the data structures.
2346 BC_SIG_LOCK;
2347 idx = bc_program_addString(p, str);
2348 BC_SIG_UNLOCK;
2349 }
2350
2351 // Set the result
2352 res.t = BC_RESULT_STR;
2353 bc_num_clear(&res.d.n);
2354 res.d.n.scale = idx;
2355
2356 // Pop and push.
2357 bc_vec_pop(&p->results);
2358 bc_vec_push(&p->results, &res);
2359
2360 return;
2361
2362 #if BC_ENABLED
2363 exit:
2364 free(temp_str);
2365 #endif // BC_ENABLED
2366 }
2367
2368 /**
2369 * Streams a number or a string to stdout.
2370 * @param p The program.
2371 */
2372 static void
bc_program_printStream(BcProgram * p)2373 bc_program_printStream(BcProgram* p)
2374 {
2375 BcResult* r;
2376 BcNum* n;
2377
2378 // Check the stack.
2379 if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);
2380
2381 assert(BC_PROG_STACK(&p->results, 1));
2382
2383 // Get the top of the results stack.
2384 bc_program_operand(p, &r, &n, 0);
2385
2386 assert(n != NULL);
2387
2388 // Stream appropriately.
2389 if (BC_PROG_NUM(r, n)) bc_num_stream(n);
2390 else bc_program_printChars(bc_program_string(p, n));
2391
2392 // Pop the operand.
2393 bc_vec_pop(&p->results);
2394 }
2395
2396 #if DC_ENABLED
2397
2398 /**
2399 * Gets the length of a register in dc and pushes it onto the results stack.
2400 * @param p The program.
2401 * @param code The bytecode vector to pull the register's index out of.
2402 * @param bgn An in/out parameter; the start of the index in the bytecode
2403 * vector, and will be updated to point after the index on return.
2404 */
2405 static void
bc_program_regStackLen(BcProgram * p,const char * restrict code,size_t * restrict bgn)2406 bc_program_regStackLen(BcProgram* p, const char* restrict code,
2407 size_t* restrict bgn)
2408 {
2409 size_t idx = bc_program_index(code, bgn);
2410 BcVec* v = bc_program_vec(p, idx, BC_TYPE_VAR);
2411
2412 bc_program_pushBigdig(p, (BcBigDig) v->len, BC_RESULT_TEMP);
2413 }
2414
2415 /**
2416 * Pushes the length of the results stack onto the results stack.
2417 * @param p The program.
2418 */
2419 static void
bc_program_stackLen(BcProgram * p)2420 bc_program_stackLen(BcProgram* p)
2421 {
2422 bc_program_pushBigdig(p, (BcBigDig) p->results.len, BC_RESULT_TEMP);
2423 }
2424
2425 /**
2426 * Pops a certain number of elements off the execution stack.
2427 * @param p The program.
2428 * @param inst The instruction to tell us how many. There is one to pop up to
2429 * 2, and one to pop the amount equal to the number at the top of
2430 * the results stack.
2431 */
2432 static void
bc_program_nquit(BcProgram * p,uchar inst)2433 bc_program_nquit(BcProgram* p, uchar inst)
2434 {
2435 BcResult* opnd;
2436 BcNum* num;
2437 BcBigDig val;
2438 size_t i;
2439
2440 // Ensure that the tail calls stack is correct.
2441 assert(p->stack.len == p->tail_calls.len);
2442
2443 // Get the number of executions to pop.
2444 if (inst == BC_INST_QUIT) val = 2;
2445 else
2446 {
2447 bc_program_prep(p, &opnd, &num, 0);
2448 val = bc_num_bigdig(num);
2449
2450 bc_vec_pop(&p->results);
2451 }
2452
2453 // Loop over the tail call stack and adjust the quit value appropriately.
2454 for (i = 0; val && i < p->tail_calls.len; ++i)
2455 {
2456 // Get the number of tail calls for this one.
2457 size_t calls = *((size_t*) bc_vec_item_rev(&p->tail_calls, i)) + 1;
2458
2459 // Adjust the value.
2460 if (calls >= val) val = 0;
2461 else val -= (BcBigDig) calls;
2462 }
2463
2464 // If we don't have enough executions, just quit.
2465 if (i == p->stack.len)
2466 {
2467 vm->status = BC_STATUS_QUIT;
2468 BC_JMP;
2469 }
2470 else
2471 {
2472 // We can always pop the last item we reached on the tail call stack
2473 // because these are for tail calls. That means that any executions that
2474 // we would not have quit in that position on the stack would have quit
2475 // anyway.
2476 BC_SIG_LOCK;
2477 bc_vec_npop(&p->stack, i);
2478 bc_vec_npop(&p->tail_calls, i);
2479 BC_SIG_UNLOCK;
2480 }
2481 }
2482
2483 /**
2484 * Pushes the depth of the execution stack onto the stack.
2485 * @param p The program.
2486 */
2487 static void
bc_program_execStackLen(BcProgram * p)2488 bc_program_execStackLen(BcProgram* p)
2489 {
2490 size_t i, amt, len = p->tail_calls.len;
2491
2492 amt = len;
2493
2494 for (i = 0; i < len; ++i)
2495 {
2496 amt += *((size_t*) bc_vec_item(&p->tail_calls, i));
2497 }
2498
2499 bc_program_pushBigdig(p, (BcBigDig) amt, BC_RESULT_TEMP);
2500 }
2501
2502 /**
2503 *
2504 * @param p The program.
2505 * @param code The bytecode vector to pull the register's index out of.
2506 * @param bgn An in/out parameter; the start of the index in the bytecode
2507 * vector, and will be updated to point after the index on return.
2508 * @param cond True if the execution is conditional, false otherwise.
2509 * @param len The number of bytes in the bytecode vector.
2510 */
2511 static void
bc_program_execStr(BcProgram * p,const char * restrict code,size_t * restrict bgn,bool cond,size_t len)2512 bc_program_execStr(BcProgram* p, const char* restrict code,
2513 size_t* restrict bgn, bool cond, size_t len)
2514 {
2515 BcResult* r;
2516 char* str;
2517 BcFunc* f;
2518 BcInstPtr ip;
2519 size_t fidx;
2520 BcNum* n;
2521
2522 assert(p->stack.len == p->tail_calls.len);
2523
2524 // Check the stack.
2525 if (BC_ERR(!BC_PROG_STACK(&p->results, 1))) bc_err(BC_ERR_EXEC_STACK);
2526
2527 assert(BC_PROG_STACK(&p->results, 1));
2528
2529 // Get the operand.
2530 bc_program_operand(p, &r, &n, 0);
2531
2532 // If execution is conditional...
2533 if (cond)
2534 {
2535 bool exec;
2536 size_t then_idx;
2537 // These are volatile to quiet warnings on GCC about clobbering with
2538 // longjmp().
2539 volatile size_t else_idx;
2540 volatile size_t idx;
2541
2542 // Get the index of the "then" var and "else" var.
2543 then_idx = bc_program_index(code, bgn);
2544 else_idx = bc_program_index(code, bgn);
2545
2546 // Figure out if we should execute.
2547 exec = (r->d.n.len != 0);
2548
2549 idx = exec ? then_idx : else_idx;
2550
2551 BC_SIG_LOCK;
2552 BC_SETJMP_LOCKED(vm, exit);
2553
2554 // If we are supposed to execute, execute. If else_idx == SIZE_MAX, that
2555 // means there was no else clause, so if execute is false and else does
2556 // not exist, we don't execute. The goto skips all of the setup for the
2557 // execution.
2558 if (exec || (else_idx != SIZE_MAX))
2559 {
2560 n = bc_vec_top(bc_program_vec(p, idx, BC_TYPE_VAR));
2561 }
2562 else goto exit;
2563
2564 if (BC_ERR(!BC_PROG_STR(n))) bc_err(BC_ERR_EXEC_TYPE);
2565
2566 BC_UNSETJMP(vm);
2567 BC_SIG_UNLOCK;
2568 }
2569 else
2570 {
2571 // In non-conditional situations, only the top of stack can be executed,
2572 // and in those cases, variables are not allowed to be "on the stack";
2573 // they are only put on the stack to be assigned to.
2574 assert(r->t != BC_RESULT_VAR);
2575
2576 if (r->t != BC_RESULT_STR) return;
2577 }
2578
2579 assert(BC_PROG_STR(n));
2580
2581 // Get the string.
2582 str = bc_program_string(p, n);
2583
2584 // Get the function index and function.
2585 BC_SIG_LOCK;
2586 fidx = bc_program_insertFunc(p, str);
2587 BC_SIG_UNLOCK;
2588 f = bc_vec_item(&p->fns, fidx);
2589
2590 // If the function has not been parsed yet...
2591 if (!f->code.len)
2592 {
2593 BC_SIG_LOCK;
2594
2595 if (!BC_PARSE_IS_INITED(&vm->read_prs, p))
2596 {
2597 bc_parse_init(&vm->read_prs, p, fidx);
2598
2599 // Initialize this too because bc_vm_shutdown() expects them to be
2600 // initialized togther.
2601 bc_vec_init(&vm->read_buf, sizeof(char), BC_DTOR_NONE);
2602 }
2603 // This needs to be updated because the parser could have been used
2604 // somewhere else
2605 else bc_parse_updateFunc(&vm->read_prs, fidx);
2606
2607 bc_lex_file(&vm->read_prs.l, vm->file);
2608
2609 BC_SETJMP_LOCKED(vm, err);
2610
2611 BC_SIG_UNLOCK;
2612
2613 // Parse. Only one expression is needed, so stdin isn't used.
2614 bc_parse_text(&vm->read_prs, str, BC_MODE_FILE);
2615
2616 BC_SIG_LOCK;
2617 vm->expr(&vm->read_prs, BC_PARSE_NOCALL);
2618
2619 BC_UNSETJMP(vm);
2620
2621 // We can just assert this here because
2622 // dc should parse everything until EOF.
2623 assert(vm->read_prs.l.t == BC_LEX_EOF);
2624
2625 BC_SIG_UNLOCK;
2626 }
2627
2628 // Set the instruction pointer.
2629 ip.idx = 0;
2630 ip.len = p->results.len;
2631 ip.func = fidx;
2632
2633 BC_SIG_LOCK;
2634
2635 // Pop the operand.
2636 bc_vec_pop(&p->results);
2637
2638 // Tail call processing. This condition means that there is more on the
2639 // execution stack, and we are at the end of the bytecode vector, and the
2640 // last instruction is just a BC_INST_POP_EXEC, which would return.
2641 if (p->stack.len > 1 && *bgn == len - 1 && code[*bgn] == BC_INST_POP_EXEC)
2642 {
2643 size_t* call_ptr = bc_vec_top(&p->tail_calls);
2644
2645 // Add one to the tail call.
2646 *call_ptr += 1;
2647
2648 // Pop the execution stack before pushing the new instruction pointer
2649 // on.
2650 bc_vec_pop(&p->stack);
2651 }
2652 // If not a tail call, just push a new one.
2653 else bc_vec_push(&p->tail_calls, &ip.idx);
2654
2655 // Push the new function onto the execution stack and return.
2656 bc_vec_push(&p->stack, &ip);
2657
2658 BC_SIG_UNLOCK;
2659
2660 return;
2661
2662 err:
2663 BC_SIG_MAYLOCK;
2664
2665 f = bc_vec_item(&p->fns, fidx);
2666
2667 // Make sure to erase the bytecode vector so dc knows it is not parsed.
2668 bc_vec_popAll(&f->code);
2669
2670 exit:
2671 bc_vec_pop(&p->results);
2672 BC_LONGJMP_CONT(vm);
2673 }
2674
2675 /**
2676 * Prints every item on the results stack, one per line.
2677 * @param p The program.
2678 */
2679 static void
bc_program_printStack(BcProgram * p)2680 bc_program_printStack(BcProgram* p)
2681 {
2682 size_t idx;
2683
2684 for (idx = 0; idx < p->results.len; ++idx)
2685 {
2686 bc_program_print(p, BC_INST_PRINT, idx);
2687 }
2688 }
2689 #endif // DC_ENABLED
2690
2691 /**
2692 * Pushes the value of a global onto the results stack.
2693 * @param p The program.
2694 * @param inst Which global to push, as an instruction.
2695 */
2696 static void
bc_program_pushGlobal(BcProgram * p,uchar inst)2697 bc_program_pushGlobal(BcProgram* p, uchar inst)
2698 {
2699 BcResultType t;
2700
2701 // Make sure the instruction is valid.
2702 assert(inst >= BC_INST_IBASE && inst <= BC_INST_SCALE);
2703
2704 // Push the global.
2705 t = inst - BC_INST_IBASE + BC_RESULT_IBASE;
2706 bc_program_pushBigdig(p, p->globals[inst - BC_INST_IBASE], t);
2707 }
2708
2709 /**
2710 * Pushes the value of a global setting onto the stack.
2711 * @param p The program.
2712 * @param inst Which global setting to push, as an instruction.
2713 */
2714 static void
bc_program_globalSetting(BcProgram * p,uchar inst)2715 bc_program_globalSetting(BcProgram* p, uchar inst)
2716 {
2717 BcBigDig val;
2718
2719 // Make sure the instruction is valid.
2720 #if DC_ENABLED
2721 assert((inst >= BC_INST_LINE_LENGTH && inst <= BC_INST_LEADING_ZERO) ||
2722 (BC_IS_DC && inst == BC_INST_EXTENDED_REGISTERS));
2723 #else // DC_ENABLED
2724 assert(inst >= BC_INST_LINE_LENGTH && inst <= BC_INST_LEADING_ZERO);
2725 #endif // DC_ENABLED
2726
2727 if (inst == BC_INST_LINE_LENGTH)
2728 {
2729 val = (BcBigDig) vm->line_len;
2730 }
2731 #if BC_ENABLED
2732 else if (inst == BC_INST_GLOBAL_STACKS)
2733 {
2734 val = (BC_G != 0);
2735 }
2736 #endif // BC_ENABLED
2737 #if DC_ENABLED
2738 else if (inst == BC_INST_EXTENDED_REGISTERS)
2739 {
2740 val = (DC_X != 0);
2741 }
2742 #endif // DC_ENABLED
2743 else val = (BC_Z != 0);
2744
2745 // Push the global.
2746 bc_program_pushBigdig(p, val, BC_RESULT_TEMP);
2747 }
2748
2749 #if BC_ENABLE_EXTRA_MATH
2750
2751 /**
2752 * Pushes the value of seed on the stack.
2753 * @param p The program.
2754 */
2755 static void
bc_program_pushSeed(BcProgram * p)2756 bc_program_pushSeed(BcProgram* p)
2757 {
2758 BcResult* res;
2759
2760 res = bc_program_prepResult(p);
2761
2762 assert(p->nresults == 1);
2763
2764 res->t = BC_RESULT_SEED;
2765
2766 BC_SIG_LOCK;
2767
2768 // We need 2*BC_RAND_NUM_SIZE because of the size of the state.
2769 bc_num_init(&res->d.n, 2 * BC_RAND_NUM_SIZE);
2770
2771 BC_SIG_UNLOCK;
2772
2773 bc_num_createFromRNG(&res->d.n, &p->rng);
2774
2775 // XXX: Clear the number of results.
2776 p->nresults = 0;
2777 }
2778
2779 #endif // BC_ENABLE_EXTRA_MATH
2780
2781 /**
2782 * Adds a function to the fns array. The function's ID must have already been
2783 * inserted into the map.
2784 * @param p The program.
2785 * @param id_ptr The ID of the function as inserted into the map.
2786 */
2787 static void
bc_program_addFunc(BcProgram * p,BcId * id_ptr)2788 bc_program_addFunc(BcProgram* p, BcId* id_ptr)
2789 {
2790 BcFunc* f;
2791
2792 BC_SIG_ASSERT_LOCKED;
2793
2794 // Push and init.
2795 f = bc_vec_pushEmpty(&p->fns);
2796 bc_func_init(f, id_ptr->name);
2797 }
2798
2799 size_t
bc_program_insertFunc(BcProgram * p,const char * name)2800 bc_program_insertFunc(BcProgram* p, const char* name)
2801 {
2802 BcId* id_ptr;
2803 bool new;
2804 size_t idx;
2805
2806 BC_SIG_ASSERT_LOCKED;
2807
2808 assert(p != NULL && name != NULL);
2809
2810 // Insert into the map and get the resulting ID.
2811 new = bc_map_insert(&p->fn_map, name, p->fns.len, &idx);
2812 id_ptr = (BcId*) bc_vec_item(&p->fn_map, idx);
2813 idx = id_ptr->idx;
2814
2815 // If the function is new...
2816 if (new)
2817 {
2818 // Add the function to the fns array.
2819 bc_program_addFunc(p, id_ptr);
2820 }
2821 #if BC_ENABLED
2822 // bc has to reset the function because it's about to be redefined.
2823 else if (BC_IS_BC)
2824 {
2825 BcFunc* func = bc_vec_item(&p->fns, idx);
2826 bc_func_reset(func);
2827 }
2828 #endif // BC_ENABLED
2829
2830 return idx;
2831 }
2832
2833 #if BC_DEBUG || BC_ENABLE_MEMCHECK
2834 void
bc_program_free(BcProgram * p)2835 bc_program_free(BcProgram* p)
2836 {
2837 #if BC_ENABLED
2838 size_t i;
2839 #endif // BC_ENABLED
2840
2841 BC_SIG_ASSERT_LOCKED;
2842
2843 assert(p != NULL);
2844
2845 #if BC_ENABLED
2846 // Free the globals stacks.
2847 for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i)
2848 {
2849 bc_vec_free(p->globals_v + i);
2850 }
2851 #endif // BC_ENABLED
2852
2853 bc_vec_free(&p->fns);
2854 bc_vec_free(&p->fn_map);
2855 bc_vec_free(&p->vars);
2856 bc_vec_free(&p->var_map);
2857 bc_vec_free(&p->arrs);
2858 bc_vec_free(&p->arr_map);
2859 bc_vec_free(&p->results);
2860 bc_vec_free(&p->stack);
2861 bc_vec_free(&p->consts);
2862 bc_vec_free(&p->const_map);
2863 bc_vec_free(&p->strs);
2864 bc_vec_free(&p->str_map);
2865
2866 bc_num_free(&p->asciify);
2867
2868 #if BC_ENABLED
2869 if (BC_IS_BC) bc_num_free(&p->last);
2870 #endif // BC_ENABLED
2871
2872 #if BC_ENABLE_EXTRA_MATH
2873 bc_rand_free(&p->rng);
2874 #endif // BC_ENABLE_EXTRA_MATH
2875
2876 #if DC_ENABLED
2877 if (BC_IS_DC) bc_vec_free(&p->tail_calls);
2878 #endif // DC_ENABLED
2879 }
2880 #endif // BC_DEBUG || BC_ENABLE_MEMCHECK
2881
2882 void
bc_program_init(BcProgram * p)2883 bc_program_init(BcProgram* p)
2884 {
2885 BcInstPtr ip;
2886 size_t i;
2887
2888 BC_SIG_ASSERT_LOCKED;
2889
2890 assert(p != NULL);
2891
2892 // We want this clear.
2893 // NOLINTNEXTLINE
2894 memset(&ip, 0, sizeof(BcInstPtr));
2895
2896 // Setup the globals stacks and the current values.
2897 for (i = 0; i < BC_PROG_GLOBALS_LEN; ++i)
2898 {
2899 BcBigDig val = i == BC_PROG_GLOBALS_SCALE ? 0 : BC_BASE;
2900
2901 #if BC_ENABLED
2902 bc_vec_init(p->globals_v + i, sizeof(BcBigDig), BC_DTOR_NONE);
2903 bc_vec_push(p->globals_v + i, &val);
2904 #endif // BC_ENABLED
2905
2906 p->globals[i] = val;
2907 }
2908
2909 #if DC_ENABLED
2910 // dc-only setup.
2911 if (BC_IS_DC)
2912 {
2913 bc_vec_init(&p->tail_calls, sizeof(size_t), BC_DTOR_NONE);
2914
2915 // We want an item for the main function on the tail call stack.
2916 i = 0;
2917 bc_vec_push(&p->tail_calls, &i);
2918 }
2919 #endif // DC_ENABLED
2920
2921 bc_num_setup(&p->strmb, p->strmb_num, BC_NUM_BIGDIG_LOG10);
2922 bc_num_bigdig2num(&p->strmb, BC_NUM_STREAM_BASE);
2923
2924 bc_num_init(&p->asciify, BC_NUM_DEF_SIZE);
2925
2926 #if BC_ENABLE_EXTRA_MATH
2927 // We need to initialize srand() just in case /dev/urandom and /dev/random
2928 // are not available.
2929 srand((unsigned int) time(NULL));
2930 bc_rand_init(&p->rng);
2931 #endif // BC_ENABLE_EXTRA_MATH
2932
2933 #if BC_ENABLED
2934 if (BC_IS_BC) bc_num_init(&p->last, BC_NUM_DEF_SIZE);
2935 #endif // BC_ENABLED
2936
2937 #if BC_DEBUG
2938 bc_vec_init(&p->fns, sizeof(BcFunc), BC_DTOR_FUNC);
2939 #else // BC_DEBUG
2940 bc_vec_init(&p->fns, sizeof(BcFunc), BC_DTOR_NONE);
2941 #endif // BC_DEBUG
2942 bc_map_init(&p->fn_map);
2943 bc_program_insertFunc(p, bc_func_main);
2944 bc_program_insertFunc(p, bc_func_read);
2945
2946 bc_vec_init(&p->vars, sizeof(BcVec), BC_DTOR_VEC);
2947 bc_map_init(&p->var_map);
2948
2949 bc_vec_init(&p->arrs, sizeof(BcVec), BC_DTOR_VEC);
2950 bc_map_init(&p->arr_map);
2951
2952 bc_vec_init(&p->results, sizeof(BcResult), BC_DTOR_RESULT);
2953
2954 // Push the first instruction pointer onto the execution stack.
2955 bc_vec_init(&p->stack, sizeof(BcInstPtr), BC_DTOR_NONE);
2956 bc_vec_push(&p->stack, &ip);
2957
2958 bc_vec_init(&p->consts, sizeof(BcConst), BC_DTOR_CONST);
2959 bc_map_init(&p->const_map);
2960 bc_vec_init(&p->strs, sizeof(char*), BC_DTOR_NONE);
2961 bc_map_init(&p->str_map);
2962
2963 // XXX: Clear the number of results.
2964 p->nresults = 0;
2965 }
2966
2967 void
bc_program_printStackTrace(BcProgram * p)2968 bc_program_printStackTrace(BcProgram* p)
2969 {
2970 size_t i, max_digits;
2971
2972 max_digits = bc_vm_numDigits(p->stack.len - 1);
2973
2974 for (i = 0; i < p->stack.len; ++i)
2975 {
2976 BcInstPtr* ip = bc_vec_item_rev(&p->stack, i);
2977 BcFunc* f = bc_vec_item(&p->fns, ip->func);
2978 size_t j, digits;
2979
2980 digits = bc_vm_numDigits(i);
2981
2982 bc_file_puts(&vm->ferr, bc_flush_none, " ");
2983
2984 for (j = 0; j < max_digits - digits; ++j)
2985 {
2986 bc_file_putchar(&vm->ferr, bc_flush_none, ' ');
2987 }
2988
2989 bc_file_printf(&vm->ferr, "%zu: %s", i, f->name);
2990
2991 #if BC_ENABLED
2992 if (BC_IS_BC && ip->func != BC_PROG_MAIN && ip->func != BC_PROG_READ)
2993 {
2994 bc_file_puts(&vm->ferr, bc_flush_none, "()");
2995 }
2996 #endif // BC_ENABLED
2997
2998 bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
2999 }
3000 }
3001
3002 void
bc_program_reset(BcProgram * p)3003 bc_program_reset(BcProgram* p)
3004 {
3005 BcFunc* f;
3006 BcInstPtr* ip;
3007
3008 BC_SIG_ASSERT_LOCKED;
3009
3010 // Pop all but the last execution.
3011 bc_vec_npop(&p->stack, p->stack.len - 1);
3012
3013 #if DC_ENABLED
3014 // We need to pop tail calls too.
3015 if (BC_IS_DC) bc_vec_npop(&p->tail_calls, p->tail_calls.len - 1);
3016 #endif // DC_ENABLED
3017
3018 // Clear the stack if we are in bc. We have to do this in bc because bc's
3019 // stack is implicit.
3020 //
3021 // XXX: We don't do this in dc because other dc implementations don't.
3022 // However, we *MUST* pop the items for results that are not retired yet.
3023 if (BC_IS_DC && BC_I) bc_vec_npop(&p->results, p->nresults);
3024 else bc_vec_popAll(&p->results);
3025
3026 // Now clear how many results there are.
3027 p->nresults = 0;
3028
3029 #if BC_ENABLED
3030 // Clear the globals' stacks.
3031 if (BC_G) bc_program_popGlobals(p, true);
3032 #endif // BC_ENABLED
3033
3034 // Clear the bytecode vector of the main function.
3035 f = bc_vec_item(&p->fns, BC_PROG_MAIN);
3036 bc_vec_npop(&f->code, f->code.len);
3037
3038 // Reset the instruction pointer.
3039 ip = bc_vec_top(&p->stack);
3040 // NOLINTNEXTLINE
3041 memset(ip, 0, sizeof(BcInstPtr));
3042
3043 if (BC_SIG_INTERRUPT(vm))
3044 {
3045 // Write the ready message for a signal.
3046 bc_file_printf(&vm->fout, "%s", bc_program_ready_msg);
3047 bc_file_flush(&vm->fout, bc_flush_err);
3048 }
3049
3050 // Clear the signal.
3051 vm->sig = 0;
3052 }
3053
3054 void
bc_program_exec(BcProgram * p)3055 bc_program_exec(BcProgram* p)
3056 {
3057 size_t idx;
3058 BcResult r;
3059 BcResult* ptr;
3060 BcInstPtr* ip;
3061 BcFunc* func;
3062 char* code;
3063 bool cond = false;
3064 uchar inst;
3065 #if BC_ENABLED
3066 BcNum* num;
3067 #endif // BC_ENABLED
3068 #if !BC_HAS_COMPUTED_GOTO
3069 #if BC_DEBUG
3070 size_t jmp_bufs_len;
3071 #endif // BC_DEBUG
3072 #endif // !BC_HAS_COMPUTED_GOTO
3073
3074 #if BC_HAS_COMPUTED_GOTO
3075
3076 #if BC_GCC
3077 #pragma GCC diagnostic push
3078 #pragma GCC diagnostic ignored "-Wpedantic"
3079 #endif // BC_GCC
3080
3081 #if BC_CLANG
3082 #pragma clang diagnostic push
3083 #pragma clang diagnostic ignored "-Wgnu-label-as-value"
3084 #endif // BC_CLANG
3085
3086 BC_PROG_LBLS;
3087 BC_PROG_LBLS_ASSERT;
3088
3089 #if BC_CLANG
3090 #pragma clang diagnostic pop
3091 #endif // BC_CLANG
3092
3093 #if BC_GCC
3094 #pragma GCC diagnostic pop
3095 #endif // BC_GCC
3096
3097 // BC_INST_INVALID is a marker for the end so that we don't have to have an
3098 // execution loop.
3099 func = (BcFunc*) bc_vec_item(&p->fns, BC_PROG_MAIN);
3100 bc_vec_pushByte(&func->code, BC_INST_INVALID);
3101 #endif // BC_HAS_COMPUTED_GOTO
3102
3103 BC_SETJMP(vm, end);
3104
3105 ip = bc_vec_top(&p->stack);
3106 func = (BcFunc*) bc_vec_item(&p->fns, ip->func);
3107 code = func->code.v;
3108
3109 #if !BC_HAS_COMPUTED_GOTO
3110
3111 #if BC_DEBUG
3112 jmp_bufs_len = vm->jmp_bufs.len;
3113 #endif // BC_DEBUG
3114
3115 // This loop is the heart of the execution engine. It *is* the engine. For
3116 // computed goto, it is ignored.
3117 while (ip->idx < func->code.len)
3118 #endif // !BC_HAS_COMPUTED_GOTO
3119 {
3120 BC_SIG_ASSERT_NOT_LOCKED;
3121
3122 #if BC_HAS_COMPUTED_GOTO
3123
3124 #if BC_GCC
3125 #pragma GCC diagnostic push
3126 #pragma GCC diagnostic ignored "-Wpedantic"
3127 #endif // BC_GCC
3128
3129 #if BC_CLANG
3130 #pragma clang diagnostic push
3131 #pragma clang diagnostic ignored "-Wgnu-label-as-value"
3132 #endif // BC_CLANG
3133
3134 BC_PROG_JUMP(inst, code, ip);
3135
3136 #else // BC_HAS_COMPUTED_GOTO
3137
3138 // Get the next instruction and increment the index.
3139 inst = (uchar) code[(ip->idx)++];
3140
3141 #endif // BC_HAS_COMPUTED_GOTO
3142
3143 #if BC_DEBUG_CODE
3144 bc_file_printf(&vm->ferr, "inst: %s\n", bc_inst_names[inst]);
3145 bc_file_flush(&vm->ferr, bc_flush_none);
3146 #endif // BC_DEBUG_CODE
3147
3148 #if !BC_HAS_COMPUTED_GOTO
3149 switch (inst)
3150 #endif // !BC_HAS_COMPUTED_GOTO
3151 {
3152 #if BC_ENABLED
3153 // This just sets up the condition for the unconditional jump below,
3154 // which checks the condition, if necessary.
3155 // clang-format off
3156 BC_PROG_LBL(BC_INST_JUMP_ZERO):
3157 // clang-format on
3158 {
3159 bc_program_prep(p, &ptr, &num, 0);
3160
3161 cond = !bc_num_cmpZero(num);
3162 bc_vec_pop(&p->results);
3163
3164 BC_PROG_DIRECT_JUMP(BC_INST_JUMP)
3165 }
3166 // Fallthrough.
3167 BC_PROG_FALLTHROUGH
3168
3169 // clang-format off
3170 BC_PROG_LBL(BC_INST_JUMP):
3171 // clang-format on
3172 {
3173 idx = bc_program_index(code, &ip->idx);
3174
3175 // If a jump is required...
3176 if (inst == BC_INST_JUMP || cond)
3177 {
3178 // Get the address to jump to.
3179 size_t* addr = bc_vec_item(&func->labels, idx);
3180
3181 // If this fails, then the parser failed to set up the
3182 // labels correctly.
3183 assert(*addr != SIZE_MAX);
3184
3185 // Set the new address.
3186 ip->idx = *addr;
3187 }
3188
3189 BC_PROG_JUMP(inst, code, ip);
3190 }
3191
3192 // clang-format off
3193 BC_PROG_LBL(BC_INST_CALL):
3194 // clang-format on
3195 {
3196 assert(BC_IS_BC);
3197
3198 bc_program_call(p, code, &ip->idx);
3199
3200 // Because we changed the execution stack and where we are
3201 // executing, we have to update all of this.
3202 BC_SIG_LOCK;
3203 ip = bc_vec_top(&p->stack);
3204 func = bc_vec_item(&p->fns, ip->func);
3205 code = func->code.v;
3206 BC_SIG_UNLOCK;
3207
3208 BC_PROG_JUMP(inst, code, ip);
3209 }
3210
3211 // clang-format off
3212 BC_PROG_LBL(BC_INST_INC):
3213 BC_PROG_LBL(BC_INST_DEC):
3214 // clang-format on
3215 {
3216 bc_program_incdec(p, inst);
3217 BC_PROG_JUMP(inst, code, ip);
3218 }
3219
3220 // clang-format off
3221 BC_PROG_LBL(BC_INST_HALT):
3222 // clang-format on
3223 {
3224 vm->status = BC_STATUS_QUIT;
3225
3226 // Just jump out. The jump series will take care of everything.
3227 BC_JMP;
3228
3229 BC_PROG_JUMP(inst, code, ip);
3230 }
3231
3232 // clang-format off
3233 BC_PROG_LBL(BC_INST_RET):
3234 BC_PROG_LBL(BC_INST_RET0):
3235 BC_PROG_LBL(BC_INST_RET_VOID):
3236 // clang-format on
3237 {
3238 bc_program_return(p, inst);
3239
3240 // Because we changed the execution stack and where we are
3241 // executing, we have to update all of this.
3242 BC_SIG_LOCK;
3243 ip = bc_vec_top(&p->stack);
3244 func = bc_vec_item(&p->fns, ip->func);
3245 code = func->code.v;
3246 BC_SIG_UNLOCK;
3247
3248 BC_PROG_JUMP(inst, code, ip);
3249 }
3250 #endif // BC_ENABLED
3251
3252 // clang-format off
3253 BC_PROG_LBL(BC_INST_BOOL_OR):
3254 BC_PROG_LBL(BC_INST_BOOL_AND):
3255 BC_PROG_LBL(BC_INST_REL_EQ):
3256 BC_PROG_LBL(BC_INST_REL_LE):
3257 BC_PROG_LBL(BC_INST_REL_GE):
3258 BC_PROG_LBL(BC_INST_REL_NE):
3259 BC_PROG_LBL(BC_INST_REL_LT):
3260 BC_PROG_LBL(BC_INST_REL_GT):
3261 // clang-format on
3262 {
3263 bc_program_logical(p, inst);
3264 BC_PROG_JUMP(inst, code, ip);
3265 }
3266
3267 // clang-format off
3268 BC_PROG_LBL(BC_INST_READ):
3269 // clang-format on
3270 {
3271 // We want to flush output before
3272 // this in case there is a prompt.
3273 bc_file_flush(&vm->fout, bc_flush_save);
3274
3275 bc_program_read(p);
3276
3277 // Because we changed the execution stack and where we are
3278 // executing, we have to update all of this.
3279 BC_SIG_LOCK;
3280 ip = bc_vec_top(&p->stack);
3281 func = bc_vec_item(&p->fns, ip->func);
3282 code = func->code.v;
3283 BC_SIG_UNLOCK;
3284
3285 BC_PROG_JUMP(inst, code, ip);
3286 }
3287
3288 #if BC_ENABLE_EXTRA_MATH
3289 // clang-format off
3290 BC_PROG_LBL(BC_INST_RAND):
3291 // clang-format on
3292 {
3293 bc_program_rand(p);
3294 BC_PROG_JUMP(inst, code, ip);
3295 }
3296 #endif // BC_ENABLE_EXTRA_MATH
3297
3298 // clang-format off
3299 BC_PROG_LBL(BC_INST_MAXIBASE):
3300 BC_PROG_LBL(BC_INST_MAXOBASE):
3301 BC_PROG_LBL(BC_INST_MAXSCALE):
3302 #if BC_ENABLE_EXTRA_MATH
3303 BC_PROG_LBL(BC_INST_MAXRAND):
3304 #endif // BC_ENABLE_EXTRA_MATH
3305 // clang-format on
3306 {
3307 BcBigDig dig = vm->maxes[inst - BC_INST_MAXIBASE];
3308 bc_program_pushBigdig(p, dig, BC_RESULT_TEMP);
3309 BC_PROG_JUMP(inst, code, ip);
3310 }
3311
3312 // clang-format off
3313 BC_PROG_LBL(BC_INST_LINE_LENGTH):
3314 #if BC_ENABLED
3315 BC_PROG_LBL(BC_INST_GLOBAL_STACKS):
3316 #endif // BC_ENABLED
3317 #if DC_ENABLED
3318 BC_PROG_LBL(BC_INST_EXTENDED_REGISTERS):
3319 #endif // DC_ENABLE
3320 BC_PROG_LBL(BC_INST_LEADING_ZERO):
3321 // clang-format on
3322 {
3323 bc_program_globalSetting(p, inst);
3324 BC_PROG_JUMP(inst, code, ip);
3325 }
3326
3327 // clang-format off
3328 BC_PROG_LBL(BC_INST_VAR):
3329 // clang-format on
3330 {
3331 bc_program_pushVar(p, code, &ip->idx, false, false);
3332 BC_PROG_JUMP(inst, code, ip);
3333 }
3334
3335 // clang-format off
3336 BC_PROG_LBL(BC_INST_ARRAY_ELEM):
3337 BC_PROG_LBL(BC_INST_ARRAY):
3338 // clang-format on
3339 {
3340 bc_program_pushArray(p, code, &ip->idx, inst);
3341 BC_PROG_JUMP(inst, code, ip);
3342 }
3343
3344 // clang-format off
3345 BC_PROG_LBL(BC_INST_IBASE):
3346 BC_PROG_LBL(BC_INST_SCALE):
3347 BC_PROG_LBL(BC_INST_OBASE):
3348 // clang-format on
3349 {
3350 bc_program_pushGlobal(p, inst);
3351 BC_PROG_JUMP(inst, code, ip);
3352 }
3353
3354 #if BC_ENABLE_EXTRA_MATH
3355 // clang-format off
3356 BC_PROG_LBL(BC_INST_SEED):
3357 // clang-format on
3358 {
3359 bc_program_pushSeed(p);
3360 BC_PROG_JUMP(inst, code, ip);
3361 }
3362 #endif // BC_ENABLE_EXTRA_MATH
3363
3364 // clang-format off
3365 BC_PROG_LBL(BC_INST_LENGTH):
3366 BC_PROG_LBL(BC_INST_SCALE_FUNC):
3367 BC_PROG_LBL(BC_INST_SQRT):
3368 BC_PROG_LBL(BC_INST_ABS):
3369 BC_PROG_LBL(BC_INST_IS_NUMBER):
3370 BC_PROG_LBL(BC_INST_IS_STRING):
3371 #if BC_ENABLE_EXTRA_MATH
3372 BC_PROG_LBL(BC_INST_IRAND):
3373 #endif // BC_ENABLE_EXTRA_MATH
3374 // clang-format on
3375 {
3376 bc_program_builtin(p, inst);
3377 BC_PROG_JUMP(inst, code, ip);
3378 }
3379
3380 // clang-format off
3381 BC_PROG_LBL(BC_INST_ASCIIFY):
3382 // clang-format on
3383 {
3384 bc_program_asciify(p);
3385
3386 // Because we changed the execution stack and where we are
3387 // executing, we have to update all of this.
3388 BC_SIG_LOCK;
3389 ip = bc_vec_top(&p->stack);
3390 func = bc_vec_item(&p->fns, ip->func);
3391 code = func->code.v;
3392 BC_SIG_UNLOCK;
3393
3394 BC_PROG_JUMP(inst, code, ip);
3395 }
3396
3397 // clang-format off
3398 BC_PROG_LBL(BC_INST_NUM):
3399 // clang-format on
3400 {
3401 bc_program_const(p, code, &ip->idx);
3402 BC_PROG_JUMP(inst, code, ip);
3403 }
3404
3405 // clang-format off
3406 BC_PROG_LBL(BC_INST_ZERO):
3407 BC_PROG_LBL(BC_INST_ONE):
3408 #if BC_ENABLED
3409 BC_PROG_LBL(BC_INST_LAST):
3410 #endif // BC_ENABLED
3411 // clang-format on
3412 {
3413 r.t = BC_RESULT_ZERO + (inst - BC_INST_ZERO);
3414 bc_vec_push(&p->results, &r);
3415 BC_PROG_JUMP(inst, code, ip);
3416 }
3417
3418 // clang-format off
3419 BC_PROG_LBL(BC_INST_PRINT):
3420 BC_PROG_LBL(BC_INST_PRINT_POP):
3421 #if BC_ENABLED
3422 BC_PROG_LBL(BC_INST_PRINT_STR):
3423 #endif // BC_ENABLED
3424 // clang-format on
3425 {
3426 bc_program_print(p, inst, 0);
3427
3428 // We want to flush right away to save the output for history,
3429 // if history must preserve it when taking input.
3430 bc_file_flush(&vm->fout, bc_flush_save);
3431
3432 BC_PROG_JUMP(inst, code, ip);
3433 }
3434
3435 // clang-format off
3436 BC_PROG_LBL(BC_INST_STR):
3437 // clang-format on
3438 {
3439 // Set up the result and push.
3440 r.t = BC_RESULT_STR;
3441 bc_num_clear(&r.d.n);
3442 r.d.n.scale = bc_program_index(code, &ip->idx);
3443 bc_vec_push(&p->results, &r);
3444 BC_PROG_JUMP(inst, code, ip);
3445 }
3446
3447 // clang-format off
3448 BC_PROG_LBL(BC_INST_POWER):
3449 BC_PROG_LBL(BC_INST_MULTIPLY):
3450 BC_PROG_LBL(BC_INST_DIVIDE):
3451 BC_PROG_LBL(BC_INST_MODULUS):
3452 BC_PROG_LBL(BC_INST_PLUS):
3453 BC_PROG_LBL(BC_INST_MINUS):
3454 #if BC_ENABLE_EXTRA_MATH
3455 BC_PROG_LBL(BC_INST_PLACES):
3456 BC_PROG_LBL(BC_INST_LSHIFT):
3457 BC_PROG_LBL(BC_INST_RSHIFT):
3458 #endif // BC_ENABLE_EXTRA_MATH
3459 // clang-format on
3460 {
3461 bc_program_op(p, inst);
3462 BC_PROG_JUMP(inst, code, ip);
3463 }
3464
3465 // clang-format off
3466 BC_PROG_LBL(BC_INST_NEG):
3467 BC_PROG_LBL(BC_INST_BOOL_NOT):
3468 #if BC_ENABLE_EXTRA_MATH
3469 BC_PROG_LBL(BC_INST_TRUNC):
3470 #endif // BC_ENABLE_EXTRA_MATH
3471 // clang-format on
3472 {
3473 bc_program_unary(p, inst);
3474 BC_PROG_JUMP(inst, code, ip);
3475 }
3476
3477 // clang-format off
3478 #if BC_ENABLED
3479 BC_PROG_LBL(BC_INST_ASSIGN_POWER):
3480 BC_PROG_LBL(BC_INST_ASSIGN_MULTIPLY):
3481 BC_PROG_LBL(BC_INST_ASSIGN_DIVIDE):
3482 BC_PROG_LBL(BC_INST_ASSIGN_MODULUS):
3483 BC_PROG_LBL(BC_INST_ASSIGN_PLUS):
3484 BC_PROG_LBL(BC_INST_ASSIGN_MINUS):
3485 #if BC_ENABLE_EXTRA_MATH
3486 BC_PROG_LBL(BC_INST_ASSIGN_PLACES):
3487 BC_PROG_LBL(BC_INST_ASSIGN_LSHIFT):
3488 BC_PROG_LBL(BC_INST_ASSIGN_RSHIFT):
3489 #endif // BC_ENABLE_EXTRA_MATH
3490 BC_PROG_LBL(BC_INST_ASSIGN):
3491 BC_PROG_LBL(BC_INST_ASSIGN_POWER_NO_VAL):
3492 BC_PROG_LBL(BC_INST_ASSIGN_MULTIPLY_NO_VAL):
3493 BC_PROG_LBL(BC_INST_ASSIGN_DIVIDE_NO_VAL):
3494 BC_PROG_LBL(BC_INST_ASSIGN_MODULUS_NO_VAL):
3495 BC_PROG_LBL(BC_INST_ASSIGN_PLUS_NO_VAL):
3496 BC_PROG_LBL(BC_INST_ASSIGN_MINUS_NO_VAL):
3497 #if BC_ENABLE_EXTRA_MATH
3498 BC_PROG_LBL(BC_INST_ASSIGN_PLACES_NO_VAL):
3499 BC_PROG_LBL(BC_INST_ASSIGN_LSHIFT_NO_VAL):
3500 BC_PROG_LBL(BC_INST_ASSIGN_RSHIFT_NO_VAL):
3501 #endif // BC_ENABLE_EXTRA_MATH
3502 #endif // BC_ENABLED
3503 BC_PROG_LBL(BC_INST_ASSIGN_NO_VAL):
3504 // clang-format on
3505 {
3506 bc_program_assign(p, inst);
3507 BC_PROG_JUMP(inst, code, ip);
3508 }
3509
3510 // clang-format off
3511 BC_PROG_LBL(BC_INST_POP):
3512 // clang-format on
3513 {
3514 #ifndef BC_PROG_NO_STACK_CHECK
3515 // dc must do a stack check, but bc does not.
3516 if (BC_IS_DC)
3517 {
3518 if (BC_ERR(!BC_PROG_STACK(&p->results, 1)))
3519 {
3520 bc_err(BC_ERR_EXEC_STACK);
3521 }
3522 }
3523 #endif // BC_PROG_NO_STACK_CHECK
3524
3525 assert(BC_PROG_STACK(&p->results, 1));
3526
3527 bc_vec_pop(&p->results);
3528
3529 BC_PROG_JUMP(inst, code, ip);
3530 }
3531
3532 // clang-format off
3533 BC_PROG_LBL(BC_INST_SWAP):
3534 // clang-format on
3535 {
3536 BcResult* ptr2;
3537
3538 // Check the stack.
3539 if (BC_ERR(!BC_PROG_STACK(&p->results, 2)))
3540 {
3541 bc_err(BC_ERR_EXEC_STACK);
3542 }
3543
3544 assert(BC_PROG_STACK(&p->results, 2));
3545
3546 // Get the two items.
3547 ptr = bc_vec_item_rev(&p->results, 0);
3548 ptr2 = bc_vec_item_rev(&p->results, 1);
3549
3550 // Swap. It's just easiest to do it this way.
3551 // NOLINTNEXTLINE
3552 memcpy(&r, ptr, sizeof(BcResult));
3553 // NOLINTNEXTLINE
3554 memcpy(ptr, ptr2, sizeof(BcResult));
3555 // NOLINTNEXTLINE
3556 memcpy(ptr2, &r, sizeof(BcResult));
3557
3558 BC_PROG_JUMP(inst, code, ip);
3559 }
3560
3561 // clang-format off
3562 BC_PROG_LBL(BC_INST_MODEXP):
3563 // clang-format on
3564 {
3565 bc_program_modexp(p);
3566 BC_PROG_JUMP(inst, code, ip);
3567 }
3568
3569 // clang-format off
3570 BC_PROG_LBL(BC_INST_DIVMOD):
3571 // clang-format on
3572 {
3573 bc_program_divmod(p);
3574 BC_PROG_JUMP(inst, code, ip);
3575 }
3576
3577 // clang-format off
3578 BC_PROG_LBL(BC_INST_PRINT_STREAM):
3579 // clang-format on
3580 {
3581 bc_program_printStream(p);
3582 BC_PROG_JUMP(inst, code, ip);
3583 }
3584
3585 #if DC_ENABLED
3586 // clang-format off
3587 BC_PROG_LBL(BC_INST_POP_EXEC):
3588 // clang-format on
3589 {
3590 // If this fails, the dc parser got something wrong.
3591 assert(BC_PROG_STACK(&p->stack, 2));
3592
3593 // Pop the execution stack and tail call stack.
3594 bc_vec_pop(&p->stack);
3595 bc_vec_pop(&p->tail_calls);
3596
3597 // Because we changed the execution stack and where we are
3598 // executing, we have to update all of this.
3599 BC_SIG_LOCK;
3600 ip = bc_vec_top(&p->stack);
3601 func = bc_vec_item(&p->fns, ip->func);
3602 code = func->code.v;
3603 BC_SIG_UNLOCK;
3604
3605 BC_PROG_JUMP(inst, code, ip);
3606 }
3607
3608 // clang-format off
3609 BC_PROG_LBL(BC_INST_EXECUTE):
3610 BC_PROG_LBL(BC_INST_EXEC_COND):
3611 // clang-format on
3612 {
3613 cond = (inst == BC_INST_EXEC_COND);
3614
3615 bc_program_execStr(p, code, &ip->idx, cond, func->code.len);
3616
3617 // Because we changed the execution stack and where we are
3618 // executing, we have to update all of this.
3619 BC_SIG_LOCK;
3620 ip = bc_vec_top(&p->stack);
3621 func = bc_vec_item(&p->fns, ip->func);
3622 code = func->code.v;
3623 BC_SIG_UNLOCK;
3624
3625 BC_PROG_JUMP(inst, code, ip);
3626 }
3627
3628 // clang-format off
3629 BC_PROG_LBL(BC_INST_PRINT_STACK):
3630 // clang-format on
3631 {
3632 bc_program_printStack(p);
3633 BC_PROG_JUMP(inst, code, ip);
3634 }
3635
3636 // clang-format off
3637 BC_PROG_LBL(BC_INST_CLEAR_STACK):
3638 // clang-format on
3639 {
3640 bc_vec_popAll(&p->results);
3641 BC_PROG_JUMP(inst, code, ip);
3642 }
3643
3644 // clang-format off
3645 BC_PROG_LBL(BC_INST_REG_STACK_LEN):
3646 // clang-format on
3647 {
3648 bc_program_regStackLen(p, code, &ip->idx);
3649 BC_PROG_JUMP(inst, code, ip);
3650 }
3651
3652 // clang-format off
3653 BC_PROG_LBL(BC_INST_STACK_LEN):
3654 // clang-format on
3655 {
3656 bc_program_stackLen(p);
3657 BC_PROG_JUMP(inst, code, ip);
3658 }
3659
3660 // clang-format off
3661 BC_PROG_LBL(BC_INST_DUPLICATE):
3662 // clang-format on
3663 {
3664 // Check the stack.
3665 if (BC_ERR(!BC_PROG_STACK(&p->results, 1)))
3666 {
3667 bc_err(BC_ERR_EXEC_STACK);
3668 }
3669
3670 assert(BC_PROG_STACK(&p->results, 1));
3671
3672 // Get the top of the stack.
3673 ptr = bc_vec_top(&p->results);
3674
3675 BC_SIG_LOCK;
3676
3677 // Copy and push.
3678 bc_result_copy(&r, ptr);
3679 bc_vec_push(&p->results, &r);
3680
3681 BC_SIG_UNLOCK;
3682
3683 BC_PROG_JUMP(inst, code, ip);
3684 }
3685
3686 // clang-format off
3687 BC_PROG_LBL(BC_INST_LOAD):
3688 BC_PROG_LBL(BC_INST_PUSH_VAR):
3689 // clang-format on
3690 {
3691 bool copy = (inst == BC_INST_LOAD);
3692 bc_program_pushVar(p, code, &ip->idx, true, copy);
3693 BC_PROG_JUMP(inst, code, ip);
3694 }
3695
3696 // clang-format off
3697 BC_PROG_LBL(BC_INST_PUSH_TO_VAR):
3698 // clang-format on
3699 {
3700 idx = bc_program_index(code, &ip->idx);
3701 bc_program_copyToVar(p, idx, BC_TYPE_VAR);
3702 BC_PROG_JUMP(inst, code, ip);
3703 }
3704
3705 // clang-format off
3706 BC_PROG_LBL(BC_INST_QUIT):
3707 BC_PROG_LBL(BC_INST_NQUIT):
3708 // clang-format on
3709 {
3710 bc_program_nquit(p, inst);
3711
3712 // Because we changed the execution stack and where we are
3713 // executing, we have to update all of this.
3714 BC_SIG_LOCK;
3715 ip = bc_vec_top(&p->stack);
3716 func = bc_vec_item(&p->fns, ip->func);
3717 code = func->code.v;
3718 BC_SIG_UNLOCK;
3719
3720 BC_PROG_JUMP(inst, code, ip);
3721 }
3722
3723 // clang-format off
3724 BC_PROG_LBL(BC_INST_EXEC_STACK_LEN):
3725 // clang-format on
3726 {
3727 bc_program_execStackLen(p);
3728 BC_PROG_JUMP(inst, code, ip);
3729 }
3730 #endif // DC_ENABLED
3731
3732 #if BC_HAS_COMPUTED_GOTO
3733 // clang-format off
3734 BC_PROG_LBL(BC_INST_INVALID):
3735 // clang-format on
3736 {
3737 goto end;
3738 }
3739 #else // BC_HAS_COMPUTED_GOTO
3740 default:
3741 {
3742 BC_UNREACHABLE
3743 #if BC_DEBUG && !BC_CLANG
3744 abort();
3745 #endif // BC_DEBUG && !BC_CLANG
3746 }
3747 #endif // BC_HAS_COMPUTED_GOTO
3748 }
3749
3750 #if BC_HAS_COMPUTED_GOTO
3751
3752 #if BC_CLANG
3753 #pragma clang diagnostic pop
3754 #endif // BC_CLANG
3755
3756 #if BC_GCC
3757 #pragma GCC diagnostic pop
3758 #endif // BC_GCC
3759
3760 #else // BC_HAS_COMPUTED_GOTO
3761
3762 #if BC_DEBUG
3763 // This is to allow me to use a debugger to see the last instruction,
3764 // which will point to which function was the problem. But it's also a
3765 // good smoke test for error handling changes.
3766 assert(jmp_bufs_len == vm->jmp_bufs.len);
3767 #endif // BC_DEBUG
3768
3769 #endif // BC_HAS_COMPUTED_GOTO
3770 }
3771
3772 end:
3773 BC_SIG_MAYLOCK;
3774
3775 // This is here just to print a stack trace on interrupts. This is for
3776 // finding infinite loops.
3777 if (BC_SIG_INTERRUPT(vm))
3778 {
3779 BcStatus s;
3780
3781 bc_file_putchar(&vm->ferr, bc_flush_none, '\n');
3782
3783 bc_program_printStackTrace(p);
3784
3785 s = bc_file_flushErr(&vm->ferr, bc_flush_err);
3786 if (BC_ERR(s != BC_STATUS_SUCCESS && vm->status == BC_STATUS_SUCCESS))
3787 {
3788 vm->status = (sig_atomic_t) s;
3789 }
3790 }
3791
3792 BC_LONGJMP_CONT(vm);
3793 }
3794
3795 #if BC_DEBUG_CODE
3796 #if BC_ENABLED && DC_ENABLED
3797 void
bc_program_printStackDebug(BcProgram * p)3798 bc_program_printStackDebug(BcProgram* p)
3799 {
3800 bc_file_puts(&vm->fout, bc_flush_err, "-------------- Stack ----------\n");
3801 bc_program_printStack(p);
3802 bc_file_puts(&vm->fout, bc_flush_err, "-------------- Stack End ------\n");
3803 }
3804
3805 static void
bc_program_printIndex(const char * restrict code,size_t * restrict bgn)3806 bc_program_printIndex(const char* restrict code, size_t* restrict bgn)
3807 {
3808 uchar byte, i, bytes = (uchar) code[(*bgn)++];
3809 ulong val = 0;
3810
3811 for (byte = 1, i = 0; byte && i < bytes; ++i)
3812 {
3813 byte = (uchar) code[(*bgn)++];
3814 if (byte) val |= ((ulong) byte) << (CHAR_BIT * i);
3815 }
3816
3817 bc_vm_printf(" (%lu) ", val);
3818 }
3819
3820 static void
bc_program_printStr(const BcProgram * p,const char * restrict code,size_t * restrict bgn)3821 bc_program_printStr(const BcProgram* p, const char* restrict code,
3822 size_t* restrict bgn)
3823 {
3824 size_t idx = bc_program_index(code, bgn);
3825 char* s;
3826
3827 s = *((char**) bc_vec_item(&p->strs, idx));
3828
3829 bc_vm_printf(" (\"%s\") ", s);
3830 }
3831
3832 void
bc_program_printInst(const BcProgram * p,const char * restrict code,size_t * restrict bgn)3833 bc_program_printInst(const BcProgram* p, const char* restrict code,
3834 size_t* restrict bgn)
3835 {
3836 uchar inst = (uchar) code[(*bgn)++];
3837
3838 bc_vm_printf("Inst[%zu]: %s [%lu]; ", *bgn - 1, bc_inst_names[inst],
3839 (unsigned long) inst);
3840
3841 if (inst == BC_INST_VAR || inst == BC_INST_ARRAY_ELEM ||
3842 inst == BC_INST_ARRAY)
3843 {
3844 bc_program_printIndex(code, bgn);
3845 }
3846 else if (inst == BC_INST_STR) bc_program_printStr(p, code, bgn);
3847 else if (inst == BC_INST_NUM)
3848 {
3849 size_t idx = bc_program_index(code, bgn);
3850 BcConst* c = bc_vec_item(&p->consts, idx);
3851 bc_vm_printf("(%s)", c->val);
3852 }
3853 else if (inst == BC_INST_CALL ||
3854 (inst > BC_INST_STR && inst <= BC_INST_JUMP_ZERO))
3855 {
3856 bc_program_printIndex(code, bgn);
3857 if (inst == BC_INST_CALL) bc_program_printIndex(code, bgn);
3858 }
3859
3860 bc_vm_putchar('\n', bc_flush_err);
3861 }
3862
3863 void
bc_program_code(const BcProgram * p)3864 bc_program_code(const BcProgram* p)
3865 {
3866 BcFunc* f;
3867 char* code;
3868 BcInstPtr ip;
3869 size_t i;
3870
3871 for (i = 0; i < p->fns.len; ++i)
3872 {
3873 ip.idx = ip.len = 0;
3874 ip.func = i;
3875
3876 f = bc_vec_item(&p->fns, ip.func);
3877 code = f->code.v;
3878
3879 bc_vm_printf("func[%zu]:\n", ip.func);
3880 while (ip.idx < f->code.len)
3881 {
3882 bc_program_printInst(p, code, &ip.idx);
3883 }
3884 bc_file_puts(&vm->fout, bc_flush_err, "\n\n");
3885 }
3886 }
3887 #endif // BC_ENABLED && DC_ENABLED
3888 #endif // BC_DEBUG_CODE
3889