static void cg_user_func()

in sources/cg_c.c [6259:6364]


static void cg_user_func(ast_node *ast, charbuf *is_null, charbuf *value) {
  Contract(is_ast_call(ast));
  EXTRACT_ANY_NOTNULL(name_ast, ast->left);
  EXTRACT_STRING(name, name_ast);
  EXTRACT_NOTNULL(call_arg_list, ast->right);
  EXTRACT(arg_list, call_arg_list->right);

  ast_node *params = NULL;
  ast_node *func_stmt = find_func(name);
  CSTR func_name = NULL;

  bool_t proc_as_func = 0;
  bool_t dml_proc = 0;

  if (func_stmt) {
    EXTRACT_STRING(fname, func_stmt->left);
    params = get_func_params(func_stmt);
    func_name = fname;
  }
  else {
    // has to be one of these two, already validated
    ast_node *proc_stmt = find_proc(name);
    Invariant(proc_stmt);
    params = get_proc_params(proc_stmt);
    ast_node *proc_name_ast = get_proc_name(proc_stmt);
    EXTRACT_STRING(pname, proc_name_ast);
    func_name = pname;
    proc_as_func = 1;
    dml_proc = is_dml_proc(proc_stmt->sem->sem_type);
  }

  sem_t sem_type_result = ast->sem->sem_type;

  // The answer will be stored in this scratch variable, any type is possible
  CG_SETUP_RESULT_VAR(ast, sem_type_result);
  CHARBUF_OPEN(invocation);
  CG_CHARBUF_OPEN_SYM(func_sym, func_name);

  if (dml_proc) {
    // at least one arg for the out arg so add _db_ with comma
    bprintf(&invocation, "_rc_ = %s(_db_, ", func_sym.ptr);
  }
  else {
    bprintf(&invocation, "%s(", func_sym.ptr);
  }

  ast_node *item;
  for (item = arg_list; item; item = item->right, params = params->right) {
    EXTRACT_ANY(arg, item->left);
    sem_t sem_type_arg = arg->sem->sem_type;

    EXTRACT_NOTNULL(param, params->left);
    sem_t sem_type_param = param->sem->sem_type;

    cg_emit_one_arg(arg, sem_type_param, sem_type_arg, &invocation);

    // if any more items in the list or the out arg still pending, we need comma
    if (item->right || proc_as_func) {
      bprintf(&invocation, ", ");
    }
  }

  if (!item && params) {
    // The only way this happens is when calling a stored proc like a function
    // using the last arg as the return type.
    Invariant(proc_as_func);
    Invariant(!params->right);
    EXTRACT_NOTNULL(param, params->left);
    sem_t param_type = param->sem->sem_type;
    Invariant(is_out_parameter(param_type));
    Invariant(!is_in_parameter(param_type));

    // the result variable is not an in/out arg, it's just a regular local
    // it's otherwise the same as the paramater by consruction
    sem_t arg_type = param_type & sem_not(SEM_TYPE_OUT_PARAMETER|SEM_TYPE_IN_PARAMETER);

    cg_release_out_arg_before_call(arg_type, param_type, result_var.ptr);
    bprintf(&invocation, "&%s", result_var.ptr);
  }

  bprintf(&invocation, ")");

  // Now store the result of the call.
  // the only trick here is we have to make sure we honor create semantics
  // otherwise we can just copy the data since the variable is for sure
  // an exact match for the call return by construction.

  if (proc_as_func) {
    // just do the function call, the result variable assignment happens as part of the call
    bprintf(cg_main_output, "%s;\n", invocation.ptr);
    if (dml_proc) {
      // cascade the failure
      cg_error_on_not_sqlite_ok();
    }
  }
  else if (is_create_func(func_stmt->sem->sem_type)) {
    cg_copy_for_create(cg_main_output, result_var.ptr, func_stmt->sem->sem_type, invocation.ptr);
  }
  else {
    cg_copy(cg_main_output, result_var.ptr, func_stmt->sem->sem_type, invocation.ptr);
  }

  CHARBUF_CLOSE(func_sym);
  CHARBUF_CLOSE(invocation);
  CG_CLEANUP_RESULT_VAR();  // this will restore the scratch stack for us
}