// Evaluate the given ifdef condition for the given config, or our target build // if no config is given. static bool cond_eval(ast_t* ast, buildflagset_t* config, bool release, pass_opt_t* opt) { pony_assert(ast != NULL); switch(ast_id(ast)) { case TK_NONE: // No condition to evaluate return true; case TK_IFDEFAND: { AST_GET_CHILDREN(ast, left, right); return cond_eval(left, config, release, opt) && cond_eval(right, config, release, opt); } case TK_IFDEFOR: { AST_GET_CHILDREN(ast, left, right); return cond_eval(left, config, release, opt) || cond_eval(right, config, release, opt); } case TK_IFDEFNOT: return !cond_eval(ast_child(ast), config, release, opt); case TK_IFDEFFLAG: { const char* name = ast_name(ast_child(ast)); if(config != NULL) // Lookup flag in given config. return buildflagset_get(config, name); // No config given, lookup platform flag for current build. bool val; if(os_is_target(name, release, &val, opt)) { return val; } // Not a platform flag, must be a user flag. return is_build_flag_defined(name); } default: pony_assert(0); return false; } }
bool ifdef_cond_normalise(ast_t** astp, pass_opt_t* opt) { pony_assert(astp != NULL); pony_assert(*astp != NULL); if(ast_id(*astp) == TK_NONE) // No condition to normalise. return true; cond_normalise(astp); // Check whether condition can ever be true. buildflagset_t* config = buildflagset_create(); find_flags_in_cond(*astp, config); check_config_count(config, *astp); buildflagset_startenum(config); while(buildflagset_next(config)) { if(cond_eval(*astp, config, false, opt)) { // Condition is true for this config. buildflagset_free(config); return true; } } // Condition isn't true for any configs. buildflagset_free(config); return false; }
bool ifdef_cond_eval(ast_t* ast, pass_opt_t* opt) { pony_assert(ast != NULL); if(ast_id(ast) == TK_NONE) // No condition to evaluate return true; return cond_eval(ast, NULL, opt->release, opt); }
// Find the declaration for the given FFI call that is valid for the given // build config, within the specified ifdef condition (NULL for none). // Returns: true on success, false on failure. static bool find_ffi_decl(ast_t* ast, ast_t* package, ast_t* ifdef_cond, ast_t** out_decl, pass_opt_t* opt) { pony_assert(ast != NULL); pony_assert(package != NULL); pony_assert(out_decl != NULL); const char* ffi_name = ast_name(ast_child(ast)); buildflagset_t* config = buildflagset_create(); // Find all the relevant build flags. if(ifdef_cond != NULL) find_flags_in_cond(ifdef_cond, config); if(!find_decl_flags(package, ffi_name, config)) { // There are no declarations for this FFI. buildflagset_free(config); *out_decl = NULL; return true; } check_config_count(config, ast); ffi_decl_t decl_info = { NULL, NULL }; buildflagset_startenum(config); while(buildflagset_next(config)) { if(ifdef_cond == NULL || cond_eval(ifdef_cond, config, false, opt)) { // ifdef condition true, or not in an ifdef. // Look for valid FFI declaration. if(!find_decl_for_config(ast, package, ffi_name, config, &decl_info, opt)) { // Config has failed. buildflagset_free(config); return false; } pony_assert(decl_info.decl != NULL); } } buildflagset_free(config); pony_assert(decl_info.decl != NULL); *out_decl = decl_info.decl; return true; }
// Find the declaration for the specified FFI name that is valid for the given // build config. // The declaration found is stored in the given decl info argument. // There must be exactly one valid declaration. // If a declaration is already specified in the given decl info this must be // the same as the one found. // All other cases are errors, which will be reported by this function. // Returns: true on success, false on failure. static bool find_decl_for_config(ast_t* call, ast_t* package, const char* ffi_name, buildflagset_t* config, ffi_decl_t* decl_info, pass_opt_t* opt) { pony_assert(call != NULL); pony_assert(package != NULL); pony_assert(ffi_name != NULL); pony_assert(config != NULL); pony_assert(decl_info != NULL); bool had_valid_decl = false; // FFI declarations are package wide, so check all modules in package. for(ast_t* m = ast_child(package); m != NULL; m = ast_sibling(m)) { // Find all the FFI declarations in this module. for(ast_t* use = ast_child(m); use != NULL; use = ast_sibling(use)) { if(ast_id(use) == TK_USE) { AST_GET_CHILDREN(use, alias, decl, guard); if(ast_id(decl) == TK_FFIDECL && ffi_name == ast_name(ast_child(decl))) { // We have an FFI declaration for the specified name. if(cond_eval(guard, config, false, opt)) { // This declaration is valid for this config. had_valid_decl = true; if(decl_info->decl != NULL) { // We already have a decalaration, is it the same one? if(decl_info->decl != decl) { ast_error(opt->check.errors, call, "Multiple possible declarations for FFI call"); ast_error_continue(opt->check.errors, decl_info->decl, "This declaration valid for config: %s", decl_info->config); ast_error_continue(opt->check.errors, decl, "This declaration valid for config: %s", buildflagset_print(config)); return false; } } else { // Store the declaration found. // We store the config string incase we need it for error // messages later. We stringtab it because the version we are // given is in a temporary buffer. decl_info->decl = decl; decl_info->config = stringtab(buildflagset_print(config)); } } } } } } if(!had_valid_decl) { ast_error(opt->check.errors, call, "No FFI declaration found for '%s' in config: %s", ffi_name, buildflagset_print(config)); return false; } return true; }
int mdb_cond_evaluate(mdb_table_t *tbl, mqi_cond_entry_t **cond_ptr,void *data) { static int precedence[mqi_operator_max] = { [ mqi_done ] = 0, [ mqi_begin ] = 1, [ mqi_and ] = 2, [ mqi_or ] = 3, [ mqi_less ] = 4, [ mqi_leq ] = 4, [ mqi_eq ] = 4, [ mqi_geq ] = 4, [ mqi_gt ] = 4, [ mqi_not ] = 5 }; mqi_cond_entry_t *cond = *cond_ptr; cond_stack_t stack[256] = { [0] = { precedence[mqi_begin], { .operator = mqi_begin } } }; cond_stack_t *sp = stack + 1; cond_stack_t *lastop = stack; int result; int pr; MDB_CHECKARG(cond && data, -1); for (;;) { switch (cond->type) { case mqi_operator: pr = precedence[cond->u.operator]; sp += cond_eval(sp, lastop, pr); switch (cond->u.operator) { case mqi_begin: cond++; result = mdb_cond_evaluate(tbl, &cond, data); cond++; sp->data.v.integer = result >= 0 ? result : 0; sp->precedence = PRECEDENCE_DATA; sp->data.type = mqi_integer; sp++; break; case mqi_end: *cond_ptr = cond+1; sp--; if (sp->precedence < PRECEDENCE_DATA || sp->data.type != mqi_integer) { errno = ENOENT; return -1; } return sp->data.v.integer ? 1 : 0; case mqi_and: case mqi_or: case mqi_less: case mqi_leq: case mqi_eq: case mqi_geq: case mqi_gt: case mqi_not: lastop = sp++; lastop->precedence = pr; lastop->operator = cond->u.operator; cond++; break; default: break; } break; case mqi_variable: case mqi_column: sp += cond_get_data(sp, cond, tbl->columns, data); cond++; break; default: errno = EINVAL; return -1; } } /* for ;; */ }