mirror of
https://github.com/meekrosoft/fff
synced 2026-01-23 00:15:59 +01:00
* Fixed some return statements that were generated unconditionally, leading to unreachable return statements in some cases. * Regenerated fff.h
698 lines
24 KiB
Ruby
698 lines
24 KiB
Ruby
|
|
# fakegen.rb
|
|
# A simple code generator to create some C macros for defining test fake functions
|
|
|
|
|
|
$cpp_output = true
|
|
$MAX_ARGS = 20
|
|
$DEFAULT_ARG_HISTORY = 50
|
|
$MAX_CALL_HISTORY = 50
|
|
|
|
def license
|
|
File.foreach("#{__dir__}/LICENSE") { |line| putd line }
|
|
end
|
|
|
|
def include_dependencies
|
|
putd "#include <stdarg.h>"
|
|
putd "#include <string.h> /* For memset and memcpy */"
|
|
puts
|
|
end
|
|
|
|
def output_constants
|
|
putd "#define FFF_MAX_ARGS (#{$MAX_ARGS}u)"
|
|
putd "#ifndef FFF_ARG_HISTORY_LEN"
|
|
indent {
|
|
putd "#define FFF_ARG_HISTORY_LEN (#{$DEFAULT_ARG_HISTORY}u)"
|
|
}
|
|
putd "#endif"
|
|
putd "#ifndef FFF_CALL_HISTORY_LEN"
|
|
indent {
|
|
putd "#define FFF_CALL_HISTORY_LEN (#{$MAX_CALL_HISTORY}u)"
|
|
}
|
|
putd "#endif"
|
|
putd "#ifndef FFF_GCC_FUNCTION_ATTRIBUTES"
|
|
indent {
|
|
putd "#define FFF_GCC_FUNCTION_ATTRIBUTES"
|
|
}
|
|
putd "#endif"
|
|
|
|
end
|
|
|
|
|
|
def output_default_function_pointer_macro(has_calling_conventions)
|
|
name = has_calling_conventions ? "(CALLING_CONVENTION *FUNCNAME)" : "(*FUNCNAME)"
|
|
calling_conv = has_calling_conventions ? ", CALLING_CONVENTION" : ""
|
|
putd "#ifndef CUSTOM_FFF_FUNCTION_TEMPLATE"
|
|
putd_backslash "#define CUSTOM_FFF_FUNCTION_TEMPLATE(RETURN#{calling_conv}, FUNCNAME, ...)"
|
|
indent {
|
|
putd "RETURN#{name}(__VA_ARGS__)"
|
|
}
|
|
putd "#endif /* CUSTOM_FFF_FUNCTION_TEMPLATE */"
|
|
end
|
|
|
|
|
|
|
|
|
|
# ------ Helper macros to use internally ------ #
|
|
def output_internal_helper_macros
|
|
putd "/* -- INTERNAL HELPER MACROS -- */"
|
|
|
|
define_return_sequence_helper
|
|
define_custom_fake_sequence_helper
|
|
define_reset_fake_macro
|
|
define_declare_arg_helper
|
|
define_declare_all_func_common_helper
|
|
define_declare_return_value_history
|
|
define_save_arg_helper
|
|
define_room_for_more_history
|
|
define_save_ret_history_helper
|
|
define_save_arg_history_helper
|
|
define_history_dropped_helper
|
|
define_value_function_variables_helper
|
|
define_custom_fake_seq_variables_helper
|
|
define_increment_call_count_helper
|
|
define_return_fake_result_helper
|
|
define_extern_c_helper
|
|
define_reset_fake_helper
|
|
|
|
putd "/* -- END INTERNAL HELPER MACROS -- */"
|
|
puts
|
|
end
|
|
|
|
def define_return_sequence_helper
|
|
putd_backslash "#define SET_RETURN_SEQ(FUNCNAME, ARRAY_POINTER, ARRAY_LEN)"
|
|
indent {
|
|
putd_backslash "FUNCNAME##_fake.return_val_seq = ARRAY_POINTER;"
|
|
putd "FUNCNAME##_fake.return_val_seq_len = ARRAY_LEN;"
|
|
}
|
|
end
|
|
|
|
def define_custom_fake_sequence_helper
|
|
putd_backslash "#define SET_CUSTOM_FAKE_SEQ(FUNCNAME, ARRAY_POINTER, ARRAY_LEN)"
|
|
indent {
|
|
putd_backslash "FUNCNAME##_fake.custom_fake_seq = ARRAY_POINTER;"
|
|
putd "FUNCNAME##_fake.custom_fake_seq_len = ARRAY_LEN;"
|
|
}
|
|
end
|
|
|
|
def define_reset_fake_macro
|
|
puts
|
|
putd "/* Defining a function to reset a fake function */"
|
|
putd_backslash "#define RESET_FAKE(FUNCNAME) {"
|
|
indent {
|
|
putd_backslash "FUNCNAME##_reset();"
|
|
}
|
|
putd_backslash "}"
|
|
puts
|
|
end
|
|
|
|
def define_declare_arg_helper
|
|
puts
|
|
putd_backslash "#define DECLARE_ARG(type, n, FUNCNAME)"
|
|
indent {
|
|
putd_backslash "type arg##n##_val;"
|
|
putd "type arg##n##_history[FFF_ARG_HISTORY_LEN];"
|
|
}
|
|
end
|
|
|
|
def define_declare_all_func_common_helper
|
|
puts
|
|
putd_backslash "#define DECLARE_ALL_FUNC_COMMON"
|
|
indent {
|
|
putd_backslash "unsigned int call_count;"
|
|
putd_backslash "unsigned int arg_history_len;"
|
|
putd_backslash "unsigned int arg_histories_dropped;"
|
|
}
|
|
end
|
|
|
|
def define_declare_return_value_history
|
|
putd ""
|
|
putd_backslash "#define DECLARE_RETURN_VALUE_HISTORY(RETURN_TYPE)"
|
|
indent {
|
|
putd "RETURN_TYPE return_val_history[FFF_ARG_HISTORY_LEN];"
|
|
}
|
|
end
|
|
|
|
def define_save_arg_helper
|
|
puts
|
|
putd_backslash "#define SAVE_ARG(FUNCNAME, n)"
|
|
indent {
|
|
putd "memcpy((void*)&FUNCNAME##_fake.arg##n##_val, (void*)&arg##n, sizeof(arg##n));"
|
|
}
|
|
end
|
|
|
|
def define_save_ret_history_helper
|
|
putd ""
|
|
putd_backslash "#define SAVE_RET_HISTORY(FUNCNAME, RETVAL)"
|
|
indent {
|
|
putd_backslash "if ((FUNCNAME##_fake.call_count - 1) < FFF_ARG_HISTORY_LEN)"
|
|
indent {
|
|
putd_backslash "memcpy((void *)&FUNCNAME##_fake.return_val_history[FUNCNAME##_fake.call_count - 1], (const void *) &RETVAL, sizeof(RETVAL));"
|
|
}
|
|
}
|
|
end
|
|
|
|
def define_room_for_more_history
|
|
puts
|
|
putd_backslash "#define ROOM_FOR_MORE_HISTORY(FUNCNAME)"
|
|
indent {
|
|
putd "FUNCNAME##_fake.call_count < FFF_ARG_HISTORY_LEN"
|
|
}
|
|
end
|
|
|
|
def define_save_arg_history_helper
|
|
puts
|
|
putd_backslash "#define SAVE_ARG_HISTORY(FUNCNAME, ARGN)"
|
|
indent {
|
|
putd "memcpy((void*)&FUNCNAME##_fake.arg##ARGN##_history[FUNCNAME##_fake.call_count], (void*)&arg##ARGN, sizeof(arg##ARGN));"
|
|
}
|
|
end
|
|
|
|
def define_history_dropped_helper
|
|
puts
|
|
putd_backslash "#define HISTORY_DROPPED(FUNCNAME)"
|
|
indent {
|
|
putd "FUNCNAME##_fake.arg_histories_dropped++"
|
|
}
|
|
end
|
|
|
|
def define_value_function_variables_helper
|
|
puts
|
|
putd_backslash "#define DECLARE_VALUE_FUNCTION_VARIABLES(RETURN_TYPE)"
|
|
indent {
|
|
putd_backslash "RETURN_TYPE return_val;"
|
|
putd_backslash "int return_val_seq_len;"
|
|
putd_backslash "int return_val_seq_idx;"
|
|
putd_backslash "RETURN_TYPE * return_val_seq;"
|
|
}
|
|
end
|
|
|
|
def define_custom_fake_seq_variables_helper
|
|
puts
|
|
putd_backslash "#define DECLARE_CUSTOM_FAKE_SEQ_VARIABLES"
|
|
indent {
|
|
putd_backslash "int custom_fake_seq_len;"
|
|
putd_backslash "int custom_fake_seq_idx;"
|
|
}
|
|
end
|
|
|
|
def define_increment_call_count_helper
|
|
puts
|
|
putd_backslash "#define INCREMENT_CALL_COUNT(FUNCNAME)"
|
|
indent {
|
|
putd "FUNCNAME##_fake.call_count++"
|
|
}
|
|
end
|
|
|
|
def define_return_fake_result_helper
|
|
puts
|
|
putd_backslash "#define RETURN_FAKE_RESULT(FUNCNAME)"
|
|
indent {
|
|
putd_backslash "if (FUNCNAME##_fake.return_val_seq_len){ /* then its a sequence */"
|
|
indent {
|
|
putd_backslash "if(FUNCNAME##_fake.return_val_seq_idx < FUNCNAME##_fake.return_val_seq_len) {"
|
|
indent {
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_idx])"
|
|
putd_backslash "return FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_idx++];"
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_len-1])"
|
|
putd_backslash "return FUNCNAME##_fake.return_val_seq[FUNCNAME##_fake.return_val_seq_len-1]; /* return last element */"
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, FUNCNAME##_fake.return_val)"
|
|
putd_backslash "return FUNCNAME##_fake.return_val;"
|
|
}
|
|
end
|
|
|
|
def define_extern_c_helper
|
|
puts
|
|
putd "#ifdef __cplusplus"
|
|
indent {
|
|
putd "#define FFF_EXTERN_C extern \"C\"{"
|
|
putd "#define FFF_END_EXTERN_C } "
|
|
}
|
|
putd "#else /* ansi c */"
|
|
indent {
|
|
putd "#define FFF_EXTERN_C "
|
|
putd "#define FFF_END_EXTERN_C "
|
|
}
|
|
putd "#endif /* cpp/ansi c */"
|
|
end
|
|
|
|
def define_reset_fake_helper
|
|
puts
|
|
putd_backslash "#define DEFINE_RESET_FUNCTION(FUNCNAME)"
|
|
indent {
|
|
putd_backslash "void FUNCNAME##_reset(void){"
|
|
indent {
|
|
putd_backslash "memset((void*)&FUNCNAME##_fake, 0, sizeof(FUNCNAME##_fake) - sizeof(FUNCNAME##_fake.custom_fake) - sizeof(FUNCNAME##_fake.custom_fake_seq));"
|
|
putd_backslash "FUNCNAME##_fake.custom_fake = NULL;"
|
|
putd_backslash "FUNCNAME##_fake.custom_fake_seq = NULL;"
|
|
putd_backslash "FUNCNAME##_fake.arg_history_len = FFF_ARG_HISTORY_LEN;"
|
|
}
|
|
putd "}"
|
|
}
|
|
end
|
|
# ------ End Helper macros ------ #
|
|
|
|
#fakegen helpers to print at levels of indentation
|
|
$current_depth = 0
|
|
def putd(str)
|
|
$current_depth.times {|not_used| print " "}
|
|
puts str
|
|
end
|
|
|
|
#multiline putd which adds a \ at the end of the generated macro
|
|
def putd_backslash str
|
|
putd(str + " \\")
|
|
end
|
|
|
|
def pushd
|
|
$current_depth = $current_depth + 4
|
|
end
|
|
|
|
def popd
|
|
$current_depth = $current_depth - 4
|
|
end
|
|
|
|
def indent
|
|
pushd
|
|
yield
|
|
popd
|
|
end
|
|
|
|
def output_macro(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
|
|
vararg_name = has_varargs ? "_VARARG" : ""
|
|
fake_macro_name = is_value_function ? "FAKE_VALUE_FUNC#{arg_count}#{vararg_name}" : "FAKE_VOID_FUNC#{arg_count}#{vararg_name}"
|
|
declare_macro_name = "DECLARE_#{fake_macro_name}"
|
|
define_macro_name = "DEFINE_#{fake_macro_name}"
|
|
saved_arg_count = arg_count - (has_varargs ? 1 : 0)
|
|
return_type = is_value_function ? "RETURN_TYPE" : ""
|
|
|
|
puts
|
|
output_macro_header(declare_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
|
indent {
|
|
output_variables(saved_arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
}
|
|
|
|
puts
|
|
output_macro_header(define_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
|
indent {
|
|
putd_backslash "FUNCNAME##_Fake FUNCNAME##_fake;"
|
|
putd_backslash function_signature(saved_arg_count, has_varargs, has_calling_conventions, is_value_function) + "{"
|
|
indent {
|
|
output_function_body(saved_arg_count, has_varargs, is_value_function)
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "DEFINE_RESET_FUNCTION(FUNCNAME)"
|
|
}
|
|
|
|
puts
|
|
|
|
output_macro_header(fake_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
|
indent {
|
|
putd macro_signature_for(declare_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
|
putd macro_signature_for(define_macro_name, saved_arg_count, has_varargs, has_calling_conventions, return_type)
|
|
puts
|
|
}
|
|
end
|
|
|
|
def output_macro_header(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
|
output_macro_name(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
|
end
|
|
|
|
# #define #macro_name(RETURN_TYPE, FUNCNAME, ARG0,...)
|
|
def output_macro_name(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
|
putd "#define " + macro_signature_for(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
|
end
|
|
|
|
# #macro_name(RETURN_TYPE, FUNCNAME, ARG0,...) \
|
|
def macro_signature_for(macro_name, arg_count, has_varargs, has_calling_conventions, return_type)
|
|
parameter_list = "#{macro_name}("
|
|
if return_type != ""
|
|
parameter_list += return_type
|
|
parameter_list += ", "
|
|
end
|
|
parameter_list += "CALLING_CONVENTION, " if (has_calling_conventions)
|
|
parameter_list += "FUNCNAME"
|
|
|
|
arg_count.times { |i| parameter_list += ", ARG#{i}_TYPE" }
|
|
|
|
parameter_list += ", ..." if has_varargs
|
|
|
|
parameter_list += ") \\"
|
|
|
|
parameter_list
|
|
end
|
|
|
|
def output_variables(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
in_struct{
|
|
arg_count.times { |argN|
|
|
putd_backslash "DECLARE_ARG(ARG#{argN}_TYPE, #{argN}, FUNCNAME)"
|
|
}
|
|
putd_backslash "DECLARE_ALL_FUNC_COMMON"
|
|
putd_backslash "DECLARE_VALUE_FUNCTION_VARIABLES(RETURN_TYPE)" unless not is_value_function
|
|
putd_backslash "DECLARE_RETURN_VALUE_HISTORY(RETURN_TYPE)" unless not is_value_function
|
|
putd_backslash "DECLARE_CUSTOM_FAKE_SEQ_VARIABLES"
|
|
output_custom_function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
output_custom_function_array(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
}
|
|
putd_backslash "extern FUNCNAME##_Fake FUNCNAME##_fake;"
|
|
putd_backslash "void FUNCNAME##_reset(void);"
|
|
putd_backslash function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function) + ";"
|
|
end
|
|
|
|
#example: ARG0_TYPE arg0, ARG1_TYPE arg1
|
|
def arg_val_list(args_count)
|
|
return "void" if (args_count == 0)
|
|
arguments = []
|
|
args_count.times { |i| arguments << "ARG#{i}_TYPE arg#{i}" }
|
|
arguments.join(", ")
|
|
end
|
|
|
|
#example: ARG0_TYPE, ARG1_TYPE
|
|
def arg_type_list(args_count)
|
|
return "void" if (args_count == 0)
|
|
arguments = []
|
|
args_count.times { |i| arguments << "ARG#{i}_TYPE" }
|
|
arguments.join(", ")
|
|
end
|
|
|
|
#example: arg0, arg1
|
|
def arg_list(args_count)
|
|
arguments = []
|
|
args_count.times { |i| arguments << "arg#{i}" }
|
|
arguments.join(", ")
|
|
end
|
|
|
|
# RETURN_TYPE (*custom_fake)(ARG0_TYPE arg0);\
|
|
# OR
|
|
# RETURN_TYPE (CALLING_CONVENTION *custom_fake)(ARG0_TYPE arg0);\
|
|
#
|
|
# void (*custom_fake)(ARG0_TYPE arg0, ARG1_TYPE arg1, ARG2_TYPE arg2);\
|
|
def output_custom_function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
|
ap_list = has_varargs ? ", va_list ap" : ""
|
|
calling_conv = has_calling_conventions ? ", CALLING_CONVENTION" : ""
|
|
putd_backslash "CUSTOM_FFF_FUNCTION_TEMPLATE(#{return_type}#{calling_conv}, custom_fake, #{arg_type_list(arg_count)}#{ap_list});"
|
|
end
|
|
|
|
def output_custom_function_array(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
|
ap_list = has_varargs ? ", va_list ap" : ""
|
|
calling_conv = has_calling_conventions ? ", CALLING_CONVENTION" : ""
|
|
putd_backslash "CUSTOM_FFF_FUNCTION_TEMPLATE(#{return_type}#{calling_conv}, *custom_fake_seq, #{arg_type_list(arg_count)}#{ap_list});"
|
|
end
|
|
|
|
# example: RETURN_TYPE FUNCNAME(ARG0_TYPE arg0, ARG1_TYPE arg1)
|
|
# OR
|
|
# RETURN_TYPE CALLING_CONVENTION FUNCNAME(ARG0_TYPE arg0, ARG1_TYPE arg1)
|
|
def function_signature(arg_count, has_varargs, has_calling_conventions, is_value_function)
|
|
return_type = is_value_function ? "RETURN_TYPE" : "void"
|
|
varargs = has_varargs ? ", ..." : ""
|
|
calling_conventions = has_calling_conventions ?
|
|
"#{return_type} FFF_GCC_FUNCTION_ATTRIBUTES CALLING_CONVENTION FUNCNAME(#{arg_val_list(arg_count)}#{varargs})" :
|
|
"#{return_type} FFF_GCC_FUNCTION_ATTRIBUTES FUNCNAME(#{arg_val_list(arg_count)}#{varargs})"
|
|
end
|
|
|
|
def output_function_body(arg_count, has_varargs, is_value_function)
|
|
arg_count.times { |i| putd_backslash "SAVE_ARG(FUNCNAME, #{i});" }
|
|
putd_backslash "if(ROOM_FOR_MORE_HISTORY(FUNCNAME)){"
|
|
indent {
|
|
arg_count.times { |i| putd_backslash "SAVE_ARG_HISTORY(FUNCNAME, #{i});" }
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "else{"
|
|
indent {
|
|
putd_backslash "HISTORY_DROPPED(FUNCNAME);"
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "INCREMENT_CALL_COUNT(FUNCNAME);"
|
|
putd_backslash "REGISTER_CALL(FUNCNAME);"
|
|
|
|
if has_varargs
|
|
return_type = is_value_function ? "return " : ""
|
|
putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_len){ /* a sequence of custom fakes */"
|
|
indent {
|
|
putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_idx < FUNCNAME##_fake.custom_fake_seq_len){"
|
|
indent {
|
|
putd_backslash "va_list ap;"
|
|
putd_backslash "va_start(ap, arg#{arg_count-1});"
|
|
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)}, ap);" unless not is_value_function
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "va_end(ap);" unless not is_value_function
|
|
putd_backslash "return ret;" unless not is_value_function
|
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)}, ap);" unless is_value_function
|
|
putd_backslash "va_end(ap);" unless is_value_function
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "else{"
|
|
indent {
|
|
putd_backslash "va_list ap;"
|
|
putd_backslash "va_start(ap, arg#{arg_count-1});"
|
|
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)}, ap);" unless not is_value_function
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "va_end(ap);" unless not is_value_function
|
|
putd_backslash "return ret;" unless not is_value_function
|
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)}, ap);" unless is_value_function
|
|
putd_backslash "va_end(ap);" unless is_value_function
|
|
}
|
|
putd_backslash "}"
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "if(FUNCNAME##_fake.custom_fake){"
|
|
indent {
|
|
putd_backslash "RETURN_TYPE ret;" if is_value_function
|
|
putd_backslash "va_list ap;"
|
|
putd_backslash "va_start(ap, arg#{arg_count-1});"
|
|
}
|
|
custom_fake_call = "FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)}, ap);"
|
|
indent {
|
|
if is_value_function
|
|
putd_backslash "ret = #{custom_fake_call}"
|
|
else
|
|
putd_backslash "#{custom_fake_call}"
|
|
end
|
|
putd_backslash "va_end(ap);"
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "return ret;" if is_value_function
|
|
}
|
|
putd_backslash "}"
|
|
else
|
|
return_type = is_value_function ? "return " : ""
|
|
putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_len){ /* a sequence of custom fakes */"
|
|
indent {
|
|
putd_backslash "if (FUNCNAME##_fake.custom_fake_seq_idx < FUNCNAME##_fake.custom_fake_seq_len){"
|
|
indent {
|
|
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)});" unless not is_value_function
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "return ret;" unless not is_value_function
|
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_idx++](#{arg_list(arg_count)});" unless is_value_function
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "else{"
|
|
indent {
|
|
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)});" unless not is_value_function
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "return ret;" unless not is_value_function
|
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake_seq[FUNCNAME##_fake.custom_fake_seq_len-1](#{arg_list(arg_count)});" unless is_value_function
|
|
}
|
|
putd_backslash "}"
|
|
}
|
|
putd_backslash "}"
|
|
putd_backslash "if (FUNCNAME##_fake.custom_fake != NULL){ "
|
|
indent {
|
|
putd_backslash "RETURN_TYPE ret = FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)});" unless not is_value_function
|
|
putd_backslash "SAVE_RET_HISTORY(FUNCNAME, ret);" unless not is_value_function
|
|
putd_backslash "return ret;" unless not is_value_function
|
|
putd_backslash "#{return_type}FUNCNAME##_fake.custom_fake(#{arg_list(arg_count)});" unless is_value_function
|
|
}
|
|
putd_backslash "}"
|
|
end
|
|
|
|
putd_backslash "RETURN_FAKE_RESULT(FUNCNAME)" if is_value_function
|
|
end
|
|
|
|
def define_fff_globals
|
|
putd "typedef void (*fff_function_t)(void);"
|
|
putd "typedef struct { "
|
|
indent {
|
|
putd "fff_function_t call_history[FFF_CALL_HISTORY_LEN];"
|
|
putd "unsigned int call_history_idx;"
|
|
}
|
|
putd "} fff_globals_t;"
|
|
puts
|
|
putd "FFF_EXTERN_C"
|
|
putd "extern fff_globals_t fff;"
|
|
putd "FFF_END_EXTERN_C"
|
|
puts
|
|
putd_backslash "#define DEFINE_FFF_GLOBALS"
|
|
indent {
|
|
putd_backslash "FFF_EXTERN_C"
|
|
indent {
|
|
putd_backslash "fff_globals_t fff;"
|
|
}
|
|
putd "FFF_END_EXTERN_C"
|
|
}
|
|
puts
|
|
putd_backslash "#define FFF_RESET_HISTORY()"
|
|
indent {
|
|
putd_backslash "fff.call_history_idx = 0;"
|
|
putd "memset(fff.call_history, 0, sizeof(fff.call_history));"
|
|
}
|
|
puts
|
|
putd_backslash "#define REGISTER_CALL(function)"
|
|
indent {
|
|
putd_backslash "if(fff.call_history_idx < FFF_CALL_HISTORY_LEN)"
|
|
indent {
|
|
putd "fff.call_history[fff.call_history_idx++] = (fff_function_t)function;"
|
|
}
|
|
}
|
|
end
|
|
|
|
def in_struct
|
|
putd_backslash "typedef struct FUNCNAME##_Fake {"
|
|
indent {
|
|
yield
|
|
}
|
|
putd_backslash "} FUNCNAME##_Fake;"
|
|
end
|
|
|
|
def include_guard
|
|
putd "#ifndef FAKE_FUNCTIONS"
|
|
putd "#define FAKE_FUNCTIONS"
|
|
puts
|
|
|
|
yield
|
|
|
|
puts
|
|
putd "#endif /* FAKE_FUNCTIONS */"
|
|
end
|
|
|
|
def msvc_expand_macro_fix
|
|
putd "/* MSVC expand macro fix */"
|
|
putd "#define EXPAND(x) x"
|
|
end
|
|
|
|
def generate_arg_sequence(args, prefix, do_reverse, joinstr)
|
|
fmap = (0..args).flat_map {|i| [prefix + i.to_s]}
|
|
if do_reverse then fmap.reverse.join(joinstr) else fmap.join(", ") end
|
|
end
|
|
|
|
def counting_macro_instance(type, has_calling_conventions, vararg = :non_vararg, prefix = "")
|
|
appendix = (vararg == :vararg) ? "_VARARG" : ""
|
|
if has_calling_conventions
|
|
minus_count = (type == :VOID) ? 2 : 3
|
|
else
|
|
minus_count = (type == :VOID) ? 1 : 2
|
|
end
|
|
|
|
<<-MACRO_COUNTING_INSTANCE
|
|
#define #{prefix}FAKE_#{type.to_s}_FUNC#{appendix}(...) \
|
|
EXPAND(#{prefix}FUNC_#{type.to_s}#{appendix}_(PP_NARG_MINUS#{minus_count}(__VA_ARGS__), __VA_ARGS__))
|
|
|
|
#define #{prefix}FUNC_#{type.to_s}#{appendix}_(N,...) \
|
|
EXPAND(#{prefix}FUNC_#{type.to_s}#{appendix}_N(N,__VA_ARGS__))
|
|
|
|
#define #{prefix}FUNC_#{type.to_s}#{appendix}_N(N,...) \
|
|
EXPAND(#{prefix}FAKE_#{type.to_s}_FUNC ## N#{" ## _VARARG" if vararg == :vararg}(__VA_ARGS__))
|
|
|
|
MACRO_COUNTING_INSTANCE
|
|
end
|
|
|
|
def output_macro_counting_shortcuts(has_calling_conventions)
|
|
has_calling_conventions ?
|
|
(arg_depth = ["3", "2"]; calling_conv = "callingConv, ") :
|
|
(arg_depth = ["2", "1"]; calling_conv = "")
|
|
|
|
msvc_expand_macro_fix
|
|
putd <<-MACRO_COUNTING
|
|
|
|
#define PP_NARG_MINUS#{arg_depth[0]}(...) \
|
|
EXPAND(PP_NARG_MINUS#{arg_depth[0]}_(__VA_ARGS__, PP_RSEQ_N_MINUS#{arg_depth[0]}()))
|
|
|
|
#define PP_NARG_MINUS#{arg_depth[0]}_(...) \
|
|
EXPAND(PP_ARG_MINUS#{arg_depth[0]}_N(__VA_ARGS__))
|
|
|
|
#define PP_ARG_MINUS#{arg_depth[0]}_N(returnVal, #{calling_conv} #{generate_arg_sequence($MAX_ARGS, '_', false, ", ")}, N, ...) N
|
|
|
|
#define PP_RSEQ_N_MINUS#{arg_depth[0]}() \
|
|
#{generate_arg_sequence($MAX_ARGS, '', true, ',')}
|
|
|
|
#define PP_NARG_MINUS#{arg_depth[1]}(...) \
|
|
EXPAND(PP_NARG_MINUS#{arg_depth[1]}_(__VA_ARGS__, PP_RSEQ_N_MINUS#{arg_depth[1]}()))
|
|
|
|
#define PP_NARG_MINUS#{arg_depth[1]}_(...) \
|
|
EXPAND(PP_ARG_MINUS#{arg_depth[1]}_N(__VA_ARGS__))
|
|
|
|
#define PP_ARG_MINUS#{arg_depth[1]}_N(#{calling_conv} #{generate_arg_sequence($MAX_ARGS, '_', false, ", ")}, N, ...) N
|
|
|
|
#define PP_RSEQ_N_MINUS#{arg_depth[1]}() \
|
|
#{generate_arg_sequence($MAX_ARGS, '', true, ',')}
|
|
|
|
|
|
|
|
/* DECLARE AND DEFINE FAKE FUNCTIONS - PLACE IN TEST FILES */
|
|
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions)}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions)}
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :vararg)}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions, :vararg)}
|
|
|
|
/* DECLARE FAKE FUNCTIONS - PLACE IN HEADER FILES */
|
|
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :non_vararg, "DECLARE_")}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions, :non_vararg, "DECLARE_")}
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :vararg, "DECLARE_")}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions, :vararg, "DECLARE_")}
|
|
|
|
/* DEFINE FAKE FUNCTIONS - PLACE IN SOURCE FILES */
|
|
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :non_vararg, "DEFINE_")}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions, :non_vararg, "DEFINE_")}
|
|
#{counting_macro_instance(:VALUE, has_calling_conventions, :vararg, "DEFINE_")}
|
|
#{counting_macro_instance(:VOID, has_calling_conventions, :vararg, "DEFINE_")}
|
|
|
|
MACRO_COUNTING
|
|
end
|
|
|
|
def output_c_and_cpp(has_calling_conventions)
|
|
license
|
|
include_guard {
|
|
include_dependencies
|
|
output_constants
|
|
output_default_function_pointer_macro(has_calling_conventions)
|
|
output_internal_helper_macros
|
|
yield
|
|
output_macro_counting_shortcuts(has_calling_conventions)
|
|
}
|
|
end
|
|
|
|
def help
|
|
# Check if we should generate _with_ support for specifying calling conventions
|
|
if (ARGV[0] == "--help" or ARGV[0] == "-h")
|
|
puts "Usage: fakegen.rb [options]
|
|
-h, --help Show this help message
|
|
-wcc, --with-calling-conventions Support specifying calling conventions"
|
|
exit
|
|
end
|
|
yield
|
|
end
|
|
|
|
help {
|
|
# Determine if we should generate with support for calling conventions
|
|
has_calling_conventions = true if (ARGV[0] == "--with-calling-conventions" or ARGV[0] == "-wcc")
|
|
# lets generate!!
|
|
output_c_and_cpp(has_calling_conventions) {
|
|
define_fff_globals
|
|
# Create fake generators for 0..MAX_ARGS
|
|
num_fake_generators = $MAX_ARGS + 1
|
|
num_fake_generators.times {|arg_count| output_macro(arg_count, false, has_calling_conventions, false)}
|
|
num_fake_generators.times {|arg_count| output_macro(arg_count, false, has_calling_conventions, true)}
|
|
# generate the varargs variants
|
|
(2..$MAX_ARGS).each {|arg_count| output_macro(arg_count, true, has_calling_conventions, false)}
|
|
(2..$MAX_ARGS).each {|arg_count| output_macro(arg_count, true, has_calling_conventions, true)}
|
|
}
|
|
} |