diff options
author | Zachary Turner <zturner@google.com> | 2017-10-06 21:26:06 +0000 |
---|---|---|
committer | Zachary Turner <zturner@google.com> | 2017-10-06 21:26:06 +0000 |
commit | 93bb30da3fff23193de80f377c0822ec160a3a2a (patch) | |
tree | 00c35c6bbfd64910b3f4daab988d07fea7f4d56c /tools/llvm-rc | |
parent | 4c72b95c52efc04bfb3cd498054704e8032abd54 (diff) |
[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
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@315110 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/llvm-rc')
-rw-r--r-- | tools/llvm-rc/ResourceFileWriter.cpp | 192 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceFileWriter.h | 8 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptParser.cpp | 40 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptParser.h | 10 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptStmt.cpp | 8 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptStmt.h | 96 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptToken.cpp | 4 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptToken.h | 1 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceVisitor.h | 1 |
9 files changed, 325 insertions, 35 deletions
diff --git a/tools/llvm-rc/ResourceFileWriter.cpp b/tools/llvm-rc/ResourceFileWriter.cpp index a15166e0edf..e6480f126ef 100644 --- a/tools/llvm-rc/ResourceFileWriter.cpp +++ b/tools/llvm-rc/ResourceFileWriter.cpp @@ -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(uint32_t Number, Twine FieldName, 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::writeIntOrString(const IntOrString &Value) { 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::visitMenuResource(const RCResource *Res) { 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(const RCResource *Base) { 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 diff --git a/tools/llvm-rc/ResourceFileWriter.h b/tools/llvm-rc/ResourceFileWriter.h index 8093dfe7968..0d248c0349e 100644 --- a/tools/llvm-rc/ResourceFileWriter.h +++ b/tools/llvm-rc/ResourceFileWriter.h @@ -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); diff --git a/tools/llvm-rc/ResourceScriptParser.cpp b/tools/llvm-rc/ResourceScriptParser.cpp index d8c081771cb..47f8745837e 100644 --- a/tools/llvm-rc/ResourceScriptParser.cpp +++ b/tools/llvm-rc/ResourceScriptParser.cpp @@ -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::parseIntExpr1() { 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::parseIntExpr2() { } case Kind::Int: - return read().intValue(); + return RCInt(read()); case Kind::LeftParen: { consume(); @@ -261,14 +261,14 @@ bool RCParser::consumeOptionalType(Kind TokenKind) { 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>> RCParser::parseVersionInfoStmt() { 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; diff --git a/tools/llvm-rc/ResourceScriptParser.h b/tools/llvm-rc/ResourceScriptParser.h index 85d103dfe03..f2afe6d7c32 100644 --- a/tools/llvm-rc/ResourceScriptParser.h +++ b/tools/llvm-rc/ResourceScriptParser.h @@ -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 diff --git a/tools/llvm-rc/ResourceScriptStmt.cpp b/tools/llvm-rc/ResourceScriptStmt.cpp index dbfed4c55a1..42505cc76d0 100644 --- a/tools/llvm-rc/ResourceScriptStmt.cpp +++ b/tools/llvm-rc/ResourceScriptStmt.cpp @@ -161,8 +161,12 @@ raw_ostream &VersionInfoBlock::log(raw_ostream &OS) const { 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"; } diff --git a/tools/llvm-rc/ResourceScriptStmt.h b/tools/llvm-rc/ResourceScriptStmt.h index 9addca90b8a..2457d831b1e 100644 --- a/tools/llvm-rc/ResourceScriptStmt.h +++ b/tools/llvm-rc/ResourceScriptStmt.h @@ -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. diff --git a/tools/llvm-rc/ResourceScriptToken.cpp b/tools/llvm-rc/ResourceScriptToken.cpp index df9e3d9caab..36027d14ba0 100644 --- a/tools/llvm-rc/ResourceScriptToken.cpp +++ b/tools/llvm-rc/ResourceScriptToken.cpp @@ -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; } diff --git a/tools/llvm-rc/ResourceScriptToken.h b/tools/llvm-rc/ResourceScriptToken.h index 6846e096848..af22fa8d3eb 100644 --- a/tools/llvm-rc/ResourceScriptToken.h +++ b/tools/llvm-rc/ResourceScriptToken.h @@ -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; diff --git a/tools/llvm-rc/ResourceVisitor.h b/tools/llvm-rc/ResourceVisitor.h index 16ffba6b9bd..c0d00287e8a 100644 --- a/tools/llvm-rc/ResourceVisitor.h +++ b/tools/llvm-rc/ResourceVisitor.h @@ -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; |