/* * ***************************************************************************** * * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2018-2021 Gavin D. Howard and contributors. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * ***************************************************************************** * * Code to manipulate data structures in programs. * */ #include #include #include #include #include #include void bc_const_free(void *constant) { BcConst *c = constant; BC_SIG_ASSERT_LOCKED; assert(c->val != NULL); bc_num_free(&c->num); } #if BC_ENABLED void bc_func_insert(BcFunc *f, BcProgram *p, char *name, BcType type, size_t line) { BcAuto a; size_t i, idx; // The function must *always* be valid. assert(f != NULL); // Get the index of the variable. idx = bc_program_search(p, name, type == BC_TYPE_VAR); // Search through all of the other autos/parameters. for (i = 0; i < f->autos.len; ++i) { // Get the auto. BcAuto *aptr = bc_vec_item(&f->autos, i); // If they match, barf. if (BC_ERR(idx == aptr->idx && type == aptr->type)) { const char *array = type == BC_TYPE_ARRAY ? "[]" : ""; bc_error(BC_ERR_PARSE_DUP_LOCAL, line, name, array); } } // Set the auto. a.idx = idx; a.type = type; // Push it. bc_vec_push(&f->autos, &a); } #endif // BC_ENABLED void bc_func_init(BcFunc *f, const char *name) { BC_SIG_ASSERT_LOCKED; assert(f != NULL && name != NULL); bc_vec_init(&f->code, sizeof(uchar), BC_DTOR_NONE); bc_vec_init(&f->consts, sizeof(BcConst), BC_DTOR_CONST); bc_vec_init(&f->strs, sizeof(char*), BC_DTOR_NONE); #if BC_ENABLED // Only bc needs these things. if (BC_IS_BC) { bc_vec_init(&f->autos, sizeof(BcAuto), BC_DTOR_NONE); bc_vec_init(&f->labels, sizeof(size_t), BC_DTOR_NONE); f->nparams = 0; f->voidfn = false; } #endif // BC_ENABLED f->name = name; } void bc_func_reset(BcFunc *f) { BC_SIG_ASSERT_LOCKED; assert(f != NULL); bc_vec_popAll(&f->code); bc_vec_popAll(&f->consts); bc_vec_popAll(&f->strs); #if BC_ENABLED if (BC_IS_BC) { bc_vec_popAll(&f->autos); bc_vec_popAll(&f->labels); f->nparams = 0; f->voidfn = false; } #endif // BC_ENABLED } #ifndef NDEBUG void bc_func_free(void *func) { BcFunc *f = (BcFunc*) func; BC_SIG_ASSERT_LOCKED; assert(f != NULL); bc_vec_free(&f->code); bc_vec_free(&f->consts); bc_vec_free(&f->strs); #if BC_ENABLED if (BC_IS_BC) { bc_vec_free(&f->autos); bc_vec_free(&f->labels); } #endif // BC_ENABLED } #endif // NDEBUG void bc_array_init(BcVec *a, bool nums) { BC_SIG_ASSERT_LOCKED; // Set the proper vector. if (nums) bc_vec_init(a, sizeof(BcNum), BC_DTOR_NUM); else bc_vec_init(a, sizeof(BcVec), BC_DTOR_VEC); // We always want at least one item in the array. bc_array_expand(a, 1); } void bc_array_copy(BcVec *d, const BcVec *s) { size_t i; BC_SIG_ASSERT_LOCKED; assert(d != NULL && s != NULL); assert(d != s && d->size == s->size && d->dtor == s->dtor); // Make sure to destroy everything currently in d. This will put a lot of // temps on the reuse list, so allocating later is not going to be as // expensive as it seems. Also, it makes it easier to copy numbers that are // strings. bc_vec_popAll(d); // Preexpand. bc_vec_expand(d, s->cap); d->len = s->len; for (i = 0; i < s->len; ++i) { BcNum *dnum, *snum; dnum = bc_vec_item(d, i); snum = bc_vec_item(s, i); // We have to create a copy of the number as well. if (BC_PROG_STR(snum)) memcpy(dnum, snum, sizeof(BcNum)); else bc_num_createCopy(dnum, snum); } } void bc_array_expand(BcVec *a, size_t len) { assert(a != NULL); BC_SIG_ASSERT_LOCKED; bc_vec_expand(a, len); // If this is true, then we have a num array. if (a->size == sizeof(BcNum) && a->dtor == BC_DTOR_NUM) { // Initialize numbers until we reach the target. while (len > a->len) { BcNum *n = bc_vec_pushEmpty(a); bc_num_init(n, BC_NUM_DEF_SIZE); } } else { assert(a->size == sizeof(BcVec) && a->dtor == BC_DTOR_VEC); // Recursively initialize arrays until we reach the target. Having the // second argument of bc_array_init() be true will activate the base // case, so we're safe. while (len > a->len) { BcVec *v = bc_vec_pushEmpty(a); bc_array_init(v, true); } } } void bc_result_clear(BcResult *r) { r->t = BC_RESULT_TEMP; bc_num_clear(&r->d.n); } #if DC_ENABLED void bc_result_copy(BcResult *d, BcResult *src) { assert(d != NULL && src != NULL); BC_SIG_ASSERT_LOCKED; // d is assumed to not be valid yet. d->t = src->t; // Yes, it depends on what type. switch (d->t) { case BC_RESULT_TEMP: case BC_RESULT_IBASE: case BC_RESULT_SCALE: case BC_RESULT_OBASE: #if BC_ENABLE_EXTRA_MATH case BC_RESULT_SEED: #endif // BC_ENABLE_EXTRA_MATH { bc_num_createCopy(&d->d.n, &src->d.n); break; } case BC_RESULT_VAR: case BC_RESULT_ARRAY: case BC_RESULT_ARRAY_ELEM: { memcpy(&d->d.loc, &src->d.loc, sizeof(BcLoc)); break; } case BC_RESULT_STR: { memcpy(&d->d.n, &src->d.n, sizeof(BcNum)); break; } case BC_RESULT_ZERO: case BC_RESULT_ONE: { // Do nothing. break; } #if BC_ENABLED case BC_RESULT_VOID: case BC_RESULT_LAST: { #ifndef NDEBUG // We should *never* try copying either of these. abort(); #endif // NDEBUG } #endif // BC_ENABLED } } #endif // DC_ENABLED void bc_result_free(void *result) { BcResult *r = (BcResult*) result; BC_SIG_ASSERT_LOCKED; assert(r != NULL); switch (r->t) { case BC_RESULT_TEMP: case BC_RESULT_IBASE: case BC_RESULT_SCALE: case BC_RESULT_OBASE: #if BC_ENABLE_EXTRA_MATH case BC_RESULT_SEED: #endif // BC_ENABLE_EXTRA_MATH { bc_num_free(&r->d.n); break; } case BC_RESULT_VAR: case BC_RESULT_ARRAY: case BC_RESULT_ARRAY_ELEM: case BC_RESULT_STR: case BC_RESULT_ZERO: case BC_RESULT_ONE: #if BC_ENABLED case BC_RESULT_VOID: case BC_RESULT_LAST: #endif // BC_ENABLED { // Do nothing. break; } } }