[llvm] r315110 - [llvm-rc] Serialize VERSIONINFO resources to .res files.

Zachary Turner via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 6 14:26:06 PDT 2017


Author: zturner
Date: Fri Oct  6 14:26:06 2017
New Revision: 315110

URL: http://llvm.org/viewvc/llvm-project?rev=315110&view=rev
Log:
[llvm-rc] Serialize VERSIONINFO resources to .res files.

This is now able to dump VERSIONINFO resources.

Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381058.aspx

Differential Revision: https://reviews.llvm.org/D38410
Patch by: Marek Sokolowski

Added:
    llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo-mixed-ints-strings.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo-word-too-large.rc
    llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo.rc
    llvm/trunk/test/tools/llvm-rc/tag-versioninfo.test
Modified:
    llvm/trunk/test/tools/llvm-rc/Inputs/parser-correct-everything.rc
    llvm/trunk/test/tools/llvm-rc/parser.test
    llvm/trunk/tools/llvm-rc/ResourceFileWriter.cpp
    llvm/trunk/tools/llvm-rc/ResourceFileWriter.h
    llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp
    llvm/trunk/tools/llvm-rc/ResourceScriptParser.h
    llvm/trunk/tools/llvm-rc/ResourceScriptStmt.cpp
    llvm/trunk/tools/llvm-rc/ResourceScriptStmt.h
    llvm/trunk/tools/llvm-rc/ResourceScriptToken.cpp
    llvm/trunk/tools/llvm-rc/ResourceScriptToken.h
    llvm/trunk/tools/llvm-rc/ResourceVisitor.h

Modified: llvm/trunk/test/tools/llvm-rc/Inputs/parser-correct-everything.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/parser-correct-everything.rc?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/parser-correct-everything.rc (original)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/parser-correct-everything.rc Fri Oct  6 14:26:06 2017
@@ -93,15 +93,15 @@ BEGIN
         BLOCK "040904E4"
         {
             VALUE "CompanyName",      "a"
-            VALUE "FileDescription",  "b"
+            VALUE "FileDescription",  "b" "c" "d",   1 3  7L, "y", "h" "d"
             VALUE "FileVersion",      "c"
             VALUE "InternalName",     "d"
-            VALUE "LegalCopyright",   "e"
-            VALUE "LegalTrademarks1", "f"
+            VALUE "LegalCopyright",   "e" 0
+            VALUE "LegalTrademarks1", 1 2, 3
             VALUE "LegalTrademarks2", "g"
             VALUE "OriginalFilename", L"h"
-            VALUE "ProductName",      "ii", 2, 3
-            VALUE "ProductVersion"
+            VALUE "ProductName",      "ii", 2L, 3
+            VALUE "ProductVersion",   0x12345678L
         }
     END
 

Added: llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo-mixed-ints-strings.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo-mixed-ints-strings.rc?rev=315110&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo-mixed-ints-strings.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo-mixed-ints-strings.rc Fri Oct  6 14:26:06 2017
@@ -0,0 +1,18 @@
+1 VERSIONINFO
+FILEVERSION 1, 2, 3, 4
+PRODUCTVERSION 5, 6, 7, 8
+FILEFLAGSMASK 50
+FILEFLAGS 555
+FILEOS 110
+FILETYPE 555555
+FILESUBTYPE 14
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904E4"
+        {
+            VALUE "CompanyName", 32768
+            VALUE "FileDescription", 5, "a", 3
+        }
+    END
+END

Added: llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo-word-too-large.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo-word-too-large.rc?rev=315110&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo-word-too-large.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo-word-too-large.rc Fri Oct  6 14:26:06 2017
@@ -0,0 +1,18 @@
+1 VERSIONINFO
+FILEVERSION 1, 2, 3, 4
+PRODUCTVERSION 5, 6, 7, 8
+FILEFLAGSMASK 50
+FILEFLAGS 555
+FILEOS 110
+FILETYPE 555555
+FILESUBTYPE 14
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904E4"
+        {
+            VALUE "CompanyName", 32768
+            VALUE "FileDescription", 65536
+        }
+    END
+END

Added: llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo.rc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo.rc?rev=315110&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo.rc (added)
+++ llvm/trunk/test/tools/llvm-rc/Inputs/tag-versioninfo.rc Fri Oct  6 14:26:06 2017
@@ -0,0 +1,32 @@
+1 VERSIONINFO
+FILEVERSION 1, 2, 3, 4
+PRODUCTVERSION 5, 6, 7, 8
+FILEFLAGSMASK 50
+FILEFLAGS 555
+FILEOS 110
+FILETYPE 555555
+FILESUBTYPE 14
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904E4"
+        {
+            VALUE "CompanyName",      "a"
+            VALUE "FileDescription",  "b" "c", "d", L"eee" "f" L"g", "a", L"hohoho"
+            VALUE "FileVersion",      "c"
+            VALUE "InternalName",     "d"
+            VALUE "LegalCopyright",   "e" "0"
+            VALUE "LegalTrademarks1", 1 2, 3
+            VALUE "LegalTrademarks2", "g"
+            VALUE "OriginalFilename", L"h"
+            VALUE "ProductName",      "a" "b", "c"
+            VALUE "ProductVersion",   0x12345678L
+        }
+    END
+
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1252
+
+    END
+END

Modified: llvm/trunk/test/tools/llvm-rc/parser.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/parser.test?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/parser.test (original)
+++ llvm/trunk/test/tools/llvm-rc/parser.test Fri Oct  6 14:26:06 2017
@@ -76,19 +76,19 @@
 ; PGOOD-NEXT:    Start of block (name: "StringFileInfo")
 ; PGOOD-NEXT:    Start of block (name: "040904E4")
 ; PGOOD-NEXT:    "CompanyName" => "a"
-; PGOOD-NEXT:    "FileDescription" => "b"
+; PGOOD-NEXT:    "FileDescription" => "b" "c" "d", 1 3 7L, "y", "h" "d"
 ; PGOOD-NEXT:    "FileVersion" => "c"
 ; PGOOD-NEXT:    "InternalName" => "d"
 ; PGOOD-NEXT:    "LegalCopyright" => "e"
-; PGOOD-NEXT:    "LegalTrademarks1" => "f"
+; PGOOD-NEXT:    "LegalTrademarks1" => 1 2, 3
 ; PGOOD-NEXT:    "LegalTrademarks2" => "g"
 ; PGOOD-NEXT:    "OriginalFilename" => L"h"
-; PGOOD-NEXT:    "ProductName" => "ii" 2 3
-; PGOOD-NEXT:    "ProductVersion" =>
+; PGOOD-NEXT:    "ProductName" => "ii", 2L, 3
+; PGOOD-NEXT:    "ProductVersion" => 305419896L
 ; PGOOD-NEXT:    End of block
 ; PGOOD-NEXT:    End of block
 ; PGOOD-NEXT:    Start of block (name: "VarFileInfo")
-; PGOOD-NEXT:    "Translation" => 1033 1252
+; PGOOD-NEXT:    "Translation" => 1033, 1252
 ; PGOOD-NEXT:    End of block
 ; PGOOD-NEXT:    End of block
 ; PGOOD-NEXT:  User-defined (type: MYTYPE, name: MYNAME): "filename"

Added: llvm/trunk/test/tools/llvm-rc/tag-versioninfo.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-rc/tag-versioninfo.test?rev=315110&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-rc/tag-versioninfo.test (added)
+++ llvm/trunk/test/tools/llvm-rc/tag-versioninfo.test Fri Oct  6 14:26:06 2017
@@ -0,0 +1,66 @@
+; RUN: llvm-rc /FO %t %p/Inputs/tag-versioninfo.rc
+; RUN: llvm-readobj %t | FileCheck %s
+
+; CHECK:      Resource type (int): 16
+; CHECK-NEXT: Resource name (int): 1
+; CHECK-NEXT: Data version: 0
+; CHECK-NEXT: Memory flags: 0x30
+; CHECK-NEXT: Language ID: 1033
+; CHECK-NEXT: Version (major): 0
+; CHECK-NEXT: Version (minor): 0
+; CHECK-NEXT: Characteristics: 0
+; CHECK-NEXT: Data size: 672
+; CHECK-NEXT: Data: (
+; CHECK-NEXT:   0000: A0023400 00005600 53005F00 56004500  |..4...V.S._.V.E.|
+; CHECK-NEXT:   0010: 52005300 49004F00 4E005F00 49004E00  |R.S.I.O.N._.I.N.|
+; CHECK-NEXT:   0020: 46004F00 00000000 BD04EFFE 00000100  |F.O.............|
+; CHECK-NEXT:   0030: 02000100 04000300 06000500 08000700  |................|
+; CHECK-NEXT:   0040: 32000000 2B020000 6E000000 237A0800  |2...+...n...#z..|
+; CHECK-NEXT:   0050: 0E000000 00000000 00000000 00020000  |................|
+; CHECK-NEXT:   0060: 01005300 74007200 69006E00 67004600  |..S.t.r.i.n.g.F.|
+; CHECK-NEXT:   0070: 69006C00 65004900 6E006600 6F000000  |i.l.e.I.n.f.o...|
+; CHECK-NEXT:   0080: DC010000 01003000 34003000 39003000  |......0.4.0.9.0.|
+; CHECK-NEXT:   0090: 34004500 34000000 24000200 01004300  |4.E.4...$.....C.|
+; CHECK-NEXT:   00A0: 6F006D00 70006100 6E007900 4E006100  |o.m.p.a.n.y.N.a.|
+; CHECK-NEXT:   00B0: 6D006500 00000000 61000000 50001400  |m.e.....a...P...|
+; CHECK-NEXT:   00C0: 01004600 69006C00 65004400 65007300  |..F.i.l.e.D.e.s.|
+; CHECK-NEXT:   00D0: 63007200 69007000 74006900 6F006E00  |c.r.i.p.t.i.o.n.|
+; CHECK-NEXT:   00E0: 00000000 62006300 00006400 00006500  |....b.c...d...e.|
+; CHECK-NEXT:   00F0: 65006500 66006700 00006100 00006800  |e.e.f.g...a...h.|
+; CHECK-NEXT:   0100: 6F006800 6F006800 6F000000 24000200  |o.h.o.h.o...$...|
+; CHECK-NEXT:   0110: 01004600 69006C00 65005600 65007200  |..F.i.l.e.V.e.r.|
+; CHECK-NEXT:   0120: 73006900 6F006E00 00000000 63000000  |s.i.o.n.....c...|
+; CHECK-NEXT:   0130: 24000200 01004900 6E007400 65007200  |$.....I.n.t.e.r.|
+; CHECK-NEXT:   0140: 6E006100 6C004E00 61006D00 65000000  |n.a.l.N.a.m.e...|
+; CHECK-NEXT:   0150: 64000000 2A000300 01004C00 65006700  |d...*.....L.e.g.|
+; CHECK-NEXT:   0160: 61006C00 43006F00 70007900 72006900  |a.l.C.o.p.y.r.i.|
+; CHECK-NEXT:   0170: 67006800 74000000 65003000 00000000  |g.h.t...e.0.....|
+; CHECK-NEXT:   0180: 2E000600 00004C00 65006700 61006C00  |......L.e.g.a.l.|
+; CHECK-NEXT:   0190: 54007200 61006400 65006D00 61007200  |T.r.a.d.e.m.a.r.|
+; CHECK-NEXT:   01A0: 6B007300 31000000 01000200 03000000  |k.s.1...........|
+; CHECK-NEXT:   01B0: 2C000200 01004C00 65006700 61006C00  |,.....L.e.g.a.l.|
+; CHECK-NEXT:   01C0: 54007200 61006400 65006D00 61007200  |T.r.a.d.e.m.a.r.|
+; CHECK-NEXT:   01D0: 6B007300 32000000 67000000 2C000200  |k.s.2...g...,...|
+; CHECK-NEXT:   01E0: 01004F00 72006900 67006900 6E006100  |..O.r.i.g.i.n.a.|
+; CHECK-NEXT:   01F0: 6C004600 69006C00 65006E00 61006D00  |l.F.i.l.e.n.a.m.|
+; CHECK-NEXT:   0200: 65000000 68000000 2A000500 01005000  |e...h...*.....P.|
+; CHECK-NEXT:   0210: 72006F00 64007500 63007400 4E006100  |r.o.d.u.c.t.N.a.|
+; CHECK-NEXT:   0220: 6D006500 00000000 61006200 00006300  |m.e.....a.b...c.|
+; CHECK-NEXT:   0230: 00000000 28000400 00005000 72006F00  |....(.....P.r.o.|
+; CHECK-NEXT:   0240: 64007500 63007400 56006500 72007300  |d.u.c.t.V.e.r.s.|
+; CHECK-NEXT:   0250: 69006F00 6E000000 78563412 44000000  |i.o.n...xV4.D...|
+; CHECK-NEXT:   0260: 01005600 61007200 46006900 6C006500  |..V.a.r.F.i.l.e.|
+; CHECK-NEXT:   0270: 49006E00 66006F00 00000000 24000400  |I.n.f.o.....$...|
+; CHECK-NEXT:   0280: 00005400 72006100 6E007300 6C006100  |..T.r.a.n.s.l.a.|
+; CHECK-NEXT:   0290: 74006900 6F006E00 00000000 0904E404  |t.i.o.n.........|
+; CHECK-NEXT: )
+
+
+; RUN: not llvm-rc /FO %t %p/Inputs/tag-versioninfo-mixed-ints-strings.rc 2>&1 | FileCheck %s --check-prefix STRINT
+; STRINT: llvm-rc: Error in VERSIONINFO statement (ID 1):
+; STRINT-NEXT: VALUE "FileDescription" cannot contain both strings and integers
+
+
+; RUN: not llvm-rc /FO %t %p/Inputs/tag-versioninfo-word-too-large.rc 2>&1 | FileCheck %s --check-prefix WORD
+; WORD: llvm-rc: Error in VERSIONINFO statement (ID 1):
+; WORD-NEXT: VERSIONINFO integer value (65536) does not fit in 16 bits.

Modified: llvm/trunk/tools/llvm-rc/ResourceFileWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceFileWriter.cpp?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceFileWriter.cpp (original)
+++ llvm/trunk/tools/llvm-rc/ResourceFileWriter.cpp Fri Oct  6 14:26:06 2017
@@ -28,7 +28,7 @@ using namespace llvm::support;
 namespace llvm {
 namespace rc {
 
-// Class that employs RAII to save the current serializator object state
+// Class that employs RAII to save the current FileWriter object state
 // and revert to it as soon as we leave the scope. This is useful if resources
 // declare their own resource-local statements.
 class ContextKeeper {
@@ -79,6 +79,12 @@ static Error checkSignedNumberFits(uint3
   return Error::success();
 }
 
+static Error checkRCInt(RCInt Number, Twine FieldName) {
+  if (Number.isLong())
+    return Error::success();
+  return checkNumberFits<uint16_t>(Number, FieldName);
+}
+
 static Error checkIntOrString(IntOrString Value, Twine FieldName) {
   if (!Value.isInt())
     return Error::success();
@@ -177,6 +183,13 @@ Error ResourceFileWriter::writeIntOrStri
   return Error::success();
 }
 
+void ResourceFileWriter::writeRCInt(RCInt Value) {
+  if (Value.isLong())
+    writeObject((uint32_t)Value);
+  else
+    writeObject((uint16_t)Value);
+}
+
 Error ResourceFileWriter::appendFile(StringRef Filename) {
   bool IsLong;
   stripQuotes(Filename, IsLong);
@@ -245,6 +258,10 @@ Error ResourceFileWriter::visitMenuResou
   return writeResource(Res, &ResourceFileWriter::writeMenuBody);
 }
 
+Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
+  return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
+}
+
 Error ResourceFileWriter::visitCharacteristicsStmt(
     const CharacteristicsStmt *Stmt) {
   ObjectData.Characteristics = Stmt->Value;
@@ -922,5 +939,178 @@ Error ResourceFileWriter::writeMenuBody(
   return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
 }
 
+// --- VersionInfoResourceResource helpers. --- //
+
+Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
+  // Output the header if the block has name.
+  bool OutputHeader = Blk.Name != "";
+  uint64_t LengthLoc;
+
+  if (OutputHeader) {
+    LengthLoc = writeObject<uint16_t>(0);
+    writeObject<uint16_t>(0);
+    writeObject<uint16_t>(true);
+    RETURN_IF_ERROR(writeCString(Blk.Name));
+    padStream(sizeof(uint32_t));
+  }
+
+  for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
+    VersionInfoStmt *ItemPtr = Item.get();
+
+    if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
+      RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
+      continue;
+    }
+
+    auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
+    RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
+  }
+
+  if (OutputHeader) {
+    uint64_t CurLoc = tell();
+    writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
+  }
+
+  padStream(sizeof(uint32_t));
+  return Error::success();
+}
+
+Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
+  // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
+  // is a mapping from the key (string) to the value (a sequence of ints or
+  // a sequence of strings).
+  //
+  // If integers are to be written: width of each integer written depends on
+  // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
+  // ValueLength defined in structure referenced below is then the total
+  // number of bytes taken by these integers.
+  //
+  // If strings are to be written: characters are always WORDs.
+  // Moreover, '\0' character is written after the last string, and between
+  // every two strings separated by comma (if strings are not comma-separated,
+  // they're simply concatenated). ValueLength is equal to the number of WORDs
+  // written (that is, half of the bytes written).
+  //
+  // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
+  bool HasStrings = false, HasInts = false;
+  for (auto &Item : Val.Values)
+    (Item.isInt() ? HasInts : HasStrings) = true;
+
+  assert((HasStrings || HasInts) && "VALUE must have at least one argument");
+  if (HasStrings && HasInts)
+    return createError(Twine("VALUE ") + Val.Key +
+                       " cannot contain both strings and integers");
+
+  auto LengthLoc = writeObject<uint16_t>(0);
+  auto ValLengthLoc = writeObject<uint16_t>(0);
+  writeObject<uint16_t>(HasStrings);
+  RETURN_IF_ERROR(writeCString(Val.Key));
+  padStream(sizeof(uint32_t));
+
+  auto DataLoc = tell();
+  for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
+    auto &Item = Val.Values[Id];
+    if (Item.isInt()) {
+      auto Value = Item.getInt();
+      RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
+      writeRCInt(Value);
+      continue;
+    }
+
+    bool WriteTerminator =
+        Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
+    RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
+  }
+
+  auto CurLoc = tell();
+  auto ValueLength = CurLoc - DataLoc;
+  if (HasStrings) {
+    assert(ValueLength % 2 == 0);
+    ValueLength /= 2;
+  }
+  writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
+  writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
+  padStream(sizeof(uint32_t));
+  return Error::success();
+}
+
+template <typename Ty>
+static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
+                         const Ty &Default) {
+  auto Iter = Map.find(Key);
+  if (Iter != Map.end())
+    return Iter->getValue();
+  return Default;
+}
+
+Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
+  auto *Res = cast<VersionInfoResource>(Base);
+
+  const auto &FixedData = Res->FixedData;
+
+  struct /* VS_FIXEDFILEINFO */ {
+    ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
+    ulittle32_t StructVersion = ulittle32_t(0x10000);
+    // It's weird to have most-significant DWORD first on the little-endian
+    // machines, but let it be this way.
+    ulittle32_t FileVersionMS;
+    ulittle32_t FileVersionLS;
+    ulittle32_t ProductVersionMS;
+    ulittle32_t ProductVersionLS;
+    ulittle32_t FileFlagsMask;
+    ulittle32_t FileFlags;
+    ulittle32_t FileOS;
+    ulittle32_t FileType;
+    ulittle32_t FileSubtype;
+    // MS implementation seems to always set these fields to 0.
+    ulittle32_t FileDateMS = ulittle32_t(0);
+    ulittle32_t FileDateLS = ulittle32_t(0);
+  } FixedInfo;
+
+  // First, VS_VERSIONINFO.
+  auto LengthLoc = writeObject<uint16_t>(0);
+  writeObject(ulittle16_t(sizeof(FixedInfo)));
+  writeObject(ulittle16_t(0));
+  cantFail(writeCString("VS_VERSION_INFO"));
+  padStream(sizeof(uint32_t));
+
+  using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
+  auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
+    static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
+    if (!FixedData.IsTypePresent[(int)Type])
+      return DefaultOut;
+    return FixedData.FixedInfo[(int)Type];
+  };
+
+  auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
+  RETURN_IF_ERROR(checkNumberFits<uint16_t>(
+      *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
+  FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
+  FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
+
+  auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
+  RETURN_IF_ERROR(checkNumberFits<uint16_t>(
+      *std::max_element(ProdVer.begin(), ProdVer.end()),
+      "PRODUCTVERSION fields"));
+  FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
+  FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
+
+  FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
+  FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
+  FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
+  FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
+  FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
+
+  writeObject(FixedInfo);
+  padStream(sizeof(uint32_t));
+
+  RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
+
+  // FIXME: check overflow?
+  writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
+
+  return Error::success();
+}
+
 } // namespace rc
 } // namespace llvm

Modified: llvm/trunk/tools/llvm-rc/ResourceFileWriter.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceFileWriter.h?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceFileWriter.h (original)
+++ llvm/trunk/tools/llvm-rc/ResourceFileWriter.h Fri Oct  6 14:26:06 2017
@@ -36,6 +36,7 @@ public:
   Error visitHTMLResource(const RCResource *) override;
   Error visitIconResource(const RCResource *) override;
   Error visitMenuResource(const RCResource *) override;
+  Error visitVersionInfoResource(const RCResource *) override;
 
   Error visitCaptionStmt(const CaptionStmt *) override;
   Error visitCharacteristicsStmt(const CharacteristicsStmt *) override;
@@ -98,6 +99,11 @@ private:
   Error writeMenuDefinitionList(const MenuDefinitionList &List);
   Error writeMenuBody(const RCResource *);
 
+  // VersionInfoResource
+  Error writeVersionInfoBody(const RCResource *);
+  Error writeVersionInfoBlock(const VersionInfoBlock &);
+  Error writeVersionInfoValue(const VersionInfoValue &);
+
   // Output stream handling.
   std::unique_ptr<raw_fd_ostream> FS;
 
@@ -126,6 +132,8 @@ private:
   Error writeIdentifier(const IntOrString &Ident);
   Error writeIntOrString(const IntOrString &Data);
 
+  void writeRCInt(RCInt);
+
   Error appendFile(StringRef Filename);
 
   void padStream(uint64_t Length);

Modified: llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp (original)
+++ llvm/trunk/tools/llvm-rc/ResourceScriptParser.cpp Fri Oct  6 14:26:06 2017
@@ -134,12 +134,12 @@ void RCParser::consume() {
 //    1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff;
 //    1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff.
 
-Expected<uint32_t> RCParser::readInt() { return parseIntExpr1(); }
+Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); }
 
-Expected<uint32_t> RCParser::parseIntExpr1() {
+Expected<RCInt> RCParser::parseIntExpr1() {
   // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2.
   ASSIGN_OR_RETURN(FirstResult, parseIntExpr2());
-  uint32_t Result = *FirstResult;
+  RCInt Result = *FirstResult;
 
   while (!isEof() && look().isBinaryOp()) {
     auto OpToken = read();
@@ -170,7 +170,7 @@ Expected<uint32_t> RCParser::parseIntExp
   return Result;
 }
 
-Expected<uint32_t> RCParser::parseIntExpr2() {
+Expected<RCInt> RCParser::parseIntExpr2() {
   // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1).
   static const char ErrorMsg[] = "'-', '~', integer or '('";
 
@@ -191,7 +191,7 @@ Expected<uint32_t> RCParser::parseIntExp
   }
 
   case Kind::Int:
-    return read().intValue();
+    return RCInt(read());
 
   case Kind::LeftParen: {
     consume();
@@ -261,14 +261,14 @@ bool RCParser::consumeOptionalType(Kind
   return false;
 }
 
-Expected<SmallVector<uint32_t, 8>>
-RCParser::readIntsWithCommas(size_t MinCount, size_t MaxCount) {
+Expected<SmallVector<RCInt, 8>> RCParser::readIntsWithCommas(size_t MinCount,
+                                                             size_t MaxCount) {
   assert(MinCount <= MaxCount);
 
-  SmallVector<uint32_t, 8> Result;
+  SmallVector<RCInt, 8> Result;
 
   auto FailureHandler =
-      [&](llvm::Error Err) -> Expected<SmallVector<uint32_t, 8>> {
+      [&](llvm::Error Err) -> Expected<SmallVector<RCInt, 8>> {
     if (Result.size() < MinCount)
       return std::move(Err);
     consumeError(std::move(Err));
@@ -477,7 +477,7 @@ Expected<Control> RCParser::parseControl
   ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8));
 
   auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> {
-    return Args->size() > Id ? (*Args)[Id] : Optional<uint32_t>();
+    return Args->size() > Id ? (uint32_t)(*Args)[Id] : Optional<uint32_t>();
   };
 
   return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2],
@@ -608,15 +608,22 @@ Expected<std::unique_ptr<VersionInfoStmt
 
   if (TypeResult->equals_lower("VALUE")) {
     ASSIGN_OR_RETURN(KeyResult, readString());
-    // Read a (possibly empty) list of strings and/or ints, each preceded by
-    // a comma.
+    // Read a non-empty list of strings and/or ints, each
+    // possibly preceded by a comma. Unfortunately, the tool behavior depends
+    // on them existing or not, so we need to memorize where we found them.
     std::vector<IntOrString> Values;
-
-    while (consumeOptionalType(Kind::Comma)) {
+    std::vector<bool> PrecedingCommas;
+    RETURN_IF_ERROR(consumeType(Kind::Comma));
+    while (!isNextTokenKind(Kind::Identifier) &&
+           !isNextTokenKind(Kind::BlockEnd)) {
+      // Try to eat a comma if it's not the first statement.
+      bool HadComma = Values.size() > 0 && consumeOptionalType(Kind::Comma);
       ASSIGN_OR_RETURN(ValueResult, readIntOrString());
       Values.push_back(*ValueResult);
+      PrecedingCommas.push_back(HadComma);
     }
-    return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values));
+    return llvm::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
+                                               std::move(PrecedingCommas));
   }
 
   return getExpectedError("BLOCK or VALUE", true);
@@ -641,7 +648,8 @@ RCParser::parseVersionInfoFixed() {
     // VERSION variations take multiple integers.
     size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
     ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
-    Result.setValue(FixedType, *ArgsResult);
+    SmallVector<uint32_t, 4> ArgInts(ArgsResult->begin(), ArgsResult->end());
+    Result.setValue(FixedType, ArgInts);
   }
 
   return Result;

Modified: llvm/trunk/tools/llvm-rc/ResourceScriptParser.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceScriptParser.h?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceScriptParser.h (original)
+++ llvm/trunk/tools/llvm-rc/ResourceScriptParser.h Fri Oct  6 14:26:06 2017
@@ -79,15 +79,15 @@ private:
   // correct type and then parse it.
   // Each integer can be written as an arithmetic expression producing an
   // unsigned 32-bit integer.
-  Expected<uint32_t> readInt();            // Parse an integer.
+  Expected<RCInt> readInt();               // Parse an integer.
   Expected<StringRef> readString();        // Parse a string.
   Expected<StringRef> readIdentifier();    // Parse an identifier.
   Expected<IntOrString> readIntOrString(); // Parse an integer or a string.
   Expected<IntOrString> readTypeOrName();  // Parse an integer or an identifier.
 
   // Helper integer expression parsing methods.
-  Expected<uint32_t> parseIntExpr1();
-  Expected<uint32_t> parseIntExpr2();
+  Expected<RCInt> parseIntExpr1();
+  Expected<RCInt> parseIntExpr2();
 
   // Advance the state by one, discarding the current token.
   // If the discarded token had an incorrect type, fail.
@@ -101,8 +101,8 @@ private:
   // commas. The parser stops reading after fetching MaxCount integers
   // or after an error occurs. Whenever the parser reads a comma, it
   // expects an integer to follow.
-  Expected<SmallVector<uint32_t, 8>> readIntsWithCommas(size_t MinCount,
-                                                        size_t MaxCount);
+  Expected<SmallVector<RCInt, 8>> readIntsWithCommas(size_t MinCount,
+                                                     size_t MaxCount);
 
   // Read an unknown number of flags preceded by commas. Each correct flag
   // has an entry in FlagDesc array of length NumFlags. In case i-th

Modified: llvm/trunk/tools/llvm-rc/ResourceScriptStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceScriptStmt.cpp?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceScriptStmt.cpp (original)
+++ llvm/trunk/tools/llvm-rc/ResourceScriptStmt.cpp Fri Oct  6 14:26:06 2017
@@ -161,8 +161,12 @@ raw_ostream &VersionInfoBlock::log(raw_o
 
 raw_ostream &VersionInfoValue::log(raw_ostream &OS) const {
   OS << "  " << Key << " =>";
-  for (auto &Value : Values)
-    OS << " " << Value;
+  size_t NumValues = Values.size();
+  for (size_t Id = 0; Id < NumValues; ++Id) {
+    if (Id > 0 && HasPrecedingComma[Id])
+      OS << ",";
+    OS << " " << Values[Id];
+  }
   return OS << "\n";
 }
 

Modified: llvm/trunk/tools/llvm-rc/ResourceScriptStmt.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceScriptStmt.h?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceScriptStmt.h (original)
+++ llvm/trunk/tools/llvm-rc/ResourceScriptStmt.h Fri Oct  6 14:26:06 2017
@@ -22,17 +22,62 @@
 namespace llvm {
 namespace rc {
 
+// Integer wrapper that also holds information whether the user declared
+// the integer to be long (by appending L to the end of the integer) or not.
+// It allows to be implicitly cast from and to uint32_t in order
+// to be compatible with the parts of code that don't care about the integers
+// being marked long.
+class RCInt {
+  uint32_t Val;
+  bool Long;
+
+public:
+  RCInt(const RCToken &Token)
+      : Val(Token.intValue()), Long(Token.isLongInt()) {}
+  RCInt(uint32_t Value) : Val(Value), Long(false) {}
+  RCInt(uint32_t Value, bool IsLong) : Val(Value), Long(IsLong) {}
+  operator uint32_t() const { return Val; }
+  bool isLong() const { return Long; }
+
+  RCInt &operator+=(const RCInt &Rhs) {
+    std::tie(Val, Long) = std::make_pair(Val + Rhs.Val, Long | Rhs.Long);
+    return *this;
+  }
+
+  RCInt &operator-=(const RCInt &Rhs) {
+    std::tie(Val, Long) = std::make_pair(Val - Rhs.Val, Long | Rhs.Long);
+    return *this;
+  }
+
+  RCInt &operator|=(const RCInt &Rhs) {
+    std::tie(Val, Long) = std::make_pair(Val | Rhs.Val, Long | Rhs.Long);
+    return *this;
+  }
+
+  RCInt &operator&=(const RCInt &Rhs) {
+    std::tie(Val, Long) = std::make_pair(Val & Rhs.Val, Long | Rhs.Long);
+    return *this;
+  }
+
+  RCInt operator-() const { return {-Val, Long}; }
+  RCInt operator~() const { return {~Val, Long}; }
+
+  friend raw_ostream &operator<<(raw_ostream &OS, const RCInt &Int) {
+    return OS << Int.Val << (Int.Long ? "L" : "");
+  }
+};
+
 // A class holding a name - either an integer or a reference to the string.
 class IntOrString {
 private:
   union Data {
-    uint32_t Int;
+    RCInt Int;
     StringRef String;
-    Data(uint32_t Value) : Int(Value) {}
+    Data(RCInt Value) : Int(Value) {}
     Data(const StringRef Value) : String(Value) {}
     Data(const RCToken &Token) {
       if (Token.kind() == RCToken::Kind::Int)
-        Int = Token.intValue();
+        Int = RCInt(Token);
       else
         String = Token.value();
     }
@@ -40,8 +85,9 @@ private:
   bool IsInt;
 
 public:
-  IntOrString() : IntOrString(0) {}
+  IntOrString() : IntOrString(RCInt(0)) {}
   IntOrString(uint32_t Value) : Data(Value), IsInt(1) {}
+  IntOrString(RCInt Value) : Data(Value), IsInt(1) {}
   IntOrString(StringRef Value) : Data(Value), IsInt(0) {}
   IntOrString(const RCToken &Token)
       : Data(Token), IsInt(Token.kind() == RCToken::Kind::Int) {}
@@ -52,7 +98,7 @@ public:
 
   bool isInt() const { return IsInt; }
 
-  uint32_t getInt() const {
+  RCInt getInt() const {
     assert(IsInt);
     return Data.Int;
   }
@@ -570,8 +616,15 @@ public:
 // A single VERSIONINFO statement;
 class VersionInfoStmt {
 public:
+  enum StmtKind { StBase = 0, StBlock = 1, StValue = 2 };
+
   virtual raw_ostream &log(raw_ostream &OS) const { return OS << "VI stmt\n"; }
   virtual ~VersionInfoStmt() {}
+
+  virtual StmtKind getKind() const { return StBase; }
+  static bool classof(const VersionInfoStmt *S) {
+    return S->getKind() == StBase;
+  }
 };
 
 // BLOCK definition; also the main VERSIONINFO declaration is considered a
@@ -579,25 +632,38 @@ public:
 // The correct top-level blocks are "VarFileInfo" and "StringFileInfo". We don't
 // care about them at the parsing phase.
 class VersionInfoBlock : public VersionInfoStmt {
+public:
   std::vector<std::unique_ptr<VersionInfoStmt>> Stmts;
   StringRef Name;
 
-public:
   VersionInfoBlock(StringRef BlockName) : Name(BlockName) {}
   void addStmt(std::unique_ptr<VersionInfoStmt> Stmt) {
     Stmts.push_back(std::move(Stmt));
   }
   raw_ostream &log(raw_ostream &) const override;
+
+  StmtKind getKind() const override { return StBlock; }
+  static bool classof(const VersionInfoStmt *S) {
+    return S->getKind() == StBlock;
+  }
 };
 
 class VersionInfoValue : public VersionInfoStmt {
+public:
   StringRef Key;
   std::vector<IntOrString> Values;
+  std::vector<bool> HasPrecedingComma;
 
-public:
-  VersionInfoValue(StringRef InfoKey, std::vector<IntOrString> &&Vals)
-      : Key(InfoKey), Values(std::move(Vals)) {}
+  VersionInfoValue(StringRef InfoKey, std::vector<IntOrString> &&Vals,
+                   std::vector<bool> &&CommasBeforeVals)
+      : Key(InfoKey), Values(std::move(Vals)),
+        HasPrecedingComma(std::move(CommasBeforeVals)) {}
   raw_ostream &log(raw_ostream &) const override;
+
+  StmtKind getKind() const override { return StValue; }
+  static bool classof(const VersionInfoStmt *S) {
+    return S->getKind() == StValue;
+  }
 };
 
 class VersionInfoResource : public RCResource {
@@ -641,16 +707,24 @@ public:
     raw_ostream &log(raw_ostream &) const;
   };
 
-private:
   VersionInfoBlock MainBlock;
   VersionInfoFixed FixedData;
 
-public:
   VersionInfoResource(VersionInfoBlock &&TopLevelBlock,
                       VersionInfoFixed &&FixedInfo)
       : MainBlock(std::move(TopLevelBlock)), FixedData(std::move(FixedInfo)) {}
 
   raw_ostream &log(raw_ostream &) const override;
+  IntOrString getResourceType() const override { return RkVersionInfo; }
+  uint16_t getMemoryFlags() const override { return MfMoveable | MfPure; }
+  Twine getResourceTypeName() const override { return "VERSIONINFO"; }
+  Error visit(Visitor *V) const override {
+    return V->visitVersionInfoResource(this);
+  }
+  ResourceKind getKind() const override { return RkVersionInfo; }
+  static bool classof(const RCResource *Res) {
+    return Res->getKind() == RkVersionInfo;
+  }
 };
 
 // CHARACTERISTICS optional statement.

Modified: llvm/trunk/tools/llvm-rc/ResourceScriptToken.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceScriptToken.cpp?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceScriptToken.cpp (original)
+++ llvm/trunk/tools/llvm-rc/ResourceScriptToken.cpp Fri Oct  6 14:26:06 2017
@@ -56,6 +56,10 @@ uint32_t RCToken::intValue() const {
   return Result;
 }
 
+bool RCToken::isLongInt() const {
+  return TokenKind == Kind::Int && std::toupper(TokenValue.back()) == 'L';
+}
+
 StringRef RCToken::value() const { return TokenValue; }
 
 Kind RCToken::kind() const { return TokenKind; }

Modified: llvm/trunk/tools/llvm-rc/ResourceScriptToken.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceScriptToken.h?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceScriptToken.h (original)
+++ llvm/trunk/tools/llvm-rc/ResourceScriptToken.h Fri Oct  6 14:26:06 2017
@@ -56,6 +56,7 @@ public:
 
   // Get an integer value of the integer token.
   uint32_t intValue() const;
+  bool isLongInt() const;
 
   StringRef value() const;
   Kind kind() const;

Modified: llvm/trunk/tools/llvm-rc/ResourceVisitor.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-rc/ResourceVisitor.h?rev=315110&r1=315109&r2=315110&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-rc/ResourceVisitor.h (original)
+++ llvm/trunk/tools/llvm-rc/ResourceVisitor.h Fri Oct  6 14:26:06 2017
@@ -37,6 +37,7 @@ public:
   virtual Error visitHTMLResource(const RCResource *) = 0;
   virtual Error visitIconResource(const RCResource *) = 0;
   virtual Error visitMenuResource(const RCResource *) = 0;
+  virtual Error visitVersionInfoResource(const RCResource *) = 0;
 
   virtual Error visitCaptionStmt(const CaptionStmt *) = 0;
   virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0;




More information about the llvm-commits mailing list