diff --git a/llvm/test/tools/llvm-rc/Inputs/split-path.rc b/llvm/test/tools/llvm-rc/Inputs/split-path.rc new file mode 100644 index 0000000000000..4f60b6f78ae15 --- /dev/null +++ b/llvm/test/tools/llvm-rc/Inputs/split-path.rc @@ -0,0 +1 @@ +100 ICON "subdir" "/icon-new.ico" diff --git a/llvm/test/tools/llvm-rc/Inputs/tokens-windres-invalid-concat.rc b/llvm/test/tools/llvm-rc/Inputs/tokens-windres-invalid-concat.rc new file mode 100644 index 0000000000000..5051ffaed513f --- /dev/null +++ b/llvm/test/tools/llvm-rc/Inputs/tokens-windres-invalid-concat.rc @@ -0,0 +1 @@ +"narrow" L"wide" diff --git a/llvm/test/tools/llvm-rc/Inputs/tokens-windres.rc b/llvm/test/tools/llvm-rc/Inputs/tokens-windres.rc new file mode 100644 index 0000000000000..46c6312002a55 --- /dev/null +++ b/llvm/test/tools/llvm-rc/Inputs/tokens-windres.rc @@ -0,0 +1,3 @@ +L"wide" " also wide", +"narrow" " also narrow", +L"wide" L" more wide", diff --git a/llvm/test/tools/llvm-rc/split-path.test b/llvm/test/tools/llvm-rc/split-path.test new file mode 100644 index 0000000000000..b396d805df5c9 --- /dev/null +++ b/llvm/test/tools/llvm-rc/split-path.test @@ -0,0 +1,6 @@ +; RUN: rm -rf %t +; RUN: mkdir %t +; RUN: cd %t +; RUN: mkdir subdir +; RUN: cp %p/Inputs/icon-new.ico subdir +; RUN: llvm-windres --no-preprocess %p/Inputs/split-path.rc %t/split-path.res diff --git a/llvm/test/tools/llvm-rc/windres-format.test b/llvm/test/tools/llvm-rc/windres-format.test index 81e6147d94b48..b647be6da6a61 100644 --- a/llvm/test/tools/llvm-rc/windres-format.test +++ b/llvm/test/tools/llvm-rc/windres-format.test @@ -2,11 +2,11 @@ ; from file suffixes. ; RUN: rm -f %t.res -; RUN: llvm-windres --no-preprocess %p/Inputs/tag-stringtable-basic.rc %t.res +; RUN: llvm-windres --no-preprocess %p/Inputs/tag-versioninfo.rc %t.res ; RUN: llvm-readobj %t.res | FileCheck %s --check-prefix=CHECK-RES ; RUN: rm -f %t.o -; RUN: llvm-windres --no-preprocess -F pe-x86-64 %p/Inputs/tag-stringtable-basic.rc %t.o +; RUN: llvm-windres --no-preprocess -F pe-x86-64 %p/Inputs/tag-versioninfo.rc %t.o ; RUN: llvm-readobj --coff-resources %t.o | FileCheck %s --check-prefix=CHECK-OBJ ; RUN: rm -f %t.obj @@ -16,7 +16,7 @@ ; Check that we can specify the input/output file types explicitly. ; Also check options for specifying the input/output file names. -; RUN: cat %p/Inputs/tag-stringtable-basic.rc > %t-anonymous +; RUN: cat %p/Inputs/tag-versioninfo.rc > %t-anonymous ; RUN: rm -f %t-anonymous2 ; RUN: llvm-windres --no-preprocess -O res -J rc -o %t-anonymous2 -i %t-anonymous ; RUN: llvm-readobj %t-anonymous2 | FileCheck %s --check-prefix=CHECK-RES @@ -25,7 +25,7 @@ ; RUN: llvm-windres -F pe-x86-64 -J res -O coff -i%t-anonymous2 -o%t-anonymous3 ; RUN: llvm-readobj --coff-resources %t-anonymous3 | FileCheck %s --check-prefix=CHECK-OBJ -; CHECK-RES: Resource type (int): STRINGTABLE +; CHECK-RES: Resource type (int): ; CHECK-OBJ: Format: COFF-x86-64 ; CHECK-OBJ: Resources [ diff --git a/llvm/test/tools/llvm-rc/windres-prefix.test b/llvm/test/tools/llvm-rc/windres-prefix.test index 9d24bf9ee7b2a..f085693610fc1 100644 --- a/llvm/test/tools/llvm-rc/windres-prefix.test +++ b/llvm/test/tools/llvm-rc/windres-prefix.test @@ -10,7 +10,7 @@ ; Check that the triple prefix also affects the output object file type. -; RUN: %t/aarch64-w64-mingw32-windres --no-preprocess %p/Inputs/tag-stringtable-basic.rc %t.o +; RUN: %t/aarch64-w64-mingw32-windres --no-preprocess %p/Inputs/tag-versioninfo.rc %t.o ; RUN: llvm-readobj --coff-resources %t.o | FileCheck %s --check-prefix=CHECK-OBJ ; CHECK-OBJ: Format: COFF-ARM64 diff --git a/llvm/test/tools/llvm-rc/windres-target.test b/llvm/test/tools/llvm-rc/windres-target.test index a832c038efccd..c72ad25fa15fa 100644 --- a/llvm/test/tools/llvm-rc/windres-target.test +++ b/llvm/test/tools/llvm-rc/windres-target.test @@ -17,13 +17,13 @@ ; Check the actual written object types. -; RUN: llvm-windres --no-preprocess -F i686-w64-mingw32 %p/Inputs/tag-stringtable-basic.rc %t.o +; RUN: llvm-windres --no-preprocess -F i686-w64-mingw32 %p/Inputs/tag-versioninfo.rc %t.o ; RUN: llvm-readobj --coff-resources %t.o | FileCheck %s --check-prefixes=CHECK-OBJ,CHECK-OBJ-I686 -; RUN: llvm-windres --no-preprocess -F x86_64-w64-mingw32 %p/Inputs/tag-stringtable-basic.rc %t.o +; RUN: llvm-windres --no-preprocess -F x86_64-w64-mingw32 %p/Inputs/tag-versioninfo.rc %t.o ; RUN: llvm-readobj --coff-resources %t.o | FileCheck %s --check-prefixes=CHECK-OBJ,CHECK-OBJ-X86-64 -; RUN: llvm-windres --no-preprocess -F armv7-w64-mingw32 %p/Inputs/tag-stringtable-basic.rc %t.o +; RUN: llvm-windres --no-preprocess -F armv7-w64-mingw32 %p/Inputs/tag-versioninfo.rc %t.o ; RUN: llvm-readobj --coff-resources %t.o | FileCheck %s --check-prefixes=CHECK-OBJ,CHECK-OBJ-ARMV7 -; RUN: llvm-windres --no-preprocess -F aarch64-w64-mingw32 %p/Inputs/tag-stringtable-basic.rc %t.o +; RUN: llvm-windres --no-preprocess -F aarch64-w64-mingw32 %p/Inputs/tag-versioninfo.rc %t.o ; RUN: llvm-readobj --coff-resources %t.o | FileCheck %s --check-prefixes=CHECK-OBJ,CHECK-OBJ-AARCH64 ; CHECK-OBJ-I686: Format: COFF-i386 diff --git a/llvm/test/tools/llvm-rc/windres-tokenizer.test b/llvm/test/tools/llvm-rc/windres-tokenizer.test new file mode 100644 index 0000000000000..b7c88fa2a9179 --- /dev/null +++ b/llvm/test/tools/llvm-rc/windres-tokenizer.test @@ -0,0 +1,13 @@ +; RUN: not llvm-windres --no-preprocess --verbose %p/Inputs/tokens-windres.rc %t.res | FileCheck %s +; llvm-windres fails on this sample because it is an invalid resource file +; script. We silence the error message and just analyze the output. + +; CHECK: String: L"wide also wide" +; CHECK-NEXT: Comma: , +; CHECK-NEXT: String: "narrow also narrow" +; CHECK-NEXT: Comma: , +; CHECK-NEXT: String: L"wide more wide" + +; RUN: not llvm-windres --no-preprocess --verbose %p/Inputs/tokens-windres-invalid-concat.rc %t.res 2>&1 | FileCheck %s --check-prefix=CONCAT-ERROR + +; CONCAT-ERROR: Error parsing file: Cannot concatenate a wide string L"wide" after a narrow one "narrow" diff --git a/llvm/tools/llvm-rc/ResourceScriptToken.cpp b/llvm/tools/llvm-rc/ResourceScriptToken.cpp index a8f40abdf8a68..f036d279a1a9a 100644 --- a/llvm/tools/llvm-rc/ResourceScriptToken.cpp +++ b/llvm/tools/llvm-rc/ResourceScriptToken.cpp @@ -85,7 +85,9 @@ namespace { class Tokenizer { public: - Tokenizer(StringRef Input) : Data(Input), DataLength(Input.size()), Pos(0) {} + Tokenizer(StringRef Input, StringSaver &Saver, bool IsWindres) + : Data(Input), DataLength(Input.size()), Pos(0), Saver(Saver), + IsWindres(IsWindres) {} Expected> run(); @@ -141,8 +143,14 @@ class Tokenizer { // an identifier describing a block start or end. void processIdentifier(RCToken &token) const; + Expected mergeStringTokens(const RCToken &FirstToken, + const RCToken &SecondToken); + StringRef Data; size_t DataLength, Pos; + + StringSaver &Saver; + bool IsWindres; }; void Tokenizer::skipCurrentLine() { @@ -188,6 +196,16 @@ Expected> Tokenizer::run() { return getStringError("Integer invalid or too large: " + Token.value().str()); } + } else if (TokenKind == Kind::String && !Result.empty() && IsWindres) { + RCToken &Prev = Result.back(); + if (Prev.kind() == Kind::String) { + Expected MergedToken = mergeStringTokens(Prev, Token); + if (!MergedToken) + return MergedToken.takeError(); + // Remove the old token and replace the new one with the merged token. + Result.pop_back(); + Token = *MergedToken; + } } Result.push_back(Token); @@ -356,12 +374,43 @@ void Tokenizer::processIdentifier(RCToken &Token) const { Token = RCToken(Kind::BlockEnd, Name); } +Expected Tokenizer::mergeStringTokens(const RCToken &FirstToken, + const RCToken &SecondToken) { + assert(FirstToken.kind() == Kind::String && + SecondToken.kind() == Kind::String); + StringRef First = FirstToken.value(); + StringRef Second = SecondToken.value(); + bool IsFirstLong = First.starts_with_insensitive("L"); + bool IsSecondLong = Second.starts_with_insensitive("L"); + // "aaa" "bbb" = "aaabbb" + // L"aaa" L"bbb" = L"aaabbb" + // L"aaa" "bbb" = L"aaabbb" + // "aaa" L"bbb" = + if (IsSecondLong && !IsFirstLong) + return getStringError("Cannot concatenate a wide string " + Twine(Second) + + " after a narrow one " + Twine(First)); + if (IsFirstLong) + First = First.drop_front(); + if (IsSecondLong) + Second = Second.drop_front(); + bool FirstUnquoted = First.consume_front("\"") && First.consume_back("\""); + bool SecondUnquoted = Second.consume_front("\"") && Second.consume_back("\""); + assert(FirstUnquoted && SecondUnquoted); + (void)FirstUnquoted; + (void)SecondUnquoted; + + StringRef MergedString = + Saver.save(Twine(IsFirstLong ? "L" : "") + "\"" + First + Second + "\""); + return RCToken(Kind::String, MergedString); +} + } // anonymous namespace namespace llvm { -Expected> tokenizeRC(StringRef Input) { - return Tokenizer(Input).run(); +Expected> tokenizeRC(StringRef Input, StringSaver &Saver, + bool IsWindres) { + return Tokenizer(Input, Saver, IsWindres).run(); } } // namespace llvm diff --git a/llvm/tools/llvm-rc/ResourceScriptToken.h b/llvm/tools/llvm-rc/ResourceScriptToken.h index cc8ca48b4907e..7144b4f406e83 100644 --- a/llvm/tools/llvm-rc/ResourceScriptToken.h +++ b/llvm/tools/llvm-rc/ResourceScriptToken.h @@ -26,6 +26,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" +#include "llvm/Support/StringSaver.h" #include #include @@ -74,7 +75,8 @@ class RCToken { // Tokens returned by this function hold only references to the parts // of the Input. Memory buffer containing Input cannot be freed, // modified or reallocated. -Expected> tokenizeRC(StringRef Input); +Expected> tokenizeRC(StringRef Input, StringSaver &Saver, + bool IsWindres); } // namespace llvm diff --git a/llvm/tools/llvm-rc/llvm-rc.cpp b/llvm/tools/llvm-rc/llvm-rc.cpp index b955347f2a864..a8c1ac7e46224 100644 --- a/llvm/tools/llvm-rc/llvm-rc.cpp +++ b/llvm/tools/llvm-rc/llvm-rc.cpp @@ -635,7 +635,11 @@ void doRc(std::string Src, std::string Dest, RcOptions &Opts, StringRef Contents = FileContents->getBuffer(); std::string FilteredContents = filterCppOutput(Contents); - std::vector Tokens = ExitOnErr(tokenizeRC(FilteredContents)); + + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + std::vector Tokens = + ExitOnErr(tokenizeRC(FilteredContents, Saver, Opts.IsWindres)); if (Opts.BeVerbose) { const Twine TokenNames[] = {