Refactor transform_inline_functions

- Just looking for static|inline in the gsub is a bit too aggressive.
  Instead, look for the "static inline" and parse it:
  - Everything before the match should just be copied, we don't want
    to touch anything but the inline functions.
  - Remove the implementation of the inline function (this is enclosed
    in square brackets) and replace it with ";" to complete the
    transformation to normal/non-inline function.
  - Copy everything after the inline function implementation

Repeat the above step until we can't find "static inline" anymore

- To remove the inline implementation,
  we count the number of square-bracket 'levels' in the inline function
  and remove every pair. This ensures that constructs like:
  inline void func(void) {
      if (...) {
        //NOP
      }
      else
      {
        //NOP
      }
  }
  will not end up leaving the 'else' keyword unremoved:
  inline void func(void);
      else
  If we count the number of levels, we don't end up in this scenario
  since we remove 3 'pairs', the if-one, the else-one and finally the
  main body.
This commit is contained in:
laurens
2019-11-12 20:01:04 +01:00
parent 2def6c4f21
commit 0af0e20d15
2 changed files with 112 additions and 22 deletions
+52 -15
View File
@@ -74,27 +74,64 @@ class CMockHeaderParser
end
def transform_inline_functions(source)
# Format to look for inline functions.
# This is a combination of "static" and "inline" keywords ("static inline", "inline static", "inline", "static")
# There are several possibilities:
# - sometimes they appear together, sometimes individually,
# - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff),
# so we check for word boundaries when searching for them
# - We first remove "static inline" combinations and boil down to
inline_function_regex_formats = [
/(static\s+inline|inline\s+static)\s*/, # Last part (\s*) is just to remove whitespaces (only to prettify the output)
/(\bstatic\b|\binline\b)\s*/, # Last part (\s*) is just to remove whitespaces (only to prettify the output)
]
# let's clean up the encoding in case they've done anything weird with the characters we might find
source = source.force_encoding("ISO-8859-1").encode("utf-8", :replace => nil)
source.gsub!(/(static|inline)+.*\{.*\w*\}/m) do |m|
m.gsub!(/(static|inline)/, '') # remove static and inline keywords
m = remove_nested_pairs_of_braces(m)
# - Just looking for static|inline in the gsub is a bit too aggressive (functions that are named like this, ...), so we try to be a bit smarter
# Instead, look for "static inline" and parse it:
# - Everything before the match should just be copied, we don't want
# to touch anything but the inline functions.
# - Remove the implementation of the inline function (this is enclosed
# in square brackets) and replace it with ";" to complete the
# transformation to normal/non-inline function.
# To ensure proper removal of the function body, we count the number of square-bracket pairs
# and remove the pairs one-by-one.
# - Copy everything after the inline function implementation and start the parsing of the next inline function
# Functions having "{ }" at this point are/were inline functions,
# Disguise them as normal functions with the ";"
m.gsub!(/\s*\{\s\}/, ";")
inline_function_regex_formats.each do |format|
loop do
inline_function_match = source.match(/#{format}/) # Search for inline function declaration
break if nil == inline_function_match
# Cleanup the function declarations
# Not strictly necessary, it will compile just fine, but it can help during debugging
m_lines = m.split(/\s*;\s*/).uniq
m_lines.each do |m_line|
m_line.gsub!(/^\s+/, '') # remove extra white space from beginning of line
m_line.gsub!(/\s+/, ' ') # remove remaining extra white space
m_line.gsub!(/\n/, '') # remove newlines
# Get number of square brackets in function, we already now there is atleast 1 pair (main function body)
curr = 0
started = false
total = 0
inline_function_match.post_match.each_char do |c|
if ("{" == c)
curr += 1
total +=1
started = true
elsif ("}" == c)
curr -=1
end
break if started && curr == 0 # We reached the end of the inline function body
end
inline_function_stripped = inline_function_match.post_match
until total == 1
inline_function_stripped.sub!(/\{[^\{\}]*\}/, "") # Remove code in between square brackets
total -= 1
end
inline_function_stripped.sub!(/(\s*\{[^\{\}]*|[^\{\}]*\})+/, ";") # Remove inline implementation
source = inline_function_match.pre_match + inline_function_stripped # Make new source with the inline function removed and move on to the next
end
m_lines.join(";\n") + ";" # Join the lines and add the last semicolon manually
end
return source
+60 -7
View File
@@ -1842,18 +1842,59 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
"};\n" +
"int my_function(int a);\n" +
"int my_better_function(struct my_struct *s);\n" +
"static inline int get_member_a(struct my_struct *s)\n" +
"static inline int staticinlinebar(struct my_struct *s)\n" + # This is a function with a lot of indentation levels, we should be able to handle it
"{\n" +
"\t{\n" +
"\t\t{\n" +
"\t\t\treturn s->a;\n" +
"\t\t}\n" +
"\t}\n" +
"}\n" +
"static inline int staticinlinefunc(struct my_struct *s)\n" +
"{\n" +
" return s->a;\n" +
"}\n" +
"inline static int my_func_0(int a)\n" +
"{\n" +
"int bar(struct my_struct *s);\n" + # A normal function to screw with our parser
"inline static int inlinestaticfunc(int a) {\n" +
" return a + 42;\n" +
"}\n" +
"inline int my_func_1(struct my_struct *s)\n" +
"inline int StaticInlineFunc(struct my_struct *s)\n" +
"{\n" +
" return get_member_a(s) + 42;\n" +
"}\n" +
"int inline StaticInlineBar(struct my_struct *s)\n" +
"{\n" +
" return get_member_a(s) + 42;\n" +
"}\n" +
"struct staticinlinestruct {\n" + # Add a structure declaration between the inline functions, just to make sure we don't touch it!
"int a;\n" +
"};\n" +
"\n" +
"struct staticinlinestruct fubarstruct(struct my_struct *s);\n" + # Another normal function to screw with our parser
"static inline struct staticinlinestruct inlinefubarfunction(struct my_struct *s)\n" +
"{\n" +
" return (struct staticinlinestruct)*s;\n" +
"}\n" +
"int fubar(struct my_struct *s);\n" + # Another normal function to screw with our parser
"inline int stuff(int num)" +
"{" +
" int reg = 0x12;" +
" if (num > 0)" +
" {" +
" reg |= (0x0Eu);" +
" }" +
" else" +
" {" +
" reg |= (0x07u);" +
" }" +
" return reg;" +
"}" +
"\n" +
"int inline static dummy_func_2(int a, char b, float c) {" + # This is a sneaky one, inline static is placed AFTER the return value
" c += 3.14;" +
" b -= 32;" +
" return a + (int)(b) + (int)c;" +
"}" +
"#endif _NOINCLUDES\n"
expected =
@@ -1883,9 +1924,21 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do
"};\n" +
"int my_function(int a);\n" +
"int my_better_function(struct my_struct *s);\n" +
"int get_member_a(struct my_struct *s);\n" +
"int my_func_0(int a);\n" +
"int my_func_1(struct my_struct *s);\n" +
"int staticinlinebar(struct my_struct *s);\n" +
"int staticinlinefunc(struct my_struct *s);\n" +
"int bar(struct my_struct *s);\n" +
"int inlinestaticfunc(int a);\n" +
"int StaticInlineFunc(struct my_struct *s);\n" +
"int StaticInlineBar(struct my_struct *s);\n" +
"struct staticinlinestruct {\n" +
"int a;\n" +
"};\n" +
"\n" +
"struct staticinlinestruct fubarstruct(struct my_struct *s);\n" +
"struct staticinlinestruct inlinefubarfunction(struct my_struct *s);\n" +
"int fubar(struct my_struct *s);\n" +
"int stuff(int num);\n" +
"int dummy_func_2(int a, char b, float c);" +
"#endif _NOINCLUDES\n"
assert_equal(expected, @parser.transform_inline_functions(source))