/* context.c -- Handles compilation context state Copyright © 2021-2024 Samuel Lidén Borell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "internal.h" #include "ast.h" #include #include #include #include #define INTERR_CONTEXT(errnum) MAKE_INTERR(errnum, INTERRBASE_CONTEXT) #define INTERR_INVALIDPHASE INTERR_CONTEXT(0x01) #ifndef MALLOC #define MALLOC(n) malloc(n) #define REALLOC(p,n) realloc(p,n) #endif #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && !defined(CSLUL_NO_STDIO_MOCKS) #define MOCKED_STDIO #endif void *lowlvl_alloc(const struct CSlulConfig *cfg, size_t size) { return cfg->params.fptr_malloc ? cfg->params.fptr_malloc(size) : MALLOC(size); } void *lowlvl_realloc(const struct CSlulConfig *cfg, void *p, size_t size) { return cfg->params.fptr_realloc ? cfg->params.fptr_realloc(p, size) : REALLOC(p, size); } void lowlvl_free(const struct CSlulConfig *cfg, void *p) { if (cfg->params.fptr_free) { cfg->params.fptr_free(p); } else { free(p); } } void ctx_outofmem(struct CSlul *ctx) { /* TODO it would be nice to have the line/column number here */ message(ctx, CSLUL_E_OUTOFMEMORY, ctx->current_filename, 0, 0, 0); } void reset_msgstate(struct CSlulState *st) { struct CSlulLocation *loc = &st->locs[0]; int count = CSLUL_MAX_LOCATIONS; while (count--) { loc->filename = NULL; loc->type = CSLUL_LT_INVALID; loc->line = 0; loc->column = 0; loc->length = 0; loc->text = NULL; loc++; } } #ifdef CSLUL_IN_TESTS /* For debugging leaks in tests */ int num_ctxs = 0; #endif struct CSlul *cslul_create(const struct CSlulConfig *config) { struct CSlul *ctx = NULL; const struct CSlulConfig *cfg; struct CSlulConfig *defaults = NULL; if (config) { cfg = config; } else { defaults = cslul_config_create(NULL); if (!defaults) return NULL; if (UNLIKELY(!cslul_config_add_default_iface_dirs(defaults))) goto error; if (UNLIKELY(!cslul_config_add_host_arch(defaults))) goto error; cfg = defaults; } assert(!cfg->has_errors); ctx = lowlvl_alloc(cfg, sizeof(struct CSlul)); if (UNLIKELY(!ctx)) goto error; ctx->cfg = cfg; ctx->has_errors = 0; ctx->has_fatal_errors = 0; ctx->must_have_errors = 0; ctx->buffer = NULL; ctx->current_filename = NULL; ctx->phase = CSLUL_P_INIT; ctx->toklen = 0; ctx->tmplen = 0; ctx->case_insens = 0; ctx->funcbody = NULL; ctx->num_typedefs = 0; ctx->num_datadefs = 0; ctx->num_literals = 0; ctx->num_funcdefs = 0; ctx->has_app_main = 0; ctx->outputs = NULL; ctx->ir_dims_buffer = NULL; ctx->ir_dims_capacity = 0; ctx->owned_config = defaults; reset_msgstate(&ctx->msgstate); /* Arena must be set up before doing any allocations! */ ctx->arenas = NULL; if (UNLIKELY(!arena_new(ctx))) goto error; /* Initialize builtin definitions */ if (UNLIKELY(!builtins_init(ctx))) goto error_arena; #ifdef CSLUL_IN_TESTS num_ctxs++; #endif PROTECT_STRUCT(*ctx); return ctx; error_arena: arena_free(ctx); error: if (ctx) lowlvl_free(cfg, ctx); if (defaults) cslul_config_free(defaults); return NULL; } void cslul_free(struct CSlul *ctx) { struct CSlulConfig *owned_cfg; if (!ctx) return; #ifdef CSLUL_IN_TESTS num_ctxs--; #endif arena_free(ctx); owned_cfg = ctx->owned_config; if (ctx->ir_dims_buffer) lowlvl_free(ctx->cfg, ctx->ir_dims_buffer); lowlvl_free(ctx->cfg, ctx); cslul_config_free(owned_cfg); } const char *cslul_supported_langver(void) { /* When adding a version, it must be added in match_langver() in mhparse.c also */ return "0.0.0"; } const char *cslul_lookup_message(enum CSlulErrorCode errorcode) { if (!CSLUL_IS_INTERNAL_ERR(errorcode)) { /* Normal error */ return lookup_error_message(errorcode); } else { /* Internal compiler error */ enum IntErrBase errbase = INTERR_BASE(errorcode); switch (errbase) { case INTERRBASE_ARENA: return "arena"; case INTERRBASE_BUILD: return "build"; case INTERRBASE_BUILTINS: return "builtins"; case INTERRBASE_CONTEXT: return "context"; case INTERRBASE_MISC: return "misc"; case INTERRBASE_PARSE: return "parse"; case INTERRBASE_MHPARSE: return "mhparse"; case INTERRBASE_TREE: return "tree"; case INTERRBASE_BWRAPPER: return "bwrapper"; case INTERRBASE_PLATFORM: return "platform"; case INTERRBASE_ARCH: return "arch"; case INTERRBASE_TLVERIFY: return "tlverify"; case INTERRBASE_EXPRCHK: return "exprchk"; case INTERRBASE_FUNCCHK: return "funcchk"; case INTERRBASE_TYPECHK: return "typechk"; case INTERRBASE_TOKEN: return "token"; case INTERRBASE_MHTOKEN: return "mhtoken"; case INTERRBASE_CONFIG: return "config"; case INTERRBASE_IR: return "ir"; case INTERRBASE_CHKUTIL: return "chkutil"; case INTERRBASE_TYPECOMPAT: return "typecompat"; } return "unknown"; } } void module_init(struct Module *mod) { mod->name = NULL; mod->namelen = 0; mod->namehash = 0; mod->version = NULL; mod->versionlen = 0; mod->type = CSLUL_MT_UNSET; mod->is_unstable = 1; mod->min_langver = LANGVER_UNSET; mod->impl_minlangver = LANGVER_UNSET; mod->max_langver = LANGVER_UNSET; mod->deps_root = NULL; mod->first_dep = NULL; mod->last_dep = NULL; mod->num_apidefs = 0; mod->apidefs_root = NULL; mod->first_apidef = NULL; mod->last_apidef = NULL; } static void start_of_module(struct CSlul *ctx, struct Module *mod) { assert(mod != NULL); ctx->tokstate.mh = MHTDone; ctx->reused_token.mh = CSLUL_MHT_NONE_QUEUED; ctx->parser.mh.state = MHPDone; ctx->parser.mh.attr = MANone; ctx->parser.mh.seen_explicit_slulrt_dep = 0; /* Interface toplevels are assigned at end of module parsing */ ctx->parsed_module = mod; ctx->in_moduleheader = 1; /* No specific limitations on scripts (but most module attributes only allow a subset of ASCII) */ ctx->allowed_scripts = SCRIPT_ALL|SCRIPT_RTL; module_init(mod); } int cslul_ll_start_phase(struct CSlul *ctx, enum CSlulPhase phase) { /* Finalize the current phase */ switch (ctx->phase) { case CSLUL_P_INIT: case CSLUL_P_MODULEHEADER: case CSLUL_P_VERIFY: case CSLUL_P_CODEGEN: /* No finalization step of these phases */ break; case CSLUL_P_DEPS: /* TODO save toplevel state */ break; case CSLUL_P_IMPL: memcpy(&ctx->impl, &ctx->tl, sizeof(struct TopLevels)); break; case CSLUL_P_IFACE: memcpy(&ctx->module.iface, &ctx->tl, sizeof(struct TopLevels)); break; } /* Start the new phase */ ctx->phase = phase; ctx->case_insens = 0; switch (phase) { case CSLUL_P_INIT: return 1; case CSLUL_P_MODULEHEADER: ctx->num_sourcefiles = 0; ctx->sources_root = NULL; ctx->first_srcfile = NULL; ctx->last_srcfile = NULL; start_of_module(ctx, &ctx->module); return 1; case CSLUL_P_DEPS: ctx->parsed_module = NULL; /* Set by cslul_ll_start_parsing_dep */ /* Fall through */ case CSLUL_P_IFACE: case CSLUL_P_IMPL: ctx->tl.idents_root = NULL; ctx->tl.types_root = NULL; ctx->tl.idents_list = NULL; ctx->tl.types_list = NULL; ctx->params_root = NULL; ctx->in_moduleheader = 0; if (phase == CSLUL_P_IMPL) { if (ctx->module.impl_minlangver == LANGVER_BAD) { error_linecol(ctx, CSLUL_E_UNSUPPORTEDIMPLLANGVER, 0, 0); return 0; } determine_effective_langver(ctx, 1); } return 1; case CSLUL_P_VERIFY: { int ok = 1; ctx->recursion_level = 0; PROTECT_STRUCT(ctx->this_tr); ctx->this_tr.type = NULL; ctx->current_assign_lvalue = NULL; ctx->current_nonecheck_expr = NULL; ok &= tlverify_bind_iface_refs(ctx); ctx->in_typedef_check = 0; ctx->verifying_impl = 0; ok &= tlverify_iface_decls(ctx); ctx->verifying_impl = 1; ok &= tlverify_impl_decls(ctx); ok &= tlverify_check_modspecific(ctx); /* TODO check that slulrt satisfies these requirements: 1. SlulApp must be a private type. 2. SlulExitStatus must be a typedef to int. ... or perhaps enforce this via api-hashes? - note that this MUST be done if the module contains an explicit "\depends slulrt" line (which would override the default one) */ ok &= tlverify_funcs(ctx); assert(ctx->recursion_level == 0 || ctx->has_errors); return ok; } case CSLUL_P_CODEGEN: return backend_output(ctx); default: internal_error(ctx, INTERR_INVALIDPHASE); return 0; } } void cslul_ll_leave_dep_moduleheader(struct CSlul *ctx) { assert(ctx->phase == CSLUL_P_DEPS); ctx->in_moduleheader = 0; ctx->tl.idents_root = NULL; ctx->tl.types_root = NULL; ctx->tl.idents_list = NULL; ctx->tl.types_list = NULL; } /** * Determines the effective language version. * The maximum version can be either: * - the minimum version, * - the impl version, if we are parsing an implementation file, * - the upto version, if it is supported, or * - the latest supported version, if the upto version is not supported. * * The minimum version is always the declared minimum, which * depends on whether we are parsing the interface or compiling the * implementation. */ void determine_effective_langver(struct CSlul *ctx, int in_impl) { /* Maximum version */ if (ctx->module.max_langver == LANGVER_BAD) { ctx->module.effective_maxlangver = LANGVER_LATEST; } else if (ctx->module.max_langver > 0) { ctx->module.effective_maxlangver = ctx->module.max_langver; } else if (in_impl && ctx->module.impl_minlangver > 0) { ctx->module.effective_maxlangver = ctx->module.impl_minlangver; } else { ctx->module.effective_maxlangver = ctx->module.min_langver; } /* Minimum version */ if (in_impl && ctx->module.impl_minlangver > 0) { ctx->module.effective_minlangver = ctx->module.impl_minlangver; } else { ctx->module.effective_minlangver = ctx->module.min_langver; } } int cslul_ll_start_parsing_dep(struct CSlul *ctx, struct CSlulDepIter *depiter, const char *filename) { assert(depiter->internal != NULL); ctx->parsed_module = depiter->internal->module; start_of_module(ctx, depiter->internal->module); depiter->internal->module->filename = filename; return 1; } void cslul_ll_done_parsing_dep(struct CSlul *ctx) { assert(ctx->parsed_module != NULL); memcpy(&ctx->parsed_module->iface, &ctx->tl, sizeof(struct TopLevels)); ctx->parsed_module = NULL; } void cslul_ll_set_current_filename(struct CSlul *ctx, const char *filename) { ctx->toklen = 0; ctx->tmplen = 0; ctx->tokline = 0; ctx->tokcolumn = 0; ctx->case_insens = 0; ctx->line = 1; ctx->startcolumn = 1; ctx->mbtrailerbytes = 0; ctx->utf8state = 0; ctx->numspaces = 0; ctx->in_multiline_comment = 0; ctx->case_insens = 0; ctx->buffer = NULL; ctx->current_filename = filename; ctx->typedepth = -1; ctx->blockdepth = -1; ctx->exprdepth = -1; if (ctx->phase != CSLUL_P_MODULEHEADER && ctx->phase != CSLUL_P_DEPS) { ctx->reused_token.slul = CSLUL_T_NONE_QUEUED; start_of_code(ctx); } } void start_of_code(struct CSlul *ctx) { ctx->in_moduleheader = 0; ctx->toklen = 0; ctx->tmplen = 0; ctx->tokstate.slul = TDone; ctx->parser.slul.state = PDone; ctx->parser.slul.sincever_state = SVDone; if (ctx->reused_token.slul != CSLUL_T_KW_Since) { ctx->parser.slul.version_line = 0; } ctx->parser.slul.forbidden_quals = 0; ctx->typedepth = -1; ctx->blockdepth = -1; ctx->exprdepth = -1; ctx->generic_param_depth = 0; ctx->current_functype = NULL; ctx->current_block = NULL; ctx->current_sinceversions = NULL; ctx->next_decl_sinceversions = NULL; } void cslul_ll_set_input_buffer(struct CSlul *ctx, const char *buffer, size_t buffer_size, int is_last) { ctx->buffer = buffer; ctx->bufferstart = buffer; ctx->bufferend = &buffer[buffer_size]; ctx->last_buffer = is_last; } int cslul_ll_implsource_iter(struct CSlul *ctx, struct CSlulSrcIter *srciter) { struct CSlulSourceFile *sf = (srciter->filename ? srciter->internal : ctx->first_srcfile); if (!sf) return 0; srciter->filename = node_nameptr(&sf->node); srciter->namelen = sf->node.length; srciter->internal = sf->next; return 1; } int cslul_ll_dependency_iter(struct CSlul *ctx, struct CSlulDepIter *depiter) { struct CSlulDependency *dep = (depiter->module_name ? depiter->internal->next : ctx->module.first_dep); if (!dep) return 0; depiter->module_name = node_nameptr(&dep->node); depiter->modnamelen = dep->node.length; depiter->min_version = dep->min_version; depiter->flags = dep->flags; depiter->internal = dep; return 1; } int cslul_ll_ifacedep_iter(struct CSlul *ctx, struct CSlulDepIter *dep, struct CSlulInterfaceDepIter *iter) { struct CSlulInterfaceDep *ifacedep = (iter->inited ? iter->internal->sincever_next : dep->internal->first_ifacever); (void) ctx; /* currently unused */ if (!ifacedep) return 0; iter->min_version = ifacedep->min_version; iter->since_version = node_nameptr(&ifacedep->node); iter->sinceverlen = ifacedep->node.length; iter->internal = ifacedep; iter->inited = 1; return 1; } int cslul_ll_has_errors(struct CSlul *ctx) { return ctx->has_errors; } int cslul_ll_has_fatal_errors(struct CSlul *ctx) { return ctx->has_fatal_errors; } /** * Sets has_errors and has_fatal_errors. * Returns 1 if the error should be reported. */ static int check_error(struct CSlul *ctx, enum CSlulErrorCode errorcode) { ctx->msgstate.level = lookup_error_level(errorcode); if (ctx->msgstate.level <= CSLUL_L_ERROR) { ctx->has_errors = 1; if (ctx->msgstate.level == CSLUL_L_FATAL) { ctx->has_fatal_errors = 1; } } if (ctx->msgstate.level <= ctx->cfg->msglevel && ctx->cfg->msghandler) { return 1; } else { reset_msgstate(&ctx->msgstate); return 0; } } void internal_error(struct CSlul *ctx, enum CSlulErrorCode errorcode) { error_linecol(ctx, errorcode, 0, 0); } void error_linecol(struct CSlul *ctx, enum CSlulErrorCode errorcode, int line, int column) { message(ctx, errorcode, ctx->current_filename, line, column, 0); } void error_tok(struct CSlul *ctx, enum CSlulErrorCode errorcode) { error_textlen(ctx, errorcode, ctx->tokline, ctx->tokcolumn, ctx->tokval, ctx->toklen); } void error_prevtok_start(struct CSlul *ctx, enum CSlulErrorCode errorcode) { error_linecol(ctx, errorcode, ctx->prev_tok_line, ctx->prev_tok_col); } void error_prevtok_end(struct CSlul *ctx, enum CSlulErrorCode errorcode) { error_linecol(ctx, errorcode, ctx->prev_tok_line, ctx->prev_tok_endcol); } void error_prevtok_line(struct CSlul *ctx, enum CSlulErrorCode errorcode) { error_linecol(ctx, errorcode, ctx->prev_tok_line, 1); } void error_sameline(struct CSlul *ctx, enum CSlulErrorCode errorcode) { if (ctx->tokline == ctx->prev_tok_line || !ctx->prev_tok_line) { error_tok(ctx, errorcode); } else { error_prevtok_end(ctx, errorcode); } } void error_sincever(struct CSlul *ctx, enum CSlulErrorCode errorcode) { error_linecol(ctx, errorcode, ctx->parser.slul.sincekeyword_line, ctx->parser.slul.sincekeyword_column); } void error_expr(struct CSlul *ctx, enum CSlulErrorCode errorcode, const struct ExprRoot *exprroot, const struct ExprNode *subexpr) { message_set_expr(ctx, 0, CSLUL_LT_MAIN, exprroot, subexpr); message_final(ctx, errorcode); } void error_stmt(struct CSlul *ctx, enum CSlulErrorCode errorcode, const struct FuncBody *func, const struct Stmt *stmt) { message_set_stmt(ctx, 0, CSLUL_LT_MAIN, func, stmt); message_final(ctx, errorcode); } void error_text(struct CSlul *ctx, enum CSlulErrorCode errorcode, int line, int column, const char *text) { error_textlen(ctx, errorcode, line, column, text, strlen(text)); } void error_textlen(struct CSlul *ctx, enum CSlulErrorCode errorcode, int line, int column, const char *text, int len) { struct CSlulState *st = &ctx->msgstate; if (!check_error(ctx, errorcode)) return; st->locs[0].filename = ctx->current_filename; st->locs[0].type = CSLUL_LT_MAIN; st->locs[0].line = line; st->locs[0].column = column; st->locs[0].length = len; st->locs[0].text = text; message_final(ctx, errorcode); } void message(struct CSlul *ctx, enum CSlulErrorCode errorcode, const char *filename, int line, int column, int length) { struct CSlulState *st = &ctx->msgstate; if (!check_error(ctx, errorcode)) return; st->locs[0].filename = filename; st->locs[0].type = CSLUL_LT_MAIN; st->locs[0].line = line; st->locs[0].column = column; st->locs[0].length = length; st->locs[0].text = NULL; message_final(ctx, errorcode); } void message_set_token(struct CSlul *ctx, int index, enum CSlulLocationType loctype) { struct CSlulLocation *loc; assert(index < CSLUL_MAX_LOCATIONS); loc = &ctx->msgstate.locs[index]; loc->filename = ctx->current_filename; loc->type = loctype; loc->line = ctx->tokline; loc->column = ctx->tokcolumn; loc->length = ctx->toklen; loc->text = NULL; } void message_set_ident(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const struct TreeNode *ident) { struct CSlulLocation *loc; assert(index < CSLUL_MAX_LOCATIONS); loc = &ctx->msgstate.locs[index]; loc->filename = ident->filename; loc->type = loctype; loc->line = ident->line; loc->column = ident->column; loc->length = ident->length; loc->text = node_nameptr(ident); } void message_set_typedecl(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const struct TypeDecl *decl) { if (!IS_IDENT_DEFINED(decl)) return; if (decl->type.type == T_IMPORTED) { decl = decl->type.u.ident; if (!IS_IDENT_DEFINED(decl)) return; assert(decl->type.type != T_IMPORTED); } message_set_type(ctx, index, loctype, decl, &decl->type); } void message_set_type(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const struct TypeDecl *typedecl, const struct Type *type) { message_set_type_ident(ctx, index, loctype, &typedecl->ident, type); } /** * Includes a type in an error message. The root typedecl is used to * determine the filename, but the line and column are determined from * the type. */ void message_set_type_ident(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const struct TreeNode *ident, const struct Type *type) { struct CSlulLocation *loc; assert(index < CSLUL_MAX_LOCATIONS); loc = &ctx->msgstate.locs[index]; loc->filename = ident->filename; loc->type = loctype; loc->line = type->line; loc->column = type->column; loc->length = ident->length; loc->text = node_nameptr(ident); } void message_set_expr(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const struct ExprRoot *exprroot, const struct ExprNode *subexpr) { struct CSlulLocation *loc; assert(index < CSLUL_MAX_LOCATIONS); loc = &ctx->msgstate.locs[index]; loc->filename = exprroot->filename; loc->type = loctype; loc->line = exprroot->line_start + subexpr->line_offset; loc->column = subexpr->column; /* TODO it would be nice to have the source text also */ loc->length = 0; loc->text = NULL; } void message_set_expr_text(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const struct ExprRoot *exprroot, const struct ExprNode *subexpr, const char *text, int textlen) { struct CSlulLocation *loc; assert(index < CSLUL_MAX_LOCATIONS); loc = &ctx->msgstate.locs[index]; loc->filename = exprroot->filename; loc->type = loctype; loc->line = exprroot->line_start + subexpr->line_offset; loc->column = subexpr->column; loc->length = textlen; loc->text = text; } void message_set_stmt(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const struct FuncBody *func, const struct Stmt *stmt) { struct CSlulLocation *loc; assert(index < CSLUL_MAX_LOCATIONS); loc = &ctx->msgstate.locs[index]; loc->filename = func->filename; loc->type = loctype; loc->line = stmt->line; loc->column = stmt->column; /* TODO it would be nice to have the source text also */ loc->length = 0; loc->text = NULL; } void message_set_filemsg(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const char *filename, const char *text, int textlen) { struct CSlulLocation *loc; assert(index < CSLUL_MAX_LOCATIONS); loc = &ctx->msgstate.locs[index]; loc->filename = filename; loc->type = loctype; loc->line = 0; loc->column = 0; loc->length = textlen; loc->text = text; } void message_set_module(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const struct Module *mod) { message_set_filemsg(ctx, index, loctype, mod->filename, mod->name, mod->namelen); } void message_set_text(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const char *text) { message_set_textlen(ctx, index, loctype, text, strlen(text)); } void message_set_textlen(struct CSlul *ctx, int index, enum CSlulLocationType loctype, const char *text, size_t textlen) { struct CSlulLocation *loc; assert(index < CSLUL_MAX_LOCATIONS); loc = &ctx->msgstate.locs[index]; loc->filename = NULL; loc->type = loctype; loc->line = 0; loc->column = 0; loc->length = textlen; loc->text = text; } void message_final(struct CSlul *ctx, enum CSlulErrorCode errorcode) { struct CSlulState *st = &ctx->msgstate; if (!check_error(ctx, errorcode)) return; st->cfg = NULL; st->ctx = ctx; st->phase = ctx->phase; st->errorcode = errorcode; ctx->cfg->msghandler(st); reset_msgstate(st); } #define FUZZ_DUMMY_FILE ((CSlulFile) -1) CSlulFile silent_open(struct CSlul *ctx, const char *filename, const char *mode) { #ifdef MOCKED_STDIO assert(filename != NULL && *filename); if (*mode == 'w') return FUZZ_DUMMY_FILE; #endif if (ctx->cfg->params.fptr_fopen) { return ctx->cfg->params.fptr_fopen(filename, mode); } else { return fopen(filename, mode); } } CSlulFile ctx_fopen(struct CSlul *ctx, const char *filename, const char *mode) { CSlulFile file = silent_open(ctx, filename, mode); if (!file) { message(ctx, *mode == 'w' ? CSLUL_E_CREATEFILE : CSLUL_E_OPENFILE, filename, 0, 0, 0); } return file; } CSlulFile ctx_createexec(struct CSlul *ctx, const char *filename) { #ifdef MOCKED_STDIO assert(filename != NULL && *filename); return FUZZ_DUMMY_FILE; #else CSlulFile file; if (ctx->cfg->params.fptr_createexec) { file = ctx->cfg->params.fptr_createexec(filename); } else { file = slul_createexec(filename); } if (!file) { message(ctx, CSLUL_E_CREATEFILE, filename, 0, 0, 0); } return file; #endif } #ifdef MOCKED_STDIO #define SKIP_FUZZ_DUMMY if (file == FUZZ_DUMMY_FILE) return 0; #define SKIP_FUZZ_DUMMY_B if (file == FUZZ_DUMMY_FILE) { \ assert(buffer != NULL); \ assert(nmemb == 1); \ ((volatile char*)buffer)[0]; \ ((volatile char*)buffer)[size]; \ return 0; \ } #else #define SKIP_FUZZ_DUMMY #define SKIP_FUZZ_DUMMY_B #endif int ctx_fclose(struct CSlul *ctx, CSlulFile file) { SKIP_FUZZ_DUMMY return ctx->cfg->params.fptr_fclose ? ctx->cfg->params.fptr_fclose(file) : fclose(file); } int ctx_ferror(struct CSlul *ctx, CSlulFile file) { SKIP_FUZZ_DUMMY return ctx->cfg->params.fptr_ferror ? ctx->cfg->params.fptr_ferror(file) : ferror(file); } int ctx_remove(struct CSlul *ctx, const char *filename) { SKIP_FUZZ_DUMMY return ctx->cfg->params.fptr_remove ? ctx->cfg->params.fptr_remove(filename) : remove(filename); } size_t ctx_fread(struct CSlul *ctx, void *buffer, size_t size, size_t nmemb, CSlulFile file) { SKIP_FUZZ_DUMMY_B return ctx->cfg->params.fptr_fread ? ctx->cfg->params.fptr_fread(buffer, size, nmemb, file) : fread(buffer, size, nmemb, file); } size_t ctx_fwrite(struct CSlul *ctx, const void *buffer, size_t size, size_t nmemb, CSlulFile file) { SKIP_FUZZ_DUMMY_B return ctx->cfg->params.fptr_fwrite ? ctx->cfg->params.fptr_fwrite(buffer, size, nmemb, file) : fwrite(buffer, size, nmemb, file); } int ctx_mkdir(struct CSlul *ctx, const char *filename) { #ifdef MOCKED_STDIO return 0; #else return ctx->cfg->params.fptr_mkdir ? ctx->cfg->params.fptr_mkdir(filename) : slul_mkdir(filename); #endif } int ctx_dropprivs(struct CSlul *ctx) { #ifdef MOCKED_STDIO return 0; #else return ctx->cfg->params.fptr_dropprivs ? ctx->cfg->params.fptr_dropprivs() : 1; #endif }