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