diff options
author | Marek Sokolowski <mnbvmar@gmail.com> | 2017-09-29 19:07:44 +0000 |
---|---|---|
committer | Marek Sokolowski <mnbvmar@gmail.com> | 2017-09-29 19:07:44 +0000 |
commit | b121e77d2ef4e7dd30fbbe9404315b5852bcd1c9 (patch) | |
tree | 583060e610d99e89c1917610c1627b81518050a9 /tools/llvm-rc | |
parent | e0d97dea2f357daa9c052a91bfb5c0248cf901eb (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.cpp | 149 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceFileWriter.h | 15 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptStmt.h | 47 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceVisitor.h | 5 |
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() {} }; |