summaryrefslogtreecommitdiff
path: root/tools/llvm-rc
diff options
context:
space:
mode:
authorZachary Turner <zturner@google.com>2017-10-06 21:26:06 +0000
committerZachary Turner <zturner@google.com>2017-10-06 21:26:06 +0000
commit93bb30da3fff23193de80f377c0822ec160a3a2a (patch)
tree00c35c6bbfd64910b3f4daab988d07fea7f4d56c /tools/llvm-rc
parent4c72b95c52efc04bfb3cd498054704e8032abd54 (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.cpp192
-rw-r--r--tools/llvm-rc/ResourceFileWriter.h8
-rw-r--r--tools/llvm-rc/ResourceScriptParser.cpp40
-rw-r--r--tools/llvm-rc/ResourceScriptParser.h10
-rw-r--r--tools/llvm-rc/ResourceScriptStmt.cpp8
-rw-r--r--tools/llvm-rc/ResourceScriptStmt.h96
-rw-r--r--tools/llvm-rc/ResourceScriptToken.cpp4
-rw-r--r--tools/llvm-rc/ResourceScriptToken.h1
-rw-r--r--tools/llvm-rc/ResourceVisitor.h1
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;