summaryrefslogtreecommitdiff
path: root/tools/llvm-rc
diff options
context:
space:
mode:
authorMarek Sokolowski <mnbvmar@gmail.com>2017-09-29 19:07:44 +0000
committerMarek Sokolowski <mnbvmar@gmail.com>2017-09-29 19:07:44 +0000
commitb121e77d2ef4e7dd30fbbe9404315b5852bcd1c9 (patch)
tree583060e610d99e89c1917610c1627b81518050a9 /tools/llvm-rc
parente0d97dea2f357daa9c052a91bfb5c0248cf901eb (diff)
[llvm-rc] Serialize ACCELERATORS to .res files (serialization, pt 2).
This allows llvm-rc to serialize ACCELERATORS resources. Additionally, as this is the first type of resource to support basic optional resource statements (LANGUAGE, CHARACTERISTICS, VERSION), ACCELERATORS statement documentation: msdn.microsoft.com/en-us/library/windows/desktop/aa380610.aspx Accelerator table structure documentation: msdn.microsoft.com/en-us/library/windows/desktop/ms648010.aspx Optional resource statement fields are described in: msdn.microsoft.com/en-us/library/windows/desktop/ms648027.aspx Thanks for Nico Weber for his original work in this area. Differential Revision: https://reviews.llvm.org/D37824 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@314549 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/llvm-rc')
-rw-r--r--tools/llvm-rc/ResourceFileWriter.cpp149
-rw-r--r--tools/llvm-rc/ResourceFileWriter.h15
-rw-r--r--tools/llvm-rc/ResourceScriptStmt.h47
-rw-r--r--tools/llvm-rc/ResourceVisitor.h5
4 files changed, 205 insertions, 11 deletions
diff --git a/tools/llvm-rc/ResourceFileWriter.cpp b/tools/llvm-rc/ResourceFileWriter.cpp
index 642d6ef377c..f8ba2a7694a 100644
--- a/tools/llvm-rc/ResourceFileWriter.cpp
+++ b/tools/llvm-rc/ResourceFileWriter.cpp
@@ -28,6 +28,19 @@ using namespace llvm::support;
namespace llvm {
namespace rc {
+// Class that employs RAII to save the current serializator 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 {
+ ResourceFileWriter *FileWriter;
+ ResourceFileWriter::ObjectInfo SavedInfo;
+
+public:
+ ContextKeeper(ResourceFileWriter *V)
+ : FileWriter(V), SavedInfo(V->ObjectData) {}
+ ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
+};
+
static Error createError(Twine Message,
std::errc Type = std::errc::invalid_argument) {
return make_error<StringError>(Message, std::make_error_code(Type));
@@ -184,10 +197,20 @@ Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
return writeResource(Res, &ResourceFileWriter::writeNullBody);
}
+Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
+}
+
Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
}
+Error ResourceFileWriter::visitCharacteristicsStmt(
+ const CharacteristicsStmt *Stmt) {
+ ObjectData.Characteristics = Stmt->Value;
+ return Error::success();
+}
+
Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
@@ -195,6 +218,11 @@ Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
return Error::success();
}
+Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
+ ObjectData.VersionInfo = Stmt->Value;
+ return Error::success();
+}
+
Error ResourceFileWriter::writeResource(
const RCResource *Res,
Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
@@ -208,12 +236,16 @@ Error ResourceFileWriter::writeResource(
RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
+ // Apply the resource-local optional statements.
+ ContextKeeper RAII(this);
+ RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
+
padStream(sizeof(uint32_t));
object::WinResHeaderSuffix HeaderSuffix{
ulittle32_t(0), // DataVersion; seems to always be 0
ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo),
- ulittle32_t(0), // VersionInfo
- ulittle32_t(0)}; // Characteristics
+ ulittle32_t(ObjectData.VersionInfo),
+ ulittle32_t(ObjectData.Characteristics)};
writeObject(HeaderSuffix);
uint64_t DataLoc = tell();
@@ -229,10 +261,123 @@ Error ResourceFileWriter::writeResource(
return Error::success();
}
+// --- NullResource helpers. --- //
+
Error ResourceFileWriter::writeNullBody(const RCResource *) {
return Error::success();
}
+// --- AcceleratorsResource helpers. --- //
+
+Error ResourceFileWriter::writeSingleAccelerator(
+ const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
+ using Accelerator = AcceleratorsResource::Accelerator;
+ using Opt = Accelerator::Options;
+
+ struct AccelTableEntry {
+ ulittle16_t Flags;
+ ulittle16_t ANSICode;
+ ulittle16_t Id;
+ uint16_t Padding;
+ } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
+
+ bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
+
+ // Remove ASCII flags (which doesn't occur in .res files).
+ Entry.Flags = Obj.Flags & ~Opt::ASCII;
+
+ if (IsLastItem)
+ Entry.Flags |= 0x80;
+
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
+ Entry.Id = ulittle16_t(Obj.Id);
+
+ auto createAccError = [&Obj](const char *Msg) {
+ return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
+ };
+
+ if (IsASCII && IsVirtKey)
+ return createAccError("Accelerator can't be both ASCII and VIRTKEY");
+
+ if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
+ return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
+ " accelerators");
+
+ if (Obj.Event.isInt()) {
+ if (!IsASCII && !IsVirtKey)
+ return createAccError(
+ "Accelerator with a numeric event must be either ASCII"
+ " or VIRTKEY");
+
+ uint32_t EventVal = Obj.Event.getInt();
+ RETURN_IF_ERROR(
+ checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
+ Entry.ANSICode = ulittle16_t(EventVal);
+ writeObject(Entry);
+ return Error::success();
+ }
+
+ StringRef Str = Obj.Event.getString();
+ bool IsWide;
+ stripQuotes(Str, IsWide);
+
+ if (Str.size() == 0 || Str.size() > 2)
+ return createAccError(
+ "Accelerator string events should have length 1 or 2");
+
+ if (Str[0] == '^') {
+ if (Str.size() == 1)
+ return createAccError("No character following '^' in accelerator event");
+ if (IsVirtKey)
+ return createAccError(
+ "VIRTKEY accelerator events can't be preceded by '^'");
+
+ char Ch = Str[1];
+ if (Ch >= 'a' && Ch <= 'z')
+ Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
+ else if (Ch >= 'A' && Ch <= 'Z')
+ Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
+ else
+ return createAccError("Control character accelerator event should be"
+ " alphabetic");
+
+ writeObject(Entry);
+ return Error::success();
+ }
+
+ if (Str.size() == 2)
+ return createAccError("Event string should be one-character, possibly"
+ " preceded by '^'");
+
+ uint8_t EventCh = Str[0];
+ // The original tool just warns in this situation. We chose to fail.
+ if (IsVirtKey && !isalnum(EventCh))
+ return createAccError("Non-alphanumeric characters cannot describe virtual"
+ " keys");
+ if (EventCh > 0x7F)
+ return createAccError("Non-ASCII description of accelerator");
+
+ if (IsVirtKey)
+ EventCh = toupper(EventCh);
+ Entry.ANSICode = ulittle16_t(EventCh);
+ writeObject(Entry);
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
+ auto *Res = cast<AcceleratorsResource>(Base);
+ size_t AcceleratorId = 0;
+ for (auto &Acc : Res->Accelerators) {
+ ++AcceleratorId;
+ RETURN_IF_ERROR(
+ writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
+ }
+ return Error::success();
+}
+
+// --- HTMLResource helpers. --- //
+
+
Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
}
diff --git a/tools/llvm-rc/ResourceFileWriter.h b/tools/llvm-rc/ResourceFileWriter.h
index 2eaf2e377f4..ce730d20eed 100644
--- a/tools/llvm-rc/ResourceFileWriter.h
+++ b/tools/llvm-rc/ResourceFileWriter.h
@@ -30,14 +30,19 @@ public:
}
Error visitNullResource(const RCResource *) override;
+ Error visitAcceleratorsResource(const RCResource *) override;
Error visitHTMLResource(const RCResource *) override;
+ Error visitCharacteristicsStmt(const CharacteristicsStmt *) override;
Error visitLanguageStmt(const LanguageResource *) override;
+ Error visitVersionStmt(const VersionStmt *) override;
struct ObjectInfo {
uint16_t LanguageInfo;
+ uint32_t Characteristics;
+ uint32_t VersionInfo;
- ObjectInfo() : LanguageInfo(0) {}
+ ObjectInfo() : LanguageInfo(0), Characteristics(0), VersionInfo(0) {}
} ObjectData;
private:
@@ -47,7 +52,15 @@ private:
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 *);
+
+ // HTMLResource
Error writeHTMLBody(const RCResource *);
// Output stream handling.
diff --git a/tools/llvm-rc/ResourceScriptStmt.h b/tools/llvm-rc/ResourceScriptStmt.h
index 90fe0ac3465..6f83d722d24 100644
--- a/tools/llvm-rc/ResourceScriptStmt.h
+++ b/tools/llvm-rc/ResourceScriptStmt.h
@@ -104,9 +104,6 @@ enum MemoryFlags {
class RCResource {
public:
IntOrString ResName;
-
- RCResource() = default;
- RCResource(RCResource &&) = default;
void setName(const IntOrString &Name) { ResName = Name; }
virtual raw_ostream &log(raw_ostream &OS) const {
return OS << "Base statement\n";
@@ -117,6 +114,10 @@ public:
llvm_unreachable("This is unable to call methods from Visitor base");
}
+ // Apply the statements attached to this resource. Generic resources
+ // don't have any.
+ virtual Error applyStmts(Visitor *) const { return Error::success(); }
+
// By default, memory flags are DISCARDABLE | PURE | MOVEABLE.
virtual uint16_t getMemoryFlags() const {
return MfDiscardable | MfPure | MfMoveable;
@@ -153,11 +154,18 @@ class OptionalStmtList : public OptionalStmt {
public:
OptionalStmtList() {}
- virtual raw_ostream &log(raw_ostream &OS) const;
+ raw_ostream &log(raw_ostream &OS) const override;
void addStmt(std::unique_ptr<OptionalStmt> Stmt) {
Statements.push_back(std::move(Stmt));
}
+
+ Error visit(Visitor *V) const override {
+ for (auto &StmtPtr : Statements)
+ if (auto Err = StmtPtr->visit(V))
+ return Err;
+ return Error::success();
+ }
};
class OptStatementsRCResource : public RCResource {
@@ -166,6 +174,8 @@ public:
OptStatementsRCResource(OptionalStmtList &&Stmts)
: OptStatements(llvm::make_unique<OptionalStmtList>(std::move(Stmts))) {}
+
+ virtual Error applyStmts(Visitor *V) const { return OptStatements->visit(V); }
};
// LANGUAGE statement. It can occur both as a top-level statement (in such
@@ -218,14 +228,27 @@ public:
static uint32_t OptionsFlags[NumFlags];
};
+ std::vector<Accelerator> Accelerators;
+
using OptStatementsRCResource::OptStatementsRCResource;
void addAccelerator(IntOrString Event, uint32_t Id, uint16_t Flags) {
Accelerators.push_back(Accelerator{Event, Id, Flags});
}
raw_ostream &log(raw_ostream &) const override;
-private:
- std::vector<Accelerator> Accelerators;
+ IntOrString getResourceType() const override { return RkAccelerators; }
+ uint16_t getMemoryFlags() const override {
+ return MfPure | MfMoveable;
+ }
+ Twine getResourceTypeName() const override { return "ACCELERATORS"; }
+
+ Error visit(Visitor *V) const override {
+ return V->visitAcceleratorsResource(this);
+ }
+ ResourceKind getKind() const override { return RkAccelerators; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkAccelerators;
+ }
};
// CURSOR resource. Represents a single cursor (".cur") file.
@@ -546,22 +569,30 @@ public:
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380872(v=vs.85).aspx
class CharacteristicsStmt : public OptionalStmt {
+public:
uint32_t Value;
-public:
CharacteristicsStmt(uint32_t Characteristic) : Value(Characteristic) {}
raw_ostream &log(raw_ostream &) const override;
+
+ Twine getResourceTypeName() const override { return "CHARACTERISTICS"; }
+ Error visit(Visitor *V) const override {
+ return V->visitCharacteristicsStmt(this);
+ }
};
// VERSION optional statement.
//
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381059(v=vs.85).aspx
class VersionStmt : public OptionalStmt {
+public:
uint32_t Value;
-public:
VersionStmt(uint32_t Version) : Value(Version) {}
raw_ostream &log(raw_ostream &) const override;
+
+ Twine getResourceTypeName() const override { return "VERSION"; }
+ Error visit(Visitor *V) const override { return V->visitVersionStmt(this); }
};
// CAPTION optional statement.
diff --git a/tools/llvm-rc/ResourceVisitor.h b/tools/llvm-rc/ResourceVisitor.h
index 9c6f3a78ee3..358a31c875c 100644
--- a/tools/llvm-rc/ResourceVisitor.h
+++ b/tools/llvm-rc/ResourceVisitor.h
@@ -21,14 +21,19 @@ namespace rc {
class RCResource;
+class CharacteristicsStmt;
class LanguageResource;
+class VersionStmt;
class Visitor {
public:
virtual Error visitNullResource(const RCResource *) = 0;
+ virtual Error visitAcceleratorsResource(const RCResource *) = 0;
virtual Error visitHTMLResource(const RCResource *) = 0;
+ virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0;
virtual Error visitLanguageStmt(const LanguageResource *) = 0;
+ virtual Error visitVersionStmt(const VersionStmt *) = 0;
virtual ~Visitor() {}
};