1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 03:02:36 +01:00

[llvm-rc] Add VERSIONINFO parsing ability. [6/8]

This extends the set of llvm-rc parser's available resources by
another one, VERSIONINFO.

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

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

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

llvm-svn: 314468
This commit is contained in:
Marek Sokolowski 2017-09-28 22:41:38 +00:00
parent dffcfd6adb
commit 7414d53a94
12 changed files with 315 additions and 0 deletions

View File

@ -64,6 +64,34 @@
; PGOOD-NEXT: Control (5): EDITTEXT, title: , loc: (1, 2), size: [4, 7], style: 8
; PGOOD-NEXT: Dialog (25): loc: (1, 2), size: [3, 4], help ID: 0
; PGOOD-NEXT: DialogEx (26): loc: (1, 2), size: [3, 4], help ID: 0
; PGOOD-NEXT: VersionInfo (1):
; PGOOD-NEXT: Fixed: FILEVERSION: 1 2 3 4
; PGOOD-NEXT: Fixed: PRODUCTVERSION: 5 6 7 8
; PGOOD-NEXT: Fixed: FILEFLAGSMASK: 50
; PGOOD-NEXT: Fixed: FILEFLAGS: 555
; PGOOD-NEXT: Fixed: FILEOS: 110
; PGOOD-NEXT: Fixed: FILETYPE: 555555
; PGOOD-NEXT: Fixed: FILESUBTYPE: 14
; PGOOD-NEXT: Start of block (name: )
; PGOOD-NEXT: Start of block (name: "StringFileInfo")
; PGOOD-NEXT: Start of block (name: "040904E4")
; PGOOD-NEXT: "CompanyName" => "a"
; PGOOD-NEXT: "FileDescription" => "b"
; PGOOD-NEXT: "FileVersion" => "c"
; PGOOD-NEXT: "InternalName" => "d"
; PGOOD-NEXT: "LegalCopyright" => "e"
; PGOOD-NEXT: "LegalTrademarks1" => "f"
; PGOOD-NEXT: "LegalTrademarks2" => "g"
; PGOOD-NEXT: "OriginalFilename" => L"h"
; PGOOD-NEXT: "ProductName" => "ii" 2 3
; PGOOD-NEXT: "ProductVersion" =>
; PGOOD-NEXT: End of block
; PGOOD-NEXT: End of block
; PGOOD-NEXT: Start of block (name: "VarFileInfo")
; PGOOD-NEXT: "Translation" => 1033 1252
; PGOOD-NEXT: End of block
; PGOOD-NEXT: End of block
; RUN: not llvm-rc /V %p/Inputs/parser-stringtable-no-string.rc 2>&1 | FileCheck %s --check-prefix PSTRINGTABLE1
@ -184,3 +212,33 @@
; RUN: not llvm-rc /V %p/Inputs/parser-dialog-unnecessary-string.rc 2>&1 | FileCheck %s --check-prefix PDIALOG5
; PDIALOG5: llvm-rc: Error parsing file: expected integer, got "This shouldn't be here"
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-wrong-fixed.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO1
; PVERSIONINFO1: llvm-rc: Error parsing file: expected fixed VERSIONINFO statement type, got WEIRDFIXED
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-named-main-block.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO2
; PVERSIONINFO2: llvm-rc: Error parsing file: expected fixed VERSIONINFO statement type, got BLOCK
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-unnamed-inner-block.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO3
; PVERSIONINFO3: llvm-rc: Error parsing file: expected string, got {
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-unnamed-value.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO4
; PVERSIONINFO4: llvm-rc: Error parsing file: expected string, got END
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-bad-type.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO5
; PVERSIONINFO5: llvm-rc: Error parsing file: expected BLOCK or VALUE, got INCORRECT
; RUN: not llvm-rc /V %p/Inputs/parser-versioninfo-repeated-fixed.rc 2>&1 | FileCheck %s --check-prefix PVERSIONINFO6
; PVERSIONINFO6: llvm-rc: Error parsing file: expected yet unread fixed VERSIONINFO statement type, got FILEVERSION

View File

@ -77,6 +77,8 @@ RCParser::ParseType RCParser::parseSingleResource() {
Result = parseHTMLResource();
else if (TypeToken->equalsLower("MENU"))
Result = parseMenuResource();
else if (TypeToken->equalsLower("VERSIONINFO"))
Result = parseVersionInfoResource();
else
return getExpectedError("resource type", /* IsAlreadyRead = */ true);
@ -322,6 +324,13 @@ RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) {
return std::move(Dialog);
}
RCParser::ParseType RCParser::parseVersionInfoResource() {
ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed());
ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef()));
return make_unique<VersionInfoResource>(std::move(**BlockResult),
std::move(*FixedResult));
}
Expected<Control> RCParser::parseControl() {
// Each control definition (except CONTROL) follows one of the schemes below
// depending on the control class:
@ -446,6 +455,72 @@ RCParser::ParseType RCParser::parseStringTableResource() {
return std::move(Table);
}
Expected<std::unique_ptr<VersionInfoBlock>>
RCParser::parseVersionInfoBlockContents(StringRef BlockName) {
RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
auto Contents = make_unique<VersionInfoBlock>(BlockName);
while (!isNextTokenKind(Kind::BlockEnd)) {
ASSIGN_OR_RETURN(Stmt, parseVersionInfoStmt());
Contents->addStmt(std::move(*Stmt));
}
consume(); // Consume BlockEnd.
return std::move(Contents);
}
Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
// Expect either BLOCK or VALUE, then a name or a key (a string).
ASSIGN_OR_RETURN(TypeResult, readIdentifier());
if (TypeResult->equals_lower("BLOCK")) {
ASSIGN_OR_RETURN(NameResult, readString());
return parseVersionInfoBlockContents(*NameResult);
}
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.
std::vector<IntOrString> Values;
while (consumeOptionalType(Kind::Comma)) {
ASSIGN_OR_RETURN(ValueResult, readIntOrString());
Values.push_back(*ValueResult);
}
return make_unique<VersionInfoValue>(*KeyResult, std::move(Values));
}
return getExpectedError("BLOCK or VALUE", true);
}
Expected<VersionInfoResource::VersionInfoFixed>
RCParser::parseVersionInfoFixed() {
using RetType = VersionInfoResource::VersionInfoFixed;
RetType Result;
// Read until the beginning of the block.
while (!isNextTokenKind(Kind::BlockBegin)) {
ASSIGN_OR_RETURN(TypeResult, readIdentifier());
auto FixedType = RetType::getFixedType(*TypeResult);
if (!RetType::isTypeSupported(FixedType))
return getExpectedError("fixed VERSIONINFO statement type", true);
if (Result.IsTypePresent[FixedType])
return getExpectedError("yet unread fixed VERSIONINFO statement type",
true);
// VERSION variations take multiple integers.
size_t NumInts = RetType::isVersionType(FixedType) ? 4 : 1;
ASSIGN_OR_RETURN(ArgsResult, readIntsWithCommas(NumInts, NumInts));
Result.setValue(FixedType, *ArgsResult);
}
return Result;
}
RCParser::ParseOptionType RCParser::parseLanguageStmt() {
ASSIGN_OR_RETURN(Args, readIntsWithCommas(/* min = */ 2, /* max = */ 2));
return make_unique<LanguageResource>((*Args)[0], (*Args)[1]);

View File

@ -133,6 +133,7 @@ private:
ParseType parseHTMLResource();
ParseType parseMenuResource();
ParseType parseStringTableResource();
ParseType parseVersionInfoResource();
// Helper DIALOG parser - a single control.
Expected<Control> parseControl();
@ -140,6 +141,15 @@ private:
// Helper MENU parser.
Expected<MenuDefinitionList> parseMenuItemsList();
// Helper VERSIONINFO parser - read the contents of a single BLOCK statement,
// from BEGIN to END.
Expected<std::unique_ptr<VersionInfoBlock>>
parseVersionInfoBlockContents(StringRef BlockName);
// Helper VERSIONINFO parser - read either VALUE or BLOCK statement.
Expected<std::unique_ptr<VersionInfoStmt>> parseVersionInfoStmt();
// Helper VERSIONINFO parser - read fixed VERSIONINFO statements.
Expected<VersionInfoResource::VersionInfoFixed> parseVersionInfoFixed();
// Optional statement parsers.
ParseOptionType parseLanguageStmt();
ParseOptionType parseCharacteristicsStmt();

View File

@ -142,6 +142,78 @@ raw_ostream &DialogResource::log(raw_ostream &OS) const {
return OS;
}
raw_ostream &VersionInfoBlock::log(raw_ostream &OS) const {
OS << " Start of block (name: " << Name << ")\n";
for (auto &Stmt : Stmts)
Stmt->log(OS);
return OS << " End of block\n";
}
raw_ostream &VersionInfoValue::log(raw_ostream &OS) const {
OS << " " << Key << " =>";
for (auto &Value : Values)
OS << " " << Value;
return OS << "\n";
}
using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
using VersionInfoFixedType = VersionInfoFixed::VersionInfoFixedType;
const StringRef
VersionInfoFixed::FixedFieldsNames[VersionInfoFixed::FtNumTypes] = {
"", "FILEVERSION", "PRODUCTVERSION", "FILEFLAGSMASK",
"FILEFLAGS", "FILEOS", "FILETYPE", "FILESUBTYPE"};
const StringMap<VersionInfoFixedType> VersionInfoFixed::FixedFieldsInfoMap = {
{FixedFieldsNames[FtFileVersion], FtFileVersion},
{FixedFieldsNames[FtProductVersion], FtProductVersion},
{FixedFieldsNames[FtFileFlagsMask], FtFileFlagsMask},
{FixedFieldsNames[FtFileFlags], FtFileFlags},
{FixedFieldsNames[FtFileOS], FtFileOS},
{FixedFieldsNames[FtFileType], FtFileType},
{FixedFieldsNames[FtFileSubtype], FtFileSubtype}};
VersionInfoFixedType VersionInfoFixed::getFixedType(StringRef Type) {
auto UpperType = Type.upper();
auto Iter = FixedFieldsInfoMap.find(UpperType);
if (Iter != FixedFieldsInfoMap.end())
return Iter->getValue();
return FtUnknown;
}
bool VersionInfoFixed::isTypeSupported(VersionInfoFixedType Type) {
return FtUnknown < Type && Type < FtNumTypes;
}
bool VersionInfoFixed::isVersionType(VersionInfoFixedType Type) {
switch (Type) {
case FtFileVersion:
case FtProductVersion:
return true;
default:
return false;
}
}
raw_ostream &VersionInfoFixed::log(raw_ostream &OS) const {
for (int Type = FtUnknown; Type < FtNumTypes; ++Type) {
if (!isTypeSupported((VersionInfoFixedType)Type))
continue;
OS << " Fixed: " << FixedFieldsNames[Type] << ":";
for (uint32_t Val : FixedInfo[Type])
OS << " " << Val;
OS << "\n";
}
return OS;
}
raw_ostream &VersionInfoResource::log(raw_ostream &OS) const {
OS << "VersionInfo (" << ResName << "):\n";
FixedData.log(OS);
return MainBlock.log(OS);
}
raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const {
return OS << "Characteristics: " << Value << "\n";
}

View File

@ -319,6 +319,106 @@ public:
raw_ostream &log(raw_ostream &) const override;
};
// -- VERSIONINFO resource and its helper classes --
//
// This resource lists the version information on the executable/library.
// The declaration consists of the following items:
// * A number of fixed optional version statements (e.g. FILEVERSION, FILEOS)
// * BEGIN
// * A number of BLOCK and/or VALUE statements. BLOCK recursively defines
// another block of version information, whereas VALUE defines a
// key -> value correspondence. There might be more than one value
// corresponding to the single key.
// * END
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381058(v=vs.85).aspx
// A single VERSIONINFO statement;
class VersionInfoStmt {
public:
virtual raw_ostream &log(raw_ostream &OS) const { return OS << "VI stmt\n"; }
virtual ~VersionInfoStmt() {}
};
// BLOCK definition; also the main VERSIONINFO declaration is considered a
// BLOCK, although it has no name.
// The correct top-level blocks are "VarFileInfo" and "StringFileInfo". We don't
// care about them at the parsing phase.
class VersionInfoBlock : public VersionInfoStmt {
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;
};
class VersionInfoValue : public VersionInfoStmt {
StringRef Key;
std::vector<IntOrString> Values;
public:
VersionInfoValue(StringRef InfoKey, std::vector<IntOrString> &&Vals)
: Key(InfoKey), Values(std::move(Vals)) {}
raw_ostream &log(raw_ostream &) const override;
};
class VersionInfoResource : public RCResource {
public:
// A class listing fixed VERSIONINFO statements (occuring before main BEGIN).
// If any of these is not specified, it is assumed by the original tool to
// be equal to 0.
class VersionInfoFixed {
public:
enum VersionInfoFixedType {
FtUnknown,
FtFileVersion,
FtProductVersion,
FtFileFlagsMask,
FtFileFlags,
FtFileOS,
FtFileType,
FtFileSubtype,
FtNumTypes
};
private:
static const StringMap<VersionInfoFixedType> FixedFieldsInfoMap;
static const StringRef FixedFieldsNames[FtNumTypes];
public:
SmallVector<uint32_t, 4> FixedInfo[FtNumTypes];
SmallVector<bool, FtNumTypes> IsTypePresent;
static VersionInfoFixedType getFixedType(StringRef Type);
static bool isTypeSupported(VersionInfoFixedType Type);
static bool isVersionType(VersionInfoFixedType Type);
VersionInfoFixed() : IsTypePresent(FtNumTypes, false) {}
void setValue(VersionInfoFixedType Type, ArrayRef<uint32_t> Value) {
FixedInfo[Type] = SmallVector<uint32_t, 4>(Value.begin(), Value.end());
IsTypePresent[Type] = true;
}
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;
};
// CHARACTERISTICS optional statement.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx