Imported Upstream version 5.18.0.167

Former-commit-id: 289509151e0fee68a1b591a20c9f109c3c789d3a
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-10-20 08:25:10 +00:00
parent e19d552987
commit b084638f15
28489 changed files with 184 additions and 3866856 deletions

View File

@ -1,17 +0,0 @@
set(LLVM_LINK_COMPONENTS
Option
Support
)
set(LLVM_TARGET_DEFINITIONS Opts.td)
tablegen(LLVM Opts.inc -gen-opt-parser-defs)
add_public_tablegen_target(RcTableGen)
add_llvm_tool(llvm-rc
llvm-rc.cpp
ResourceFileWriter.cpp
ResourceScriptParser.cpp
ResourceScriptStmt.cpp
ResourceScriptToken.cpp
)

View File

@ -1,22 +0,0 @@
;===- ./tools/llvm-rc/LLVMBuild.txt ----------------------------*- Conf -*--===;
;
; The LLVM Compiler Infrastructure
;
; This file is distributed under the University of Illinois Open Source
; License. See LICENSE.TXT for details.
;
;===------------------------------------------------------------------------===;
;
; This is an LLVMBuild description file for the components in this subdirectory.
;
; For more information on the LLVMBuild system, please see:
;
; http://llvm.org/docs/LLVMBuild.html
;
;===------------------------------------------------------------------------===;
[component_0]
type = Tool
name = llvm-rc
parent = Tools
required_libraries = Option

View File

@ -1,56 +0,0 @@
include "llvm/Option/OptParser.td"
// All the switches can be preceded by either '/' or '-'.
// These options seem to be important for the tool
// and should be implemented.
def FILEOUT : Separate<[ "/", "-" ], "FO">,
HelpText<"Change the output file location.">;
def DEFINE : Separate<[ "/", "-" ], "D">,
HelpText<"Define a symbol for the C preprocessor.">;
def UNDEF : Separate<[ "/", "-" ], "U">,
HelpText<"Undefine a symbol for the C preprocessor.">;
def LANG_ID : Separate<[ "/", "-" ], "L">,
HelpText<"Set the default language identifier.">;
def LANG_NAME : Separate<[ "/", "-" ], "LN">,
HelpText<"Set the default language name.">;
def INCLUDE : Separate<[ "/", "-" ], "I">, HelpText<"Add an include path.">;
def NOINCLUDE : Flag<[ "/", "-" ], "X">, HelpText<"Ignore 'include' variable.">;
def ADD_NULL : Flag<[ "/", "-" ], "N">,
HelpText<"Null-terminate all strings in the string table.">;
def DUPID_NOWARN : Flag<[ "/", "-" ], "Y">,
HelpText<"Suppress warnings on duplicate resource IDs.">;
def VERBOSE : Flag<[ "/", "-" ], "V">, HelpText<"Be verbose.">;
def HELP : Flag<[ "/", "-" ], "?">, HelpText<"Display this help and exit.">;
def H : Flag<[ "/", "-" ], "H">,
Alias<HELP>,
HelpText<"Display this help and exit.">;
def DRY_RUN : Flag<[ "/", "-" ], "dry-run">,
HelpText<"Don't compile the input; only try to parse it.">;
// Unused switches (at least for now). These will stay unimplemented
// in an early stage of development and can be ignored. However, we need to
// parse them in order to preserve the compatibility with the original tool.
def NOLOGO : Flag<[ "/", "-" ], "NOLOGO">;
def R : Flag<[ "/", "-" ], "R">;
def SL : Flag<[ "/", "-" ], "SL">;
// (Codepages support.)
def C : Flag<[ "/", "-" ], "C">;
def W : Flag<[ "/", "-" ], "W">;
// (Support of MUI and similar.)
def FM : Separate<[ "/", "-" ], "FM">;
def Q : Separate<[ "/", "-" ], "Q">;
def G : Flag<[ "/", "-" ], "G">;
def GN : Flag<[ "/", "-" ], "GN">;
def G1 : Flag<[ "/", "-" ], "G1">;
def G2 : Flag<[ "/", "-" ], "G2">;

File diff suppressed because it is too large Load Diff

View File

@ -1,195 +0,0 @@
//===-- ResourceSerializator.h ----------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// This defines a visitor serializing resources to a .res stream.
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H
#define LLVM_TOOLS_LLVMRC_RESOURCESERIALIZATOR_H
#include "ResourceScriptStmt.h"
#include "ResourceVisitor.h"
#include "llvm/Support/Endian.h"
namespace llvm {
class MemoryBuffer;
namespace rc {
struct SearchParams {
std::vector<std::string> Include; // Additional folders to search for files.
std::vector<std::string> NoInclude; // Folders to exclude from file search.
StringRef InputFilePath; // The full path of the input file.
};
class ResourceFileWriter : public Visitor {
public:
ResourceFileWriter(const SearchParams &Params,
std::unique_ptr<raw_fd_ostream> Stream)
: Params(Params), FS(std::move(Stream)), IconCursorID(1) {
assert(FS && "Output stream needs to be provided to the serializator");
}
Error visitNullResource(const RCResource *) override;
Error visitAcceleratorsResource(const RCResource *) override;
Error visitCursorResource(const RCResource *) override;
Error visitDialogResource(const RCResource *) override;
Error visitHTMLResource(const RCResource *) override;
Error visitIconResource(const RCResource *) override;
Error visitMenuResource(const RCResource *) override;
Error visitVersionInfoResource(const RCResource *) override;
Error visitStringTableResource(const RCResource *) override;
Error visitUserDefinedResource(const RCResource *) override;
Error visitCaptionStmt(const CaptionStmt *) override;
Error visitCharacteristicsStmt(const CharacteristicsStmt *) override;
Error visitFontStmt(const FontStmt *) override;
Error visitLanguageStmt(const LanguageResource *) override;
Error visitStyleStmt(const StyleStmt *) override;
Error visitVersionStmt(const VersionStmt *) override;
// Stringtables are output at the end of .res file. We need a separate
// function to do it.
Error dumpAllStringTables();
bool AppendNull; // Append '\0' to each existing STRINGTABLE element?
struct ObjectInfo {
uint16_t LanguageInfo;
uint32_t Characteristics;
uint32_t VersionInfo;
Optional<uint32_t> Style;
StringRef Caption;
struct FontInfo {
uint32_t Size;
StringRef Typeface;
uint32_t Weight;
bool IsItalic;
uint32_t Charset;
};
Optional<FontInfo> Font;
ObjectInfo() : LanguageInfo(0), Characteristics(0), VersionInfo(0) {}
} ObjectData;
struct StringTableInfo {
// Each STRINGTABLE bundle depends on ID of the bundle and language
// description.
using BundleKey = std::pair<uint16_t, uint16_t>;
// Each bundle is in fact an array of 16 strings.
struct Bundle {
std::array<Optional<StringRef>, 16> Data;
ObjectInfo DeclTimeInfo;
Bundle(const ObjectInfo &Info) : DeclTimeInfo(Info) {}
};
std::map<BundleKey, Bundle> BundleData;
// Bundles are listed in the order of their first occurence.
std::vector<BundleKey> BundleList;
} StringTableData;
private:
Error handleError(Error Err, const RCResource *Res);
Error
writeResource(const RCResource *Res,
Error (ResourceFileWriter::*BodyWriter)(const RCResource *));
// NullResource
Error writeNullBody(const RCResource *);
// AcceleratorsResource
Error writeSingleAccelerator(const AcceleratorsResource::Accelerator &,
bool IsLastItem);
Error writeAcceleratorsBody(const RCResource *);
// CursorResource and IconResource
Error visitIconOrCursorResource(const RCResource *);
Error visitIconOrCursorGroup(const RCResource *);
Error visitSingleIconOrCursor(const RCResource *);
Error writeSingleIconOrCursorBody(const RCResource *);
Error writeIconOrCursorGroupBody(const RCResource *);
// DialogResource
Error writeSingleDialogControl(const Control &, bool IsExtended);
Error writeDialogBody(const RCResource *);
// HTMLResource
Error writeHTMLBody(const RCResource *);
// MenuResource
Error writeMenuDefinition(const std::unique_ptr<MenuDefinition> &,
uint16_t Flags);
Error writeMenuDefinitionList(const MenuDefinitionList &List);
Error writeMenuBody(const RCResource *);
// StringTableResource
Error visitStringTableBundle(const RCResource *);
Error writeStringTableBundleBody(const RCResource *);
Error insertStringIntoBundle(StringTableInfo::Bundle &Bundle,
uint16_t StringID, StringRef String);
// User defined resource
Error writeUserDefinedBody(const RCResource *);
// VersionInfoResource
Error writeVersionInfoBody(const RCResource *);
Error writeVersionInfoBlock(const VersionInfoBlock &);
Error writeVersionInfoValue(const VersionInfoValue &);
const SearchParams &Params;
// Output stream handling.
std::unique_ptr<raw_fd_ostream> FS;
uint64_t tell() const { return FS->tell(); }
uint64_t writeObject(const ArrayRef<uint8_t> Data);
template <typename T> uint64_t writeInt(const T &Value) {
support::detail::packed_endian_specific_integral<T, support::little,
support::unaligned>
Object(Value);
return writeObject(Object);
}
template <typename T> uint64_t writeObject(const T &Value) {
return writeObject(ArrayRef<uint8_t>(
reinterpret_cast<const uint8_t *>(&Value), sizeof(T)));
}
template <typename T> void writeObjectAt(const T &Value, uint64_t Position) {
FS->pwrite((const char *)&Value, sizeof(T), Position);
}
Error writeCString(StringRef Str, bool WriteTerminator = true);
Error writeIdentifier(const IntOrString &Ident);
Error writeIntOrString(const IntOrString &Data);
void writeRCInt(RCInt);
Error appendFile(StringRef Filename);
void padStream(uint64_t Length);
Expected<std::unique_ptr<MemoryBuffer>> loadFile(StringRef File) const;
// Icon and cursor IDs are allocated starting from 1 and increasing for
// each icon/cursor dumped. This maintains the current ID to be allocated.
uint16_t IconCursorID;
};
} // namespace rc
} // namespace llvm
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,187 +0,0 @@
//===-- ResourceScriptParser.h ----------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// This defines the RC scripts parser. It takes a sequence of RC tokens
// and then provides the method to parse the resources one by one.
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H
#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTPARSER_H
#include "ResourceScriptStmt.h"
#include "ResourceScriptToken.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include <system_error>
#include <vector>
namespace llvm {
namespace opt {
class InputArgList;
}
namespace rc {
class RCParser {
public:
using LocIter = std::vector<RCToken>::iterator;
using ParseType = Expected<std::unique_ptr<RCResource>>;
using ParseOptionType = Expected<std::unique_ptr<OptionalStmt>>;
// Class describing a single failure of parser.
class ParserError : public ErrorInfo<ParserError> {
public:
ParserError(const Twine &Expected, const LocIter CurLoc, const LocIter End);
void log(raw_ostream &OS) const override { OS << CurMessage; }
std::error_code convertToErrorCode() const override {
return std::make_error_code(std::errc::invalid_argument);
}
const std::string &getMessage() const { return CurMessage; }
static char ID; // Keep llvm::Error happy.
private:
std::string CurMessage;
LocIter ErrorLoc, FileEnd;
};
explicit RCParser(std::vector<RCToken> TokenList);
// Reads and returns a single resource definition, or error message if any
// occurred.
ParseType parseSingleResource();
bool isEof() const;
private:
using Kind = RCToken::Kind;
// Checks if the current parser state points to the token of type TokenKind.
bool isNextTokenKind(Kind TokenKind) const;
// These methods assume that the parser is not in EOF state.
// Take a look at the current token. Do not fetch it.
const RCToken &look() const;
// Read the current token and advance the state by one token.
const RCToken &read();
// Advance the state by one token, discarding the current token.
void consume();
// The following methods try to read a single token, check if it has the
// correct type and then parse it.
// Each integer can be written as an arithmetic expression producing an
// unsigned 32-bit 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<RCInt> parseIntExpr1();
Expected<RCInt> parseIntExpr2();
// Advance the state by one, discarding the current token.
// If the discarded token had an incorrect type, fail.
Error consumeType(Kind TokenKind);
// Check the current token type. If it's TokenKind, discard it.
// Return true if the parser consumed this token successfully.
bool consumeOptionalType(Kind TokenKind);
// Read at least MinCount, and at most MaxCount integers separated by
// 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<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
// flag (0-based) has been read, the result is OR-ed with FlagValues[i].
// As long as parser has a comma to read, it expects to be fed with
// a correct flag afterwards.
Expected<uint32_t> parseFlags(ArrayRef<StringRef> FlagDesc,
ArrayRef<uint32_t> FlagValues);
// Reads a set of optional statements. These can change the behavior of
// a number of resource types (e.g. STRINGTABLE, MENU or DIALOG) if provided
// before the main block with the contents of the resource.
// Usually, resources use a basic set of optional statements:
// CHARACTERISTICS, LANGUAGE, VERSION
// However, DIALOG and DIALOGEX extend this list by the following items:
// CAPTION, CLASS, EXSTYLE, FONT, MENU, STYLE
// UseExtendedStatements flag (off by default) allows the parser to read
// the additional types of statements.
//
// Ref (to the list of all optional statements):
// msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx
enum class OptStmtType { BasicStmt, DialogStmt, DialogExStmt };
Expected<OptionalStmtList>
parseOptionalStatements(OptStmtType StmtsType = OptStmtType::BasicStmt);
// Read a single optional statement.
Expected<std::unique_ptr<OptionalStmt>>
parseSingleOptionalStatement(OptStmtType StmtsType = OptStmtType::BasicStmt);
// Top-level resource parsers.
ParseType parseLanguageResource();
ParseType parseAcceleratorsResource();
ParseType parseCursorResource();
ParseType parseDialogResource(bool IsExtended);
ParseType parseIconResource();
ParseType parseHTMLResource();
ParseType parseMenuResource();
ParseType parseStringTableResource();
ParseType parseUserDefinedResource(IntOrString Type);
ParseType parseVersionInfoResource();
// Helper DIALOG parser - a single control.
Expected<Control> parseControl();
// 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();
ParseOptionType parseVersionStmt();
ParseOptionType parseCaptionStmt();
ParseOptionType parseFontStmt(OptStmtType DialogType);
ParseOptionType parseStyleStmt();
// Raises an error. If IsAlreadyRead = false (default), this complains about
// the token that couldn't be parsed. If the flag is on, this complains about
// the correctly read token that makes no sense (that is, the current parser
// state is beyond the erroneous token.)
Error getExpectedError(const Twine &Message, bool IsAlreadyRead = false);
std::vector<RCToken> Tokens;
LocIter CurLoc;
const LocIter End;
};
} // namespace rc
} // namespace llvm
#endif

View File

@ -1,266 +0,0 @@
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// This implements methods defined in ResourceScriptStmt.h.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx
//
//===---------------------------------------------------------------------===//
#include "ResourceScriptStmt.h"
namespace llvm {
namespace rc {
raw_ostream &operator<<(raw_ostream &OS, const IntOrString &Item) {
if (Item.IsInt)
return OS << Item.Data.Int;
else
return OS << Item.Data.String;
}
raw_ostream &OptionalStmtList::log(raw_ostream &OS) const {
for (const auto &Stmt : Statements) {
OS << " Option: ";
Stmt->log(OS);
}
return OS;
}
raw_ostream &LanguageResource::log(raw_ostream &OS) const {
return OS << "Language: " << Lang << ", Sublanguage: " << SubLang << "\n";
}
StringRef AcceleratorsResource::Accelerator::OptionsStr
[AcceleratorsResource::Accelerator::NumFlags] = {
"ASCII", "VIRTKEY", "NOINVERT", "ALT", "SHIFT", "CONTROL"};
uint32_t AcceleratorsResource::Accelerator::OptionsFlags
[AcceleratorsResource::Accelerator::NumFlags] = {ASCII, VIRTKEY, NOINVERT,
ALT, SHIFT, CONTROL};
raw_ostream &AcceleratorsResource::log(raw_ostream &OS) const {
OS << "Accelerators (" << ResName << "): \n";
OptStatements->log(OS);
for (const auto &Acc : Accelerators) {
OS << " Accelerator: " << Acc.Event << " " << Acc.Id;
for (size_t i = 0; i < Accelerator::NumFlags; ++i)
if (Acc.Flags & Accelerator::OptionsFlags[i])
OS << " " << Accelerator::OptionsStr[i];
OS << "\n";
}
return OS;
}
raw_ostream &CursorResource::log(raw_ostream &OS) const {
return OS << "Cursor (" << ResName << "): " << CursorLoc << "\n";
}
raw_ostream &IconResource::log(raw_ostream &OS) const {
return OS << "Icon (" << ResName << "): " << IconLoc << "\n";
}
raw_ostream &HTMLResource::log(raw_ostream &OS) const {
return OS << "HTML (" << ResName << "): " << HTMLLoc << "\n";
}
StringRef MenuDefinition::OptionsStr[MenuDefinition::NumFlags] = {
"CHECKED", "GRAYED", "HELP", "INACTIVE", "MENUBARBREAK", "MENUBREAK"};
uint32_t MenuDefinition::OptionsFlags[MenuDefinition::NumFlags] = {
CHECKED, GRAYED, HELP, INACTIVE, MENUBARBREAK, MENUBREAK};
raw_ostream &MenuDefinition::logFlags(raw_ostream &OS, uint16_t Flags) {
for (size_t i = 0; i < NumFlags; ++i)
if (Flags & OptionsFlags[i])
OS << " " << OptionsStr[i];
return OS;
}
raw_ostream &MenuDefinitionList::log(raw_ostream &OS) const {
OS << " Menu list starts\n";
for (auto &Item : Definitions)
Item->log(OS);
return OS << " Menu list ends\n";
}
raw_ostream &MenuItem::log(raw_ostream &OS) const {
OS << " MenuItem (" << Name << "), ID = " << Id;
logFlags(OS, Flags);
return OS << "\n";
}
raw_ostream &MenuSeparator::log(raw_ostream &OS) const {
return OS << " Menu separator\n";
}
raw_ostream &PopupItem::log(raw_ostream &OS) const {
OS << " Popup (" << Name << ")";
logFlags(OS, Flags);
OS << ":\n";
return SubItems.log(OS);
}
raw_ostream &MenuResource::log(raw_ostream &OS) const {
OS << "Menu (" << ResName << "):\n";
OptStatements->log(OS);
return Elements.log(OS);
}
raw_ostream &StringTableResource::log(raw_ostream &OS) const {
OS << "StringTable:\n";
OptStatements->log(OS);
for (const auto &String : Table)
OS << " " << String.first << " => " << String.second << "\n";
return OS;
}
const StringMap<Control::CtlInfo> Control::SupportedCtls = {
{"LTEXT", CtlInfo{0x50020000, ClsStatic, true}},
{"CTEXT", CtlInfo{0x50020001, ClsStatic, true}},
{"RTEXT", CtlInfo{0x50020002, ClsStatic, true}},
{"PUSHBUTTON", CtlInfo{0x50010000, ClsButton, true}},
{"DEFPUSHBUTTON", CtlInfo{0x50010001, ClsButton, true}},
{"EDITTEXT", CtlInfo{0x50810000, ClsEdit, false}},
};
raw_ostream &Control::log(raw_ostream &OS) const {
OS << " Control (" << ID << "): " << Type << ", title: " << Title
<< ", loc: (" << X << ", " << Y << "), size: [" << Width << ", " << Height
<< "]";
if (Style)
OS << ", style: " << *Style;
if (ExtStyle)
OS << ", ext. style: " << *ExtStyle;
if (HelpID)
OS << ", help ID: " << *HelpID;
return OS << "\n";
}
raw_ostream &DialogResource::log(raw_ostream &OS) const {
OS << "Dialog" << (IsExtended ? "Ex" : "") << " (" << ResName << "): loc: ("
<< X << ", " << Y << "), size: [" << Width << ", " << Height
<< "], help ID: " << HelpID << "\n";
OptStatements->log(OS);
for (auto &Ctl : Controls)
Ctl.log(OS);
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 << " =>";
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";
}
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 &UserDefinedResource::log(raw_ostream &OS) const {
OS << "User-defined (type: " << Type << ", name: " << ResName << "): ";
if (IsFileResource)
return OS << FileLoc << "\n";
OS << "data = ";
for (auto &Item : Contents)
OS << Item << " ";
return OS << "\n";
}
raw_ostream &CharacteristicsStmt::log(raw_ostream &OS) const {
return OS << "Characteristics: " << Value << "\n";
}
raw_ostream &VersionStmt::log(raw_ostream &OS) const {
return OS << "Version: " << Value << "\n";
}
raw_ostream &CaptionStmt::log(raw_ostream &OS) const {
return OS << "Caption: " << Value << "\n";
}
raw_ostream &FontStmt::log(raw_ostream &OS) const {
OS << "Font: size = " << Size << ", face = " << Name
<< ", weight = " << Weight;
if (Italic)
OS << ", italic";
return OS << ", charset = " << Charset << "\n";
}
raw_ostream &StyleStmt::log(raw_ostream &OS) const {
return OS << "Style: " << Value << "\n";
}
} // namespace rc
} // namespace llvm

File diff suppressed because it is too large Load Diff

View File

@ -1,366 +0,0 @@
//===-- ResourceScriptToken.cpp ---------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// This file implements an interface defined in ResourceScriptToken.h.
// In particular, it defines an .rc script tokenizer.
//
//===---------------------------------------------------------------------===//
#include "ResourceScriptToken.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <cstdlib>
#include <utility>
using namespace llvm;
using Kind = RCToken::Kind;
// Checks if Representation is a correct description of an RC integer.
// It should be a 32-bit unsigned integer, either decimal, octal (0[0-7]+),
// or hexadecimal (0x[0-9a-f]+). It might be followed by a single 'L'
// character (that is the difference between our representation and
// StringRef's one). If Representation is correct, 'true' is returned and
// the return value is put back in Num.
static bool rcGetAsInteger(StringRef Representation, uint32_t &Num) {
size_t Length = Representation.size();
if (Length == 0)
return false;
// Strip the last 'L' if unnecessary.
if (std::toupper(Representation.back()) == 'L')
Representation = Representation.drop_back(1);
return !Representation.getAsInteger<uint32_t>(0, Num);
}
RCToken::RCToken(RCToken::Kind RCTokenKind, StringRef Value)
: TokenKind(RCTokenKind), TokenValue(Value) {}
uint32_t RCToken::intValue() const {
assert(TokenKind == Kind::Int);
// We assume that the token already is a correct integer (checked by
// rcGetAsInteger).
uint32_t Result;
bool IsSuccess = rcGetAsInteger(TokenValue, Result);
assert(IsSuccess);
(void)IsSuccess; // Silence the compiler warning when -DNDEBUG flag is on.
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; }
bool RCToken::isBinaryOp() const {
switch (TokenKind) {
case Kind::Plus:
case Kind::Minus:
case Kind::Pipe:
case Kind::Amp:
return true;
default:
return false;
}
}
static Error getStringError(const Twine &message) {
return make_error<StringError>("Error parsing file: " + message,
inconvertibleErrorCode());
}
namespace {
class Tokenizer {
public:
Tokenizer(StringRef Input) : Data(Input), DataLength(Input.size()) {}
Expected<std::vector<RCToken>> run();
private:
// All 'advancing' methods return boolean values; if they're equal to false,
// the stream has ended or failed.
bool advance(size_t Amount = 1);
bool skipWhitespaces();
// Consumes a token. If any problem occurred, a non-empty Error is returned.
Error consumeToken(const Kind TokenKind);
// Check if tokenizer is about to read FollowingChars.
bool willNowRead(StringRef FollowingChars) const;
// Check if tokenizer can start reading an identifier at current position.
// The original tool did non specify the rules to determine what is a correct
// identifier. We assume they should follow the C convention:
// [a-zA-Z_][a-zA-Z0-9_]*.
bool canStartIdentifier() const;
// Check if tokenizer can continue reading an identifier.
bool canContinueIdentifier() const;
// Check if tokenizer can start reading an integer.
// A correct integer always starts with a 0-9 digit,
// can contain characters 0-9A-Fa-f (digits),
// Ll (marking the integer is 32-bit), Xx (marking the representation
// is hexadecimal). As some kind of separator should come after the
// integer, we can consume the integer until a non-alphanumeric
// character.
bool canStartInt() const;
bool canContinueInt() const;
bool canStartString() const;
// Check if tokenizer can start reading a single line comment (e.g. a comment
// that begins with '//')
bool canStartLineComment() const;
// Check if tokenizer can start or finish reading a block comment (e.g. a
// comment that begins with '/*' and ends with '*/')
bool canStartBlockComment() const;
// Throw away all remaining characters on the current line.
void skipCurrentLine();
bool streamEof() const;
// Classify the token that is about to be read from the current position.
Kind classifyCurrentToken() const;
// Process the Kind::Identifier token - check if it is
// an identifier describing a block start or end.
void processIdentifier(RCToken &token) const;
StringRef Data;
size_t DataLength, Pos;
};
void Tokenizer::skipCurrentLine() {
Pos = Data.find_first_of("\r\n", Pos);
Pos = Data.find_first_not_of("\r\n", Pos);
if (Pos == StringRef::npos)
Pos = DataLength;
}
Expected<std::vector<RCToken>> Tokenizer::run() {
Pos = 0;
std::vector<RCToken> Result;
// Consume an optional UTF-8 Byte Order Mark.
if (willNowRead("\xef\xbb\xbf"))
advance(3);
while (!streamEof()) {
if (!skipWhitespaces())
break;
Kind TokenKind = classifyCurrentToken();
if (TokenKind == Kind::Invalid)
return getStringError("Invalid token found at position " + Twine(Pos));
const size_t TokenStart = Pos;
if (Error TokenError = consumeToken(TokenKind))
return std::move(TokenError);
// Comments are just deleted, don't bother saving them.
if (TokenKind == Kind::LineComment || TokenKind == Kind::StartComment)
continue;
RCToken Token(TokenKind, Data.take_front(Pos).drop_front(TokenStart));
if (TokenKind == Kind::Identifier) {
processIdentifier(Token);
} else if (TokenKind == Kind::Int) {
uint32_t TokenInt;
if (!rcGetAsInteger(Token.value(), TokenInt)) {
// The integer has incorrect format or cannot be represented in
// a 32-bit integer.
return getStringError("Integer invalid or too large: " +
Token.value().str());
}
}
Result.push_back(Token);
}
return Result;
}
bool Tokenizer::advance(size_t Amount) {
Pos += Amount;
return !streamEof();
}
bool Tokenizer::skipWhitespaces() {
while (!streamEof() && std::isspace(Data[Pos]))
advance();
return !streamEof();
}
Error Tokenizer::consumeToken(const Kind TokenKind) {
switch (TokenKind) {
// One-character token consumption.
#define TOKEN(Name)
#define SHORT_TOKEN(Name, Ch) case Kind::Name:
#include "ResourceScriptTokenList.def"
advance();
return Error::success();
case Kind::LineComment:
advance(2);
skipCurrentLine();
return Error::success();
case Kind::StartComment: {
advance(2);
auto EndPos = Data.find("*/", Pos);
if (EndPos == StringRef::npos)
return getStringError(
"Unclosed multi-line comment beginning at position " + Twine(Pos));
advance(EndPos - Pos);
advance(2);
return Error::success();
}
case Kind::Identifier:
while (!streamEof() && canContinueIdentifier())
advance();
return Error::success();
case Kind::Int:
while (!streamEof() && canContinueInt())
advance();
return Error::success();
case Kind::String:
// Consume the preceding 'L', if there is any.
if (std::toupper(Data[Pos]) == 'L')
advance();
// Consume the double-quote.
advance();
// Consume the characters until the end of the file, line or string.
while (true) {
if (streamEof()) {
return getStringError("Unterminated string literal.");
} else if (Data[Pos] == '"') {
// Consume the ending double-quote.
advance();
// However, if another '"' follows this double-quote, the string didn't
// end and we just included '"' into the string.
if (!willNowRead("\""))
return Error::success();
} else if (Data[Pos] == '\n') {
return getStringError("String literal not terminated in the line.");
}
advance();
}
case Kind::Invalid:
assert(false && "Cannot consume an invalid token.");
}
llvm_unreachable("Unknown RCToken::Kind");
}
bool Tokenizer::willNowRead(StringRef FollowingChars) const {
return Data.drop_front(Pos).startswith(FollowingChars);
}
bool Tokenizer::canStartIdentifier() const {
assert(!streamEof());
const char CurChar = Data[Pos];
return std::isalpha(CurChar) || CurChar == '_';
}
bool Tokenizer::canContinueIdentifier() const {
assert(!streamEof());
const char CurChar = Data[Pos];
return std::isalnum(CurChar) || CurChar == '_';
}
bool Tokenizer::canStartInt() const {
assert(!streamEof());
return std::isdigit(Data[Pos]);
}
bool Tokenizer::canStartBlockComment() const {
assert(!streamEof());
return Data.drop_front(Pos).startswith("/*");
}
bool Tokenizer::canStartLineComment() const {
assert(!streamEof());
return Data.drop_front(Pos).startswith("//");
}
bool Tokenizer::canContinueInt() const {
assert(!streamEof());
return std::isalnum(Data[Pos]);
}
bool Tokenizer::canStartString() const {
return willNowRead("\"") || willNowRead("L\"") || willNowRead("l\"");
}
bool Tokenizer::streamEof() const { return Pos == DataLength; }
Kind Tokenizer::classifyCurrentToken() const {
if (canStartBlockComment())
return Kind::StartComment;
if (canStartLineComment())
return Kind::LineComment;
if (canStartInt())
return Kind::Int;
if (canStartString())
return Kind::String;
// BEGIN and END are at this point of lexing recognized as identifiers.
if (canStartIdentifier())
return Kind::Identifier;
const char CurChar = Data[Pos];
switch (CurChar) {
// One-character token classification.
#define TOKEN(Name)
#define SHORT_TOKEN(Name, Ch) \
case Ch: \
return Kind::Name;
#include "ResourceScriptTokenList.def"
default:
return Kind::Invalid;
}
}
void Tokenizer::processIdentifier(RCToken &Token) const {
assert(Token.kind() == Kind::Identifier);
StringRef Name = Token.value();
if (Name.equals_lower("begin"))
Token = RCToken(Kind::BlockBegin, Name);
else if (Name.equals_lower("end"))
Token = RCToken(Kind::BlockEnd, Name);
}
} // anonymous namespace
namespace llvm {
Expected<std::vector<RCToken>> tokenizeRC(StringRef Input) {
return Tokenizer(Input).run();
}
} // namespace llvm

View File

@ -1,83 +0,0 @@
//===-- ResourceScriptToken.h -----------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// This declares the .rc script tokens and defines an interface for tokenizing
// the input data. The list of available tokens is located at
// ResourceScriptTokenList.def.
//
// Note that the tokenizer does not support comments or preprocessor
// directives. The preprocessor should do its work on the .rc file before
// running llvm-rc.
//
// As for now, it is possible to parse ASCII files only (the behavior on
// UTF files might be undefined). However, it already consumes UTF-8 BOM, if
// there is any. Thus, ASCII-compatible UTF-8 files are tokenized correctly.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380599(v=vs.85).aspx
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTTOKEN_H
#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTTOKEN_H
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <cstdint>
#include <map>
#include <string>
#include <vector>
namespace llvm {
// A definition of a single resource script token. Each token has its kind
// (declared in ResourceScriptTokenList) and holds a value - a reference
// representation of the token.
// RCToken does not claim ownership on its value. A memory buffer containing
// the token value should be stored in a safe place and cannot be freed
// nor reallocated.
class RCToken {
public:
enum class Kind {
#define TOKEN(Name) Name,
#define SHORT_TOKEN(Name, Ch) Name,
#include "ResourceScriptTokenList.def"
};
RCToken(RCToken::Kind RCTokenKind, StringRef Value);
// Get an integer value of the integer token.
uint32_t intValue() const;
bool isLongInt() const;
StringRef value() const;
Kind kind() const;
// Check if a token describes a binary operator.
bool isBinaryOp() const;
private:
Kind TokenKind;
StringRef TokenValue;
};
// Tokenize Input.
// In case no error occured, the return value contains
// tokens in order they were in the input file.
// In case of any error, the return value contains
// a textual representation of error.
//
// Tokens returned by this function hold only references to the parts
// of the Input. Memory buffer containing Input cannot be freed,
// modified or reallocated.
Expected<std::vector<RCToken>> tokenizeRC(StringRef Input);
} // namespace llvm
#endif

View File

@ -1,40 +0,0 @@
//===-- ResourceScriptTokenList.h -------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// This is a part of llvm-rc tokenizer. It lists all the possible tokens
// that might occur in a correct .rc script.
//
//===---------------------------------------------------------------------===//
// Long tokens. They might consist of more than one character.
TOKEN(Invalid) // Invalid token. Should not occur in a valid script.
TOKEN(Int) // Integer (decimal, octal or hexadecimal).
TOKEN(String) // String value.
TOKEN(Identifier) // Script identifier (resource name or type).
TOKEN(LineComment) // Beginning of single-line comment.
TOKEN(StartComment) // Beginning of multi-line comment.
// Short tokens. They usually consist of exactly one character.
// The definitions are of the form SHORT_TOKEN(TokenName, TokenChar).
// TokenChar is the one-character token representation occuring in the correct
// .rc scripts.
SHORT_TOKEN(BlockBegin, '{') // Start of the script block; can also be BEGIN.
SHORT_TOKEN(BlockEnd, '}') // End of the block; can also be END.
SHORT_TOKEN(Comma, ',') // Comma - resource arguments separator.
SHORT_TOKEN(Plus, '+') // Addition operator.
SHORT_TOKEN(Minus, '-') // Subtraction operator.
SHORT_TOKEN(Pipe, '|') // Bitwise-OR operator.
SHORT_TOKEN(Amp, '&') // Bitwise-AND operator.
SHORT_TOKEN(Tilde, '~') // Bitwise-NOT operator.
SHORT_TOKEN(LeftParen, '(') // Left parenthesis in the script expressions.
SHORT_TOKEN(RightParen, ')') // Right parenthesis.
#undef TOKEN
#undef SHORT_TOKEN

View File

@ -1,57 +0,0 @@
//===-- ResourceVisitor.h ---------------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// This defines a base class visiting resource script resources.
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H
#define LLVM_TOOLS_LLVMRC_RESOURCEVISITOR_H
#include "llvm/Support/Error.h"
namespace llvm {
namespace rc {
class RCResource;
class CaptionStmt;
class CharacteristicsStmt;
class FontStmt;
class LanguageResource;
class StyleStmt;
class VersionStmt;
class Visitor {
public:
virtual Error visitNullResource(const RCResource *) = 0;
virtual Error visitAcceleratorsResource(const RCResource *) = 0;
virtual Error visitCursorResource(const RCResource *) = 0;
virtual Error visitDialogResource(const RCResource *) = 0;
virtual Error visitHTMLResource(const RCResource *) = 0;
virtual Error visitIconResource(const RCResource *) = 0;
virtual Error visitMenuResource(const RCResource *) = 0;
virtual Error visitStringTableResource(const RCResource *) = 0;
virtual Error visitUserDefinedResource(const RCResource *) = 0;
virtual Error visitVersionInfoResource(const RCResource *) = 0;
virtual Error visitCaptionStmt(const CaptionStmt *) = 0;
virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0;
virtual Error visitFontStmt(const FontStmt *) = 0;
virtual Error visitLanguageStmt(const LanguageResource *) = 0;
virtual Error visitStyleStmt(const StyleStmt *) = 0;
virtual Error visitVersionStmt(const VersionStmt *) = 0;
virtual ~Visitor() {}
};
} // namespace rc
} // namespace llvm
#endif

View File

@ -1,185 +0,0 @@
//===-- llvm-rc.cpp - Compile .rc scripts into .res -------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Compile .rc scripts into .res files. This is intended to be a
// platform-independent port of Microsoft's rc.exe tool.
//
//===----------------------------------------------------------------------===//
#include "ResourceFileWriter.h"
#include "ResourceScriptParser.h"
#include "ResourceScriptStmt.h"
#include "ResourceScriptToken.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <system_error>
using namespace llvm;
using namespace llvm::rc;
namespace {
// Input options tables.
enum ID {
OPT_INVALID = 0, // This is not a correct option ID.
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES) \
OPT_##ID,
#include "Opts.inc"
#undef OPTION
};
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "Opts.inc"
#undef PREFIX
static const opt::OptTable::Info InfoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES) \
{ \
PREFIX, NAME, HELPTEXT, \
METAVAR, OPT_##ID, opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, \
OPT_##ALIAS, ALIASARGS, VALUES},
#include "Opts.inc"
#undef OPTION
};
class RcOptTable : public opt::OptTable {
public:
RcOptTable() : OptTable(InfoTable, /* IgnoreCase = */ true) {}
};
static ExitOnError ExitOnErr;
LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) {
errs() << Message << "\n";
exit(1);
}
} // anonymous namespace
int main(int argc_, const char *argv_[]) {
sys::PrintStackTraceOnErrorSignal(argv_[0]);
PrettyStackTraceProgram X(argc_, argv_);
ExitOnErr.setBanner("llvm-rc: ");
SmallVector<const char *, 256> argv;
SpecificBumpPtrAllocator<char> ArgAllocator;
ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector(
argv, makeArrayRef(argv_, argc_), ArgAllocator)));
llvm_shutdown_obj Y;
RcOptTable T;
unsigned MAI, MAC;
ArrayRef<const char *> ArgsArr = makeArrayRef(argv_ + 1, argc_);
opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
// The tool prints nothing when invoked with no command-line arguments.
if (InputArgs.hasArg(OPT_HELP)) {
T.PrintHelp(outs(), "rc", "Resource Converter", false);
return 0;
}
const bool BeVerbose = InputArgs.hasArg(OPT_VERBOSE);
std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT);
if (InArgsInfo.size() != 1) {
fatalError("Exactly one input file should be provided.");
}
// Read and tokenize the input file.
ErrorOr<std::unique_ptr<MemoryBuffer>> File =
MemoryBuffer::getFile(InArgsInfo[0]);
if (!File) {
fatalError("Error opening file '" + Twine(InArgsInfo[0]) +
"': " + File.getError().message());
}
std::unique_ptr<MemoryBuffer> FileContents = std::move(*File);
StringRef Contents = FileContents->getBuffer();
std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(Contents));
if (BeVerbose) {
const Twine TokenNames[] = {
#define TOKEN(Name) #Name,
#define SHORT_TOKEN(Name, Ch) #Name,
#include "ResourceScriptTokenList.def"
};
for (const RCToken &Token : Tokens) {
outs() << TokenNames[static_cast<int>(Token.kind())] << ": "
<< Token.value();
if (Token.kind() == RCToken::Kind::Int)
outs() << "; int value = " << Token.intValue();
outs() << "\n";
}
}
SearchParams Params;
SmallString<128> InputFile(InArgsInfo[0]);
llvm::sys::fs::make_absolute(InputFile);
Params.InputFilePath = InputFile;
Params.Include = InputArgs.getAllArgValues(OPT_INCLUDE);
Params.NoInclude = InputArgs.getAllArgValues(OPT_NOINCLUDE);
std::unique_ptr<ResourceFileWriter> Visitor;
bool IsDryRun = InputArgs.hasArg(OPT_DRY_RUN);
if (!IsDryRun) {
auto OutArgsInfo = InputArgs.getAllArgValues(OPT_FILEOUT);
if (OutArgsInfo.size() != 1)
fatalError(
"Exactly one output file should be provided (using /FO flag).");
std::error_code EC;
auto FOut =
llvm::make_unique<raw_fd_ostream>(OutArgsInfo[0], EC, sys::fs::F_RW);
if (EC)
fatalError("Error opening output file '" + OutArgsInfo[0] +
"': " + EC.message());
Visitor = llvm::make_unique<ResourceFileWriter>(Params, std::move(FOut));
Visitor->AppendNull = InputArgs.hasArg(OPT_ADD_NULL);
ExitOnErr(NullResource().visit(Visitor.get()));
// Set the default language; choose en-US arbitrarily.
ExitOnErr(LanguageResource(0x09, 0x01).visit(Visitor.get()));
}
rc::RCParser Parser{std::move(Tokens)};
while (!Parser.isEof()) {
auto Resource = ExitOnErr(Parser.parseSingleResource());
if (BeVerbose)
Resource->log(outs());
if (!IsDryRun)
ExitOnErr(Resource->visit(Visitor.get()));
}
// STRINGTABLE resources come at the very end.
if (!IsDryRun)
ExitOnErr(Visitor->dumpAllStringTables());
return 0;
}