CMakePP / CMakePPLang

Object-oriented extension to the CMake language.
http://cmakepp.github.io/CMakePPLang/
Apache License 2.0
11 stars 4 forks source link

Escaped double-quotes are not honored in string parameters #46

Closed zachcran closed 2 years ago

zachcran commented 2 years ago

Describe the bug When a string with escaped double quotes (\") is passed as a string (str) parameter to a member function, the escaped double quotes are not honored, instead seeming to indicate the end of the argument.

To Reproduce Here is a unit test I wrote to verify the issue was happening: member_functions.cmake.txt. Put the file in CMakePPLang/tests/class, rename the file to member_functions.cmake, and run the tests to reproduce it easily.

Alternatively, here are steps to reproduce the behavior:

  1. Create a class with a member function that takes a string as an argument:
    
    include(cmakepp_lang/class/class)

cpp_class(MyClass) cpp_member(print_str MyClass str) function("${print_str}" self a) message("Message received: ${a}") endfunction() cpp_end_class()

2. Try to use the method:
```cmake
MyClass(CTOR my_instance)
MyClass(print_str "${my_instance}" "String \"with quotes\"")
  1. Output will stop at the first escaped double quote
    # Output
    # Message received: String

Expected behavior I expected the escaped double quotes to remain in the string and be passed along to the member function. I tested this with message() and string(APPEND from vanilla CMake, and they honored the escaped double quotes.

In my expected behavior, the output in the above code example would be Message received: String "with quotes", not Message received: String.

Additional context As shown in the attached file above, I have tried passing the variable containing escaped double quotes both surrounded by quotes ("${var}") and not surrounded by quotes (${var}), as well as the string itself instead of a variable. None of these worked.

zachcran commented 2 years ago

One more note, double quotes seem to be honored in user-defined CMake functions as well, so this does seem to be a CMakePPLang issue. For example:

function(print_str message)
    message("Message received: ${message}")
endfunction()

print_str("String \"with quotes\"")
# Output
# Message received: String "with quotes"
ryanmrichard commented 2 years ago

With CMake the number of escapes depends on how many functions you're trying to pass the string through (each function call consumes a round of escapes). I'm pretty sure that CMakePPLang doesn't directly pass the arguments to the decorated function, so assuming it needs to go through more than one function you're going to need some atrocious escape sequence. I believe it's \\\" to go through two functions, \\\\\\\" to go through three functions, etc. (if n is the number of slashes to go through n-1 functions it should take 2n+1 slashes to go through n functions, i.e., n slashes to protect the original n slashes and 1 additional slash to protect the double quotes).

That said, it'd be nice to fix this issue (ideally without more than one escape). Conceivably we could call a macro right on the other side of the MyClass function to protect the slashes (regex them to something like ASCII character 7, the BEL character) and then unprotect them (regex them back to \) when they're ready to be processed.

zachcran commented 2 years ago

Forgive me, I am still trying to wrap my head around how everything works in CMakePPLang implementation (and CMake in general). Do you think it would be reasonable to encode the escaped double quotes before the cpp_assert_signature call here? Entering that _cpp_object_call macro seems to be where the backslash escaping the quotes get stripped.

For decoding the escaped double quotes again, it looks like it would be best to decode the arguments before they are written to the file to be executed here, decoding the encoded escaped quotes as something like \\\" to account for the function writing to the file.

Let me know if that sounds reasonable!

ryanmrichard commented 2 years ago

So are you saying that \"hi\" makes it to just inside _cpp_object_call as "hi"? Or that it makes it there as \"hi\"?

I think the ideal situation has us encode the quotes as close to the user as possible. So in your above example I'd try to encode them in the body of the MyClass function. IIRC that function is autogenerated based off a template file so you'd have to add the protection to the template file.

WRT to decoding I think that's the right place.

FWIW this is going to be a problem for other special characters too (like if a user passes \$ or \;) so you may as well encode them too while you're at it.

zachcran commented 2 years ago

There seems to be a long-standing bug (known since at least 2007) where macros remove (more) escape protection from special characters. Both functions and macros remove escapes, but this but certainly doesn't help.