[llvm-rc] Serialize MENU resources to .res files (serialization, pt 3).

This allows MENU resources to be serialized.

MENU resource statement doc:
msdn.microsoft.com/en-us/library/windows/desktop/aa381025.aspx
POPUP sub-statement doc:
msdn.microsoft.com/en-us/library/windows/desktop/aa381030.aspx
MENUITEM sub-statement doc:
msdn.microsoft.com/en-us/library/windows/desktop/aa381024.aspx
MENUHEADER structure:
msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx (and
NORMALMENUITEM, POPUPMENUITEM structs).

Thanks for Nico Weber for his original work in this area.

Differential Revision: https://reviews.llvm.org/D37828

llvm-svn: 314562
diff --git a/llvm/tools/llvm-rc/ResourceFileWriter.cpp b/llvm/tools/llvm-rc/ResourceFileWriter.cpp
index f8ba2a7..af002e3 100644
--- a/llvm/tools/llvm-rc/ResourceFileWriter.cpp
+++ b/llvm/tools/llvm-rc/ResourceFileWriter.cpp
@@ -205,6 +205,10 @@
   return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
 }
 
+Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
+  return writeResource(Res, &ResourceFileWriter::writeMenuBody);
+}
+
 Error ResourceFileWriter::visitCharacteristicsStmt(
     const CharacteristicsStmt *Stmt) {
   ObjectData.Characteristics = Stmt->Value;
@@ -382,5 +386,55 @@
   return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
 }
 
+// --- MenuResource helpers. --- //
+
+Error ResourceFileWriter::writeMenuDefinition(
+    const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
+  assert(Def);
+  const MenuDefinition *DefPtr = Def.get();
+
+  if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
+    writeInt<uint16_t>(Flags);
+    RETURN_IF_ERROR(
+        checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
+    writeInt<uint16_t>(MenuItemPtr->Id);
+    RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
+    return Error::success();
+  }
+
+  if (isa<MenuSeparator>(DefPtr)) {
+    writeInt<uint16_t>(Flags);
+    writeInt<uint32_t>(0);
+    return Error::success();
+  }
+
+  auto *PopupPtr = cast<PopupItem>(DefPtr);
+  writeInt<uint16_t>(Flags);
+  RETURN_IF_ERROR(writeCString(PopupPtr->Name));
+  return writeMenuDefinitionList(PopupPtr->SubItems);
+}
+
+Error ResourceFileWriter::writeMenuDefinitionList(
+    const MenuDefinitionList &List) {
+  for (auto &Def : List.Definitions) {
+    uint16_t Flags = Def->getResFlags();
+    // Last element receives an additional 0x80 flag.
+    const uint16_t LastElementFlag = 0x0080;
+    if (&Def == &List.Definitions.back())
+      Flags |= LastElementFlag;
+
+    RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
+  }
+  return Error::success();
+}
+
+Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
+  // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
+  // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
+  writeObject<uint32_t>(0);
+
+  return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
+}
+
 } // namespace rc
 } // namespace llvm
diff --git a/llvm/tools/llvm-rc/ResourceFileWriter.h b/llvm/tools/llvm-rc/ResourceFileWriter.h
index ce730d2..ef736ba 100644
--- a/llvm/tools/llvm-rc/ResourceFileWriter.h
+++ b/llvm/tools/llvm-rc/ResourceFileWriter.h
@@ -32,6 +32,7 @@
   Error visitNullResource(const RCResource *) override;
   Error visitAcceleratorsResource(const RCResource *) override;
   Error visitHTMLResource(const RCResource *) override;
+  Error visitMenuResource(const RCResource *) override;
 
   Error visitCharacteristicsStmt(const CharacteristicsStmt *) override;
   Error visitLanguageStmt(const LanguageResource *) override;
@@ -63,6 +64,12 @@
   // HTMLResource
   Error writeHTMLBody(const RCResource *);
 
+  // MenuResource
+  Error writeMenuDefinition(const std::unique_ptr<MenuDefinition> &,
+                            uint16_t Flags);
+  Error writeMenuDefinitionList(const MenuDefinitionList &List);
+  Error writeMenuBody(const RCResource *);
+
   // Output stream handling.
   std::unique_ptr<raw_fd_ostream> FS;
 
diff --git a/llvm/tools/llvm-rc/ResourceScriptStmt.h b/llvm/tools/llvm-rc/ResourceScriptStmt.h
index 6f83d72..d00e1784 100644
--- a/llvm/tools/llvm-rc/ResourceScriptStmt.h
+++ b/llvm/tools/llvm-rc/ResourceScriptStmt.h
@@ -315,6 +315,8 @@
     MENUBREAK = 0x0040
   };
 
+  enum MenuDefKind { MkBase, MkSeparator, MkMenuItem, MkPopup };
+
   static constexpr size_t NumFlags = 6;
   static StringRef OptionsStr[NumFlags];
   static uint32_t OptionsFlags[NumFlags];
@@ -323,13 +325,16 @@
     return OS << "Base menu definition\n";
   }
   virtual ~MenuDefinition() {}
+
+  virtual uint16_t getResFlags() const { return 0; }
+  virtual MenuDefKind getKind() const { return MkBase; }
 };
 
 // Recursive description of a whole submenu.
 class MenuDefinitionList : public MenuDefinition {
+public:
   std::vector<std::unique_ptr<MenuDefinition>> Definitions;
 
-public:
   void addDefinition(std::unique_ptr<MenuDefinition> Def) {
     Definitions.push_back(std::move(Def));
   }
@@ -342,46 +347,73 @@
 class MenuSeparator : public MenuDefinition {
 public:
   raw_ostream &log(raw_ostream &) const override;
+
+  MenuDefKind getKind() const override { return MkSeparator; }
+  static bool classof(const MenuDefinition *D) {
+    return D->getKind() == MkSeparator;
+  }
 };
 
 // MENUITEM statement definition.
 //
 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx
 class MenuItem : public MenuDefinition {
+public:
   StringRef Name;
   uint32_t Id;
   uint16_t Flags;
 
-public:
   MenuItem(StringRef Caption, uint32_t ItemId, uint16_t ItemFlags)
       : Name(Caption), Id(ItemId), Flags(ItemFlags) {}
   raw_ostream &log(raw_ostream &) const override;
+
+  uint16_t getResFlags() const override { return Flags; }
+  MenuDefKind getKind() const override { return MkMenuItem; }
+  static bool classof(const MenuDefinition *D) {
+    return D->getKind() == MkMenuItem;
+  }
 };
 
 // POPUP statement definition.
 //
 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381030(v=vs.85).aspx
 class PopupItem : public MenuDefinition {
+public:
   StringRef Name;
   uint16_t Flags;
   MenuDefinitionList SubItems;
 
-public:
   PopupItem(StringRef Caption, uint16_t ItemFlags,
             MenuDefinitionList &&SubItemsList)
       : Name(Caption), Flags(ItemFlags), SubItems(std::move(SubItemsList)) {}
   raw_ostream &log(raw_ostream &) const override;
+
+  // This has an additional (0x10) flag. It doesn't match with documented
+  // 0x01 flag, though.
+  uint16_t getResFlags() const override { return Flags | 0x10; }
+  MenuDefKind getKind() const override { return MkPopup; }
+  static bool classof(const MenuDefinition *D) {
+    return D->getKind() == MkPopup;
+  }
 };
 
 // Menu resource definition.
 class MenuResource : public OptStatementsRCResource {
+public:
   MenuDefinitionList Elements;
 
-public:
   MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items)
       : OptStatementsRCResource(std::move(OptStmts)),
         Elements(std::move(Items)) {}
   raw_ostream &log(raw_ostream &) const override;
+
+  IntOrString getResourceType() const override { return RkMenu; }
+  Twine getResourceTypeName() const override { return "MENU"; }
+  Error visit(Visitor *V) const override { return V->visitMenuResource(this); }
+  ResourceKind getKind() const override { return RkMenu; }
+  static bool classof(const RCResource *Res) {
+    return Res->getKind() == RkMenu;
+  }
 };
 
 // STRINGTABLE resource. Contains a list of strings, each having its unique ID.
diff --git a/llvm/tools/llvm-rc/ResourceVisitor.h b/llvm/tools/llvm-rc/ResourceVisitor.h
index 358a31c..14992da 100644
--- a/llvm/tools/llvm-rc/ResourceVisitor.h
+++ b/llvm/tools/llvm-rc/ResourceVisitor.h
@@ -30,6 +30,7 @@
   virtual Error visitNullResource(const RCResource *) = 0;
   virtual Error visitAcceleratorsResource(const RCResource *) = 0;
   virtual Error visitHTMLResource(const RCResource *) = 0;
+  virtual Error visitMenuResource(const RCResource *) = 0;
 
   virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0;
   virtual Error visitLanguageStmt(const LanguageResource *) = 0;