forked from OSchip/llvm-project
[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 llvm-svn: 315110
This commit is contained in:
parent
c3ab013aa1
commit
07bc04ff38
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue