Bug Summary

File:tools/llvm-rc/ResourceFileWriter.cpp
Warning:line 894, column 7
Called C++ object pointer is uninitialized

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name ResourceFileWriter.cpp -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -mrelocation-model pic -pic-level 2 -mthread-model posix -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -momit-leaf-frame-pointer -ffunction-sections -fdata-sections -resource-dir /usr/lib/llvm-8/lib/clang/8.0.0 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I /build/llvm-toolchain-snapshot-8~svn345461/build-llvm/tools/llvm-rc -I /build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc -I /build/llvm-toolchain-snapshot-8~svn345461/build-llvm/include -I /build/llvm-toolchain-snapshot-8~svn345461/include -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/x86_64-linux-gnu/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/x86_64-linux-gnu/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/backward -internal-isystem /usr/include/clang/8.0.0/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-comment -std=c++11 -fdeprecated-macro -fdebug-compilation-dir /build/llvm-toolchain-snapshot-8~svn345461/build-llvm/tools/llvm-rc -ferror-limit 19 -fmessage-length 0 -fvisibility-inlines-hidden -fobjc-runtime=gcc -fdiagnostics-show-option -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -o /tmp/scan-build-2018-10-27-211344-32123-1 -x c++ /build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp -faddrsig

/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp

1//===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===---------------------------------------------------------------------===//
9//
10// This implements the visitor serializing resources to a .res stream.
11//
12//===---------------------------------------------------------------------===//
13
14#include "ResourceFileWriter.h"
15
16#include "llvm/Object/WindowsResource.h"
17#include "llvm/Support/ConvertUTF.h"
18#include "llvm/Support/Endian.h"
19#include "llvm/Support/EndianStream.h"
20#include "llvm/Support/MemoryBuffer.h"
21#include "llvm/Support/Path.h"
22#include "llvm/Support/Process.h"
23
24using namespace llvm::support;
25
26// Take an expression returning llvm::Error and forward the error if it exists.
27#define RETURN_IF_ERROR(Expr)if (auto Err = (Expr)) return Err; \
28 if (auto Err = (Expr)) \
29 return Err;
30
31namespace llvm {
32namespace rc {
33
34// Class that employs RAII to save the current FileWriter object state
35// and revert to it as soon as we leave the scope. This is useful if resources
36// declare their own resource-local statements.
37class ContextKeeper {
38 ResourceFileWriter *FileWriter;
39 ResourceFileWriter::ObjectInfo SavedInfo;
40
41public:
42 ContextKeeper(ResourceFileWriter *V)
43 : FileWriter(V), SavedInfo(V->ObjectData) {}
44 ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
45};
46
47static Error createError(const Twine &Message,
48 std::errc Type = std::errc::invalid_argument) {
49 return make_error<StringError>(Message, std::make_error_code(Type));
50}
51
52static Error checkNumberFits(uint32_t Number, size_t MaxBits,
53 const Twine &FieldName) {
54 assert(1 <= MaxBits && MaxBits <= 32)((1 <= MaxBits && MaxBits <= 32) ? static_cast<
void> (0) : __assert_fail ("1 <= MaxBits && MaxBits <= 32"
, "/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp"
, 54, __PRETTY_FUNCTION__))
;
55 if (!(Number >> MaxBits))
56 return Error::success();
57 return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
58 Twine(MaxBits) + " bits.",
59 std::errc::value_too_large);
60}
61
62template <typename FitType>
63static Error checkNumberFits(uint32_t Number, const Twine &FieldName) {
64 return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
65}
66
67// A similar function for signed integers.
68template <typename FitType>
69static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName,
70 bool CanBeNegative) {
71 int32_t SignedNum = Number;
72 if (SignedNum < std::numeric_limits<FitType>::min() ||
73 SignedNum > std::numeric_limits<FitType>::max())
74 return createError(FieldName + " (" + Twine(SignedNum) +
75 ") does not fit in " + Twine(sizeof(FitType) * 8) +
76 "-bit signed integer type.",
77 std::errc::value_too_large);
78
79 if (!CanBeNegative && SignedNum < 0)
80 return createError(FieldName + " (" + Twine(SignedNum) +
81 ") cannot be negative.");
82
83 return Error::success();
84}
85
86static Error checkRCInt(RCInt Number, const Twine &FieldName) {
87 if (Number.isLong())
88 return Error::success();
89 return checkNumberFits<uint16_t>(Number, FieldName);
90}
91
92static Error checkIntOrString(IntOrString Value, const Twine &FieldName) {
93 if (!Value.isInt())
94 return Error::success();
95 return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
96}
97
98static bool stripQuotes(StringRef &Str, bool &IsLongString) {
99 if (!Str.contains('"'))
100 return false;
101
102 // Just take the contents of the string, checking if it's been marked long.
103 IsLongString = Str.startswith_lower("L");
104 if (IsLongString)
105 Str = Str.drop_front();
106
107 bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
108 (void)StripSuccess;
109 assert(StripSuccess && "Strings should be enclosed in quotes.")((StripSuccess && "Strings should be enclosed in quotes."
) ? static_cast<void> (0) : __assert_fail ("StripSuccess && \"Strings should be enclosed in quotes.\""
, "/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp"
, 109, __PRETTY_FUNCTION__))
;
110 return true;
111}
112
113static UTF16 cp1252ToUnicode(unsigned char C) {
114 static const UTF16 Map80[] = {
115 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
116 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
117 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
118 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
119 };
120 if (C >= 0x80 && C <= 0x9F)
121 return Map80[C - 0x80];
122 return C;
123}
124
125// Describes a way to handle '\0' characters when processing the string.
126// rc.exe tool sometimes behaves in a weird way in postprocessing.
127// If the string to be output is equivalent to a C-string (e.g. in MENU
128// titles), string is (predictably) truncated after first 0-byte.
129// When outputting a string table, the behavior is equivalent to appending
130// '\0\0' at the end of the string, and then stripping the string
131// before the first '\0\0' occurrence.
132// Finally, when handling strings in user-defined resources, 0-bytes
133// aren't stripped, nor do they terminate the string.
134
135enum class NullHandlingMethod {
136 UserResource, // Don't terminate string on '\0'.
137 CutAtNull, // Terminate string on '\0'.
138 CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
139};
140
141// Parses an identifier or string and returns a processed version of it:
142// * String the string boundary quotes.
143// * Squash "" to a single ".
144// * Replace the escape sequences with their processed version.
145// For identifiers, this is no-op.
146static Error processString(StringRef Str, NullHandlingMethod NullHandler,
147 bool &IsLongString, SmallVectorImpl<UTF16> &Result,
148 int CodePage) {
149 bool IsString = stripQuotes(Str, IsLongString);
150 SmallVector<UTF16, 128> Chars;
151
152 // Convert the input bytes according to the chosen codepage.
153 if (CodePage == CpUtf8) {
154 convertUTF8ToUTF16String(Str, Chars);
155 } else if (CodePage == CpWin1252) {
156 for (char C : Str)
157 Chars.push_back(cp1252ToUnicode((unsigned char)C));
158 } else {
159 // For other, unknown codepages, only allow plain ASCII input.
160 for (char C : Str) {
161 if ((unsigned char)C > 0x7F)
162 return createError("Non-ASCII 8-bit codepoint (" + Twine(C) +
163 ") can't be interpreted in the current codepage");
164 Chars.push_back((unsigned char)C);
165 }
166 }
167
168 if (!IsString) {
169 // It's an identifier if it's not a string. Make all characters uppercase.
170 for (UTF16 &Ch : Chars) {
171 assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII")((Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII"
) ? static_cast<void> (0) : __assert_fail ("Ch <= 0x7F && \"We didn't allow identifiers to be non-ASCII\""
, "/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp"
, 171, __PRETTY_FUNCTION__))
;
172 Ch = toupper(Ch);
173 }
174 Result.swap(Chars);
175 return Error::success();
176 }
177 Result.reserve(Chars.size());
178 size_t Pos = 0;
179
180 auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error {
181 if (!IsLongString) {
182 if (NullHandler == NullHandlingMethod::UserResource) {
183 // Narrow strings in user-defined resources are *not* output in
184 // UTF-16 format.
185 if (Char > 0xFF)
186 return createError("Non-8-bit codepoint (" + Twine(Char) +
187 ") can't occur in a user-defined narrow string");
188 }
189 }
190
191 Result.push_back(Char);
192 return Error::success();
193 };
194 auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error {
195 if (!IsLongString) {
196 // Escaped chars in narrow strings have to be interpreted according to
197 // the chosen code page.
198 if (Char > 0xFF)
199 return createError("Non-8-bit escaped char (" + Twine(Char) +
200 ") can't occur in narrow string");
201 if (CodePage == CpUtf8) {
202 if (Char >= 0x80)
203 return createError("Unable to interpret single byte (" + Twine(Char) +
204 ") as UTF-8");
205 } else if (CodePage == CpWin1252) {
206 Char = cp1252ToUnicode(Char);
207 } else {
208 // Unknown/unsupported codepage, only allow ASCII input.
209 if (Char > 0x7F)
210 return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) +
211 ") can't "
212 "occur in a non-Unicode string");
213 }
214 }
215
216 return AddRes(Char);
217 };
218
219 while (Pos < Chars.size()) {
220 UTF16 CurChar = Chars[Pos];
221 ++Pos;
222
223 // Strip double "".
224 if (CurChar == '"') {
225 if (Pos == Chars.size() || Chars[Pos] != '"')
226 return createError("Expected \"\"");
227 ++Pos;
228 RETURN_IF_ERROR(AddRes('"'))if (auto Err = (AddRes('"'))) return Err;;
229 continue;
230 }
231
232 if (CurChar == '\\') {
233 UTF16 TypeChar = Chars[Pos];
234 ++Pos;
235
236 if (TypeChar == 'x' || TypeChar == 'X') {
237 // Read a hex number. Max number of characters to read differs between
238 // narrow and wide strings.
239 UTF16 ReadInt = 0;
240 size_t RemainingChars = IsLongString ? 4 : 2;
241 // We don't want to read non-ASCII hex digits. std:: functions past
242 // 0xFF invoke UB.
243 //
244 // FIXME: actually, Microsoft version probably doesn't check this
245 // condition and uses their Unicode version of 'isxdigit'. However,
246 // there are some hex-digit Unicode character outside of ASCII, and
247 // some of these are actually accepted by rc.exe, the notable example
248 // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written
249 // instead of ASCII digits in \x... escape sequence and get accepted.
250 // However, the resulting hexcodes seem totally unpredictable.
251 // We think it's infeasible to try to reproduce this behavior, nor to
252 // put effort in order to detect it.
253 while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) {
254 if (!isxdigit(Chars[Pos]))
255 break;
256 char Digit = tolower(Chars[Pos]);
257 ++Pos;
258
259 ReadInt <<= 4;
260 if (isdigit(Digit))
261 ReadInt |= Digit - '0';
262 else
263 ReadInt |= Digit - 'a' + 10;
264
265 --RemainingChars;
266 }
267
268 RETURN_IF_ERROR(AddEscapedChar(ReadInt))if (auto Err = (AddEscapedChar(ReadInt))) return Err;;
269 continue;
270 }
271
272 if (TypeChar >= '0' && TypeChar < '8') {
273 // Read an octal number. Note that we've already read the first digit.
274 UTF16 ReadInt = TypeChar - '0';
275 size_t RemainingChars = IsLongString ? 6 : 2;
276
277 while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' &&
278 Chars[Pos] < '8') {
279 ReadInt <<= 3;
280 ReadInt |= Chars[Pos] - '0';
281 --RemainingChars;
282 ++Pos;
283 }
284
285 RETURN_IF_ERROR(AddEscapedChar(ReadInt))if (auto Err = (AddEscapedChar(ReadInt))) return Err;;
286
287 continue;
288 }
289
290 switch (TypeChar) {
291 case 'A':
292 case 'a':
293 // Windows '\a' translates into '\b' (Backspace).
294 RETURN_IF_ERROR(AddRes('\b'))if (auto Err = (AddRes('\b'))) return Err;;
295 break;
296
297 case 'n': // Somehow, RC doesn't recognize '\N' and '\R'.
298 RETURN_IF_ERROR(AddRes('\n'))if (auto Err = (AddRes('\n'))) return Err;;
299 break;
300
301 case 'r':
302 RETURN_IF_ERROR(AddRes('\r'))if (auto Err = (AddRes('\r'))) return Err;;
303 break;
304
305 case 'T':
306 case 't':
307 RETURN_IF_ERROR(AddRes('\t'))if (auto Err = (AddRes('\t'))) return Err;;
308 break;
309
310 case '\\':
311 RETURN_IF_ERROR(AddRes('\\'))if (auto Err = (AddRes('\\'))) return Err;;
312 break;
313
314 case '"':
315 // RC accepts \" only if another " comes afterwards; then, \"" means
316 // a single ".
317 if (Pos == Chars.size() || Chars[Pos] != '"')
318 return createError("Expected \\\"\"");
319 ++Pos;
320 RETURN_IF_ERROR(AddRes('"'))if (auto Err = (AddRes('"'))) return Err;;
321 break;
322
323 default:
324 // If TypeChar means nothing, \ is should be output to stdout with
325 // following char. However, rc.exe consumes these characters when
326 // dealing with wide strings.
327 if (!IsLongString) {
328 RETURN_IF_ERROR(AddRes('\\'))if (auto Err = (AddRes('\\'))) return Err;;
329 RETURN_IF_ERROR(AddRes(TypeChar))if (auto Err = (AddRes(TypeChar))) return Err;;
330 }
331 break;
332 }
333
334 continue;
335 }
336
337 // If nothing interesting happens, just output the character.
338 RETURN_IF_ERROR(AddRes(CurChar))if (auto Err = (AddRes(CurChar))) return Err;;
339 }
340
341 switch (NullHandler) {
342 case NullHandlingMethod::CutAtNull:
343 for (size_t Pos = 0; Pos < Result.size(); ++Pos)
344 if (Result[Pos] == '\0')
345 Result.resize(Pos);
346 break;
347
348 case NullHandlingMethod::CutAtDoubleNull:
349 for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos)
350 if (Result[Pos] == '\0' && Result[Pos + 1] == '\0')
351 Result.resize(Pos);
352 if (Result.size() > 0 && Result.back() == '\0')
353 Result.pop_back();
354 break;
355
356 case NullHandlingMethod::UserResource:
357 break;
358 }
359
360 return Error::success();
361}
362
363uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) {
364 uint64_t Result = tell();
365 FS->write((const char *)Data.begin(), Data.size());
366 return Result;
367}
368
369Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
370 SmallVector<UTF16, 128> ProcessedString;
371 bool IsLongString;
372 RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,if (auto Err = (processString(Str, NullHandlingMethod::CutAtNull
, IsLongString, ProcessedString, Params.CodePage))) return Err
;
373 IsLongString, ProcessedString,if (auto Err = (processString(Str, NullHandlingMethod::CutAtNull
, IsLongString, ProcessedString, Params.CodePage))) return Err
;
374 Params.CodePage))if (auto Err = (processString(Str, NullHandlingMethod::CutAtNull
, IsLongString, ProcessedString, Params.CodePage))) return Err
;
;
375 for (auto Ch : ProcessedString)
376 writeInt<uint16_t>(Ch);
377 if (WriteTerminator)
378 writeInt<uint16_t>(0);
379 return Error::success();
380}
381
382Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
383 return writeIntOrString(Ident);
384}
385
386Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
387 if (!Value.isInt())
388 return writeCString(Value.getString());
389
390 writeInt<uint16_t>(0xFFFF);
391 writeInt<uint16_t>(Value.getInt());
392 return Error::success();
393}
394
395void ResourceFileWriter::writeRCInt(RCInt Value) {
396 if (Value.isLong())
397 writeInt<uint32_t>(Value);
398 else
399 writeInt<uint16_t>(Value);
400}
401
402Error ResourceFileWriter::appendFile(StringRef Filename) {
403 bool IsLong;
404 stripQuotes(Filename, IsLong);
405
406 auto File = loadFile(Filename);
407 if (!File)
408 return File.takeError();
409
410 *FS << (*File)->getBuffer();
411 return Error::success();
412}
413
414void ResourceFileWriter::padStream(uint64_t Length) {
415 assert(Length > 0)((Length > 0) ? static_cast<void> (0) : __assert_fail
("Length > 0", "/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp"
, 415, __PRETTY_FUNCTION__))
;
416 uint64_t Location = tell();
417 Location %= Length;
418 uint64_t Pad = (Length - Location) % Length;
419 for (uint64_t i = 0; i < Pad; ++i)
420 writeInt<uint8_t>(0);
421}
422
423Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) {
424 if (Err)
425 return joinErrors(createError("Error in " + Res->getResourceTypeName() +
426 " statement (ID " + Twine(Res->ResName) +
427 "): "),
428 std::move(Err));
429 return Error::success();
430}
431
432Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
433 return writeResource(Res, &ResourceFileWriter::writeNullBody);
434}
435
436Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
437 return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
438}
439
440Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) {
441 return writeResource(Res, &ResourceFileWriter::writeBitmapBody);
442}
443
444Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
445 return handleError(visitIconOrCursorResource(Res), Res);
446}
447
448Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
449 return writeResource(Res, &ResourceFileWriter::writeDialogBody);
450}
451
452Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
453 return handleError(visitIconOrCursorResource(Res), Res);
1
Calling 'ResourceFileWriter::visitIconOrCursorResource'
454}
455
456Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
457 ObjectData.Caption = Stmt->Value;
458 return Error::success();
459}
460
461Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) {
462 ObjectData.Class = Stmt->Value;
463 return Error::success();
464}
465
466Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
467 return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
468}
469
470Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
471 return writeResource(Res, &ResourceFileWriter::writeMenuBody);
472}
473
474Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
475 const auto *Res = cast<StringTableResource>(Base);
476
477 ContextKeeper RAII(this);
478 RETURN_IF_ERROR(Res->applyStmts(this))if (auto Err = (Res->applyStmts(this))) return Err;;
479
480 for (auto &String : Res->Table) {
481 RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"))if (auto Err = (checkNumberFits<uint16_t>(String.first,
"String ID"))) return Err;
;
482 uint16_t BundleID = String.first >> 4;
483 StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
484 auto &BundleData = StringTableData.BundleData;
485 auto Iter = BundleData.find(Key);
486
487 if (Iter == BundleData.end()) {
488 // Need to create a bundle.
489 StringTableData.BundleList.push_back(Key);
490 auto EmplaceResult = BundleData.emplace(
491 Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags));
492 assert(EmplaceResult.second && "Could not create a bundle")((EmplaceResult.second && "Could not create a bundle"
) ? static_cast<void> (0) : __assert_fail ("EmplaceResult.second && \"Could not create a bundle\""
, "/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp"
, 492, __PRETTY_FUNCTION__))
;
493 Iter = EmplaceResult.first;
494 }
495
496 RETURN_IF_ERROR(if (auto Err = (insertStringIntoBundle(Iter->second, String
.first, String.second))) return Err;
497 insertStringIntoBundle(Iter->second, String.first, String.second))if (auto Err = (insertStringIntoBundle(Iter->second, String
.first, String.second))) return Err;
;
498 }
499
500 return Error::success();
501}
502
503Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) {
504 return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody);
505}
506
507Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
508 return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
509}
510
511Error ResourceFileWriter::visitCharacteristicsStmt(
512 const CharacteristicsStmt *Stmt) {
513 ObjectData.Characteristics = Stmt->Value;
514 return Error::success();
515}
516
517Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
518 RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"))if (auto Err = (checkNumberFits<uint16_t>(Stmt->Size
, "Font size"))) return Err;
;
519 RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"))if (auto Err = (checkNumberFits<uint16_t>(Stmt->Weight
, "Font weight"))) return Err;
;
520 RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"))if (auto Err = (checkNumberFits<uint8_t>(Stmt->Charset
, "Font charset"))) return Err;
;
521 ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
522 Stmt->Charset};
523 ObjectData.Font.emplace(Font);
524 return Error::success();
525}
526
527Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
528 RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"))if (auto Err = (checkNumberFits(Stmt->Lang, 10, "Primary language ID"
))) return Err;
;
529 RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"))if (auto Err = (checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"
))) return Err;
;
530 ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
531 return Error::success();
532}
533
534Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
535 ObjectData.Style = Stmt->Value;
536 return Error::success();
537}
538
539Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
540 ObjectData.VersionInfo = Stmt->Value;
541 return Error::success();
542}
543
544Error ResourceFileWriter::writeResource(
545 const RCResource *Res,
546 Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
547 // We don't know the sizes yet.
548 object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
549 uint64_t HeaderLoc = writeObject(HeaderPrefix);
550
551 auto ResType = Res->getResourceType();
552 RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"))if (auto Err = (checkIntOrString(ResType, "Resource type"))) return
Err;
;
553 RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"))if (auto Err = (checkIntOrString(Res->ResName, "Resource ID"
))) return Err;
;
554 RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res))if (auto Err = (handleError(writeIdentifier(ResType), Res))) return
Err;
;
555 RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res))if (auto Err = (handleError(writeIdentifier(Res->ResName),
Res))) return Err;
;
556
557 // Apply the resource-local optional statements.
558 ContextKeeper RAII(this);
559 RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res))if (auto Err = (handleError(Res->applyStmts(this), Res))) return
Err;
;
560
561 padStream(sizeof(uint32_t));
562 object::WinResHeaderSuffix HeaderSuffix{
563 ulittle32_t(0), // DataVersion; seems to always be 0
564 ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo),
565 ulittle32_t(ObjectData.VersionInfo),
566 ulittle32_t(ObjectData.Characteristics)};
567 writeObject(HeaderSuffix);
568
569 uint64_t DataLoc = tell();
570 RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res))if (auto Err = (handleError((this->*BodyWriter)(Res), Res)
)) return Err;
;
571 // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
572
573 // Update the sizes.
574 HeaderPrefix.DataSize = tell() - DataLoc;
575 HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
576 writeObjectAt(HeaderPrefix, HeaderLoc);
577 padStream(sizeof(uint32_t));
578
579 return Error::success();
580}
581
582// --- NullResource helpers. --- //
583
584Error ResourceFileWriter::writeNullBody(const RCResource *) {
585 return Error::success();
586}
587
588// --- AcceleratorsResource helpers. --- //
589
590Error ResourceFileWriter::writeSingleAccelerator(
591 const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
592 using Accelerator = AcceleratorsResource::Accelerator;
593 using Opt = Accelerator::Options;
594
595 struct AccelTableEntry {
596 ulittle16_t Flags;
597 ulittle16_t ANSICode;
598 ulittle16_t Id;
599 uint16_t Padding;
600 } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
601
602 bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
603
604 // Remove ASCII flags (which doesn't occur in .res files).
605 Entry.Flags = Obj.Flags & ~Opt::ASCII;
606
607 if (IsLastItem)
608 Entry.Flags |= 0x80;
609
610 RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"))if (auto Err = (checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"
))) return Err;
;
611 Entry.Id = ulittle16_t(Obj.Id);
612
613 auto createAccError = [&Obj](const char *Msg) {
614 return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
615 };
616
617 if (IsASCII && IsVirtKey)
618 return createAccError("Accelerator can't be both ASCII and VIRTKEY");
619
620 if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
621 return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
622 " accelerators");
623
624 if (Obj.Event.isInt()) {
625 if (!IsASCII && !IsVirtKey)
626 return createAccError(
627 "Accelerator with a numeric event must be either ASCII"
628 " or VIRTKEY");
629
630 uint32_t EventVal = Obj.Event.getInt();
631 RETURN_IF_ERROR(if (auto Err = (checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"
))) return Err;
632 checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"))if (auto Err = (checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"
))) return Err;
;
633 Entry.ANSICode = ulittle16_t(EventVal);
634 writeObject(Entry);
635 return Error::success();
636 }
637
638 StringRef Str = Obj.Event.getString();
639 bool IsWide;
640 stripQuotes(Str, IsWide);
641
642 if (Str.size() == 0 || Str.size() > 2)
643 return createAccError(
644 "Accelerator string events should have length 1 or 2");
645
646 if (Str[0] == '^') {
647 if (Str.size() == 1)
648 return createAccError("No character following '^' in accelerator event");
649 if (IsVirtKey)
650 return createAccError(
651 "VIRTKEY accelerator events can't be preceded by '^'");
652
653 char Ch = Str[1];
654 if (Ch >= 'a' && Ch <= 'z')
655 Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
656 else if (Ch >= 'A' && Ch <= 'Z')
657 Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
658 else
659 return createAccError("Control character accelerator event should be"
660 " alphabetic");
661
662 writeObject(Entry);
663 return Error::success();
664 }
665
666 if (Str.size() == 2)
667 return createAccError("Event string should be one-character, possibly"
668 " preceded by '^'");
669
670 uint8_t EventCh = Str[0];
671 // The original tool just warns in this situation. We chose to fail.
672 if (IsVirtKey && !isalnum(EventCh))
673 return createAccError("Non-alphanumeric characters cannot describe virtual"
674 " keys");
675 if (EventCh > 0x7F)
676 return createAccError("Non-ASCII description of accelerator");
677
678 if (IsVirtKey)
679 EventCh = toupper(EventCh);
680 Entry.ANSICode = ulittle16_t(EventCh);
681 writeObject(Entry);
682 return Error::success();
683}
684
685Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
686 auto *Res = cast<AcceleratorsResource>(Base);
687 size_t AcceleratorId = 0;
688 for (auto &Acc : Res->Accelerators) {
689 ++AcceleratorId;
690 RETURN_IF_ERROR(if (auto Err = (writeSingleAccelerator(Acc, AcceleratorId == Res
->Accelerators.size()))) return Err;
691 writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()))if (auto Err = (writeSingleAccelerator(Acc, AcceleratorId == Res
->Accelerators.size()))) return Err;
;
692 }
693 return Error::success();
694}
695
696// --- BitmapResource helpers. --- //
697
698Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) {
699 StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc;
700 bool IsLong;
701 stripQuotes(Filename, IsLong);
702
703 auto File = loadFile(Filename);
704 if (!File)
705 return File.takeError();
706
707 StringRef Buffer = (*File)->getBuffer();
708
709 // Skip the 14 byte BITMAPFILEHEADER.
710 constexpr size_t BITMAPFILEHEADER_size = 14;
711 if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' ||
712 Buffer[1] != 'M')
713 return createError("Incorrect bitmap file.");
714
715 *FS << Buffer.substr(BITMAPFILEHEADER_size);
716 return Error::success();
717}
718
719// --- CursorResource and IconResource helpers. --- //
720
721// ICONRESDIR structure. Describes a single icon in resouce group.
722//
723// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
724struct IconResDir {
725 uint8_t Width;
726 uint8_t Height;
727 uint8_t ColorCount;
728 uint8_t Reserved;
729};
730
731// CURSORDIR structure. Describes a single cursor in resource group.
732//
733// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
734struct CursorDir {
735 ulittle16_t Width;
736 ulittle16_t Height;
737};
738
739// RESDIRENTRY structure, stripped from the last item. Stripping made
740// for compatibility with RESDIR.
741//
742// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
743struct ResourceDirEntryStart {
744 union {
745 CursorDir Cursor; // Used in CURSOR resources.
746 IconResDir Icon; // Used in .ico and .cur files, and ICON resources.
747 };
748 ulittle16_t Planes; // HotspotX (.cur files but not CURSOR resource).
749 ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
750 ulittle32_t Size;
751 // ulittle32_t ImageOffset; // Offset to image data (ICONDIRENTRY only).
752 // ulittle16_t IconID; // Resource icon ID (RESDIR only).
753};
754
755// BITMAPINFOHEADER structure. Describes basic information about the bitmap
756// being read.
757//
758// Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
759struct BitmapInfoHeader {
760 ulittle32_t Size;
761 ulittle32_t Width;
762 ulittle32_t Height;
763 ulittle16_t Planes;
764 ulittle16_t BitCount;
765 ulittle32_t Compression;
766 ulittle32_t SizeImage;
767 ulittle32_t XPelsPerMeter;
768 ulittle32_t YPelsPerMeter;
769 ulittle32_t ClrUsed;
770 ulittle32_t ClrImportant;
771};
772
773// Group icon directory header. Called ICONDIR in .ico/.cur files and
774// NEWHEADER in .res files.
775//
776// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
777struct GroupIconDir {
778 ulittle16_t Reserved; // Always 0.
779 ulittle16_t ResType; // 1 for icons, 2 for cursors.
780 ulittle16_t ResCount; // Number of items.
781};
782
783enum class IconCursorGroupType { Icon, Cursor };
784
785class SingleIconCursorResource : public RCResource {
786public:
787 IconCursorGroupType Type;
788 const ResourceDirEntryStart &Header;
789 ArrayRef<uint8_t> Image;
790
791 SingleIconCursorResource(IconCursorGroupType ResourceType,
792 const ResourceDirEntryStart &HeaderEntry,
793 ArrayRef<uint8_t> ImageData, uint16_t Flags)
794 : RCResource(Flags), Type(ResourceType), Header(HeaderEntry),
795 Image(ImageData) {}
796
797 Twine getResourceTypeName() const override { return "Icon/cursor image"; }
798 IntOrString getResourceType() const override {
799 return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
800 }
801 ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
802 static bool classof(const RCResource *Res) {
803 return Res->getKind() == RkSingleCursorOrIconRes;
804 }
805};
806
807class IconCursorGroupResource : public RCResource {
808public:
809 IconCursorGroupType Type;
810 GroupIconDir Header;
811 std::vector<ResourceDirEntryStart> ItemEntries;
812
813 IconCursorGroupResource(IconCursorGroupType ResourceType,
814 const GroupIconDir &HeaderData,
815 std::vector<ResourceDirEntryStart> &&Entries)
816 : Type(ResourceType), Header(HeaderData),
817 ItemEntries(std::move(Entries)) {}
818
819 Twine getResourceTypeName() const override { return "Icon/cursor group"; }
820 IntOrString getResourceType() const override {
821 return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
822 }
823 ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
824 static bool classof(const RCResource *Res) {
825 return Res->getKind() == RkCursorOrIconGroupRes;
826 }
827};
828
829Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
830 auto *Res = cast<SingleIconCursorResource>(Base);
831 if (Res->Type == IconCursorGroupType::Cursor) {
832 // In case of cursors, two WORDS are appended to the beginning
833 // of the resource: HotspotX (Planes in RESDIRENTRY),
834 // and HotspotY (BitCount).
835 //
836 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
837 // (Remarks section).
838 writeObject(Res->Header.Planes);
839 writeObject(Res->Header.BitCount);
840 }
841
842 writeObject(Res->Image);
843 return Error::success();
844}
845
846Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
847 auto *Res = cast<IconCursorGroupResource>(Base);
848 writeObject(Res->Header);
849 for (auto Item : Res->ItemEntries) {
850 writeObject(Item);
851 writeInt(IconCursorID++);
852 }
853 return Error::success();
854}
855
856Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
857 return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
858}
859
860Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
861 return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
862}
863
864Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
865 IconCursorGroupType Type;
866 StringRef FileStr;
867 IntOrString ResName = Base->ResName;
868
869 if (auto *IconRes = dyn_cast<IconResource>(Base)) {
2
Taking true branch
870 FileStr = IconRes->IconLoc;
871 Type = IconCursorGroupType::Icon;
872 } else {
873 auto *CursorRes = dyn_cast<CursorResource>(Base);
874 FileStr = CursorRes->CursorLoc;
875 Type = IconCursorGroupType::Cursor;
876 }
877
878 bool IsLong;
879 stripQuotes(FileStr, IsLong);
880 auto File = loadFile(FileStr);
881
882 if (!File)
3
Taking false branch
883 return File.takeError();
884
885 BinaryStreamReader Reader((*File)->getBuffer(), support::little);
886
887 // Read the file headers.
888 // - At the beginning, ICONDIR/NEWHEADER header.
889 // - Then, a number of RESDIR headers follow. These contain offsets
890 // to data.
891 const GroupIconDir *Header;
4
'Header' declared without an initial value
892
893 RETURN_IF_ERROR(Reader.readObject(Header))if (auto Err = (Reader.readObject(Header))) return Err;;
5
Within the expansion of the macro 'RETURN_IF_ERROR':
a
Calling 'BinaryStreamReader::readObject'
b
Returning from 'BinaryStreamReader::readObject'
894 if (Header->Reserved != 0)
8
Called C++ object pointer is uninitialized
895 return createError("Incorrect icon/cursor Reserved field; should be 0.");
896 uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
897 if (Header->ResType != NeededType)
898 return createError("Incorrect icon/cursor ResType field; should be " +
899 Twine(NeededType) + ".");
900
901 uint16_t NumItems = Header->ResCount;
902
903 // Read single ico/cur headers.
904 std::vector<ResourceDirEntryStart> ItemEntries;
905 ItemEntries.reserve(NumItems);
906 std::vector<uint32_t> ItemOffsets(NumItems);
907 for (size_t ID = 0; ID < NumItems; ++ID) {
908 const ResourceDirEntryStart *Object;
909 RETURN_IF_ERROR(Reader.readObject(Object))if (auto Err = (Reader.readObject(Object))) return Err;;
910 ItemEntries.push_back(*Object);
911 RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]))if (auto Err = (Reader.readInteger(ItemOffsets[ID]))) return Err
;
;
912 }
913
914 // Now write each icon/cursors one by one. At first, all the contents
915 // without ICO/CUR header. This is described by SingleIconCursorResource.
916 for (size_t ID = 0; ID < NumItems; ++ID) {
917 // Load the fragment of file.
918 Reader.setOffset(ItemOffsets[ID]);
919 ArrayRef<uint8_t> Image;
920 RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size))if (auto Err = (Reader.readArray(Image, ItemEntries[ID].Size)
)) return Err;
;
921 SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image,
922 Base->MemoryFlags);
923 SingleRes.setName(IconCursorID + ID);
924 RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes))if (auto Err = (visitSingleIconOrCursor(&SingleRes))) return
Err;
;
925 }
926
927 // Now, write all the headers concatenated into a separate resource.
928 for (size_t ID = 0; ID < NumItems; ++ID) {
929 // We need to rewrite the cursor headers, and fetch actual values
930 // for Planes/BitCount.
931 const auto &OldHeader = ItemEntries[ID];
932 ResourceDirEntryStart NewHeader = OldHeader;
933
934 if (Type == IconCursorGroupType::Cursor) {
935 NewHeader.Cursor.Width = OldHeader.Icon.Width;
936 // Each cursor in fact stores two bitmaps, one under another.
937 // Height provided in cursor definition describes the height of the
938 // cursor, whereas the value existing in resource definition describes
939 // the height of the bitmap. Therefore, we need to double this height.
940 NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
941
942 // Two WORDs were written at the beginning of the resource (hotspot
943 // location). This is reflected in Size field.
944 NewHeader.Size += 2 * sizeof(uint16_t);
945 }
946
947 // Now, we actually need to read the bitmap header to find
948 // the number of planes and the number of bits per pixel.
949 Reader.setOffset(ItemOffsets[ID]);
950 const BitmapInfoHeader *BMPHeader;
951 RETURN_IF_ERROR(Reader.readObject(BMPHeader))if (auto Err = (Reader.readObject(BMPHeader))) return Err;;
952 if (BMPHeader->Size == sizeof(BitmapInfoHeader)) {
953 NewHeader.Planes = BMPHeader->Planes;
954 NewHeader.BitCount = BMPHeader->BitCount;
955 } else {
956 // A PNG .ico file.
957 // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473
958 // "The image must be in 32bpp"
959 NewHeader.Planes = 1;
960 NewHeader.BitCount = 32;
961 }
962
963 ItemEntries[ID] = NewHeader;
964 }
965
966 IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
967 HeaderRes.setName(ResName);
968 if (Base->MemoryFlags & MfPreload) {
969 HeaderRes.MemoryFlags |= MfPreload;
970 HeaderRes.MemoryFlags &= ~MfPure;
971 }
972 RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes))if (auto Err = (visitIconOrCursorGroup(&HeaderRes))) return
Err;
;
973
974 return Error::success();
975}
976
977// --- DialogResource helpers. --- //
978
979Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
980 bool IsExtended) {
981 // Each control should be aligned to DWORD.
982 padStream(sizeof(uint32_t));
983
984 auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
985 uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0);
986 uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
987
988 // DIALOG(EX) item header prefix.
989 if (!IsExtended) {
990 struct {
991 ulittle32_t Style;
992 ulittle32_t ExtStyle;
993 } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)};
994 writeObject(Prefix);
995 } else {
996 struct {
997 ulittle32_t HelpID;
998 ulittle32_t ExtStyle;
999 ulittle32_t Style;
1000 } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
1001 ulittle32_t(CtlStyle)};
1002 writeObject(Prefix);
1003 }
1004
1005 // Common fixed-length part.
1006 RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.X, "Dialog control x-coordinate"
, true))) return Err;
1007 Ctl.X, "Dialog control x-coordinate", true))if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.X, "Dialog control x-coordinate"
, true))) return Err;
;
1008 RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.Y, "Dialog control y-coordinate"
, true))) return Err;
1009 Ctl.Y, "Dialog control y-coordinate", true))if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.Y, "Dialog control y-coordinate"
, true))) return Err;
;
1010 RETURN_IF_ERROR(if (auto Err = (checkSignedNumberFits<int16_t>(Ctl.Width
, "Dialog control width", false))) return Err;
1011 checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false))if (auto Err = (checkSignedNumberFits<int16_t>(Ctl.Width
, "Dialog control width", false))) return Err;
;
1012 RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.Height
, "Dialog control height", false))) return Err;
1013 Ctl.Height, "Dialog control height", false))if (auto Err = (checkSignedNumberFits<int16_t>( Ctl.Height
, "Dialog control height", false))) return Err;
;
1014 struct {
1015 ulittle16_t X;
1016 ulittle16_t Y;
1017 ulittle16_t Width;
1018 ulittle16_t Height;
1019 } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
1020 ulittle16_t(Ctl.Height)};
1021 writeObject(Middle);
1022
1023 // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
1024 if (!IsExtended) {
1025 // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
1026 // want to refer to later.
1027 if (Ctl.ID != static_cast<uint32_t>(-1))
1028 RETURN_IF_ERROR(checkNumberFits<uint16_t>(if (auto Err = (checkNumberFits<uint16_t>( Ctl.ID, "Control ID in simple DIALOG resource"
))) return Err;
1029 Ctl.ID, "Control ID in simple DIALOG resource"))if (auto Err = (checkNumberFits<uint16_t>( Ctl.ID, "Control ID in simple DIALOG resource"
))) return Err;
;
1030 writeInt<uint16_t>(Ctl.ID);
1031 } else {
1032 writeInt<uint32_t>(Ctl.ID);
1033 }
1034
1035 // Window class - either 0xFFFF + 16-bit integer or a string.
1036 RETURN_IF_ERROR(writeIntOrString(Ctl.Class))if (auto Err = (writeIntOrString(Ctl.Class))) return Err;;
1037
1038 // Element caption/reference ID. ID is preceded by 0xFFFF.
1039 RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"))if (auto Err = (checkIntOrString(Ctl.Title, "Control reference ID"
))) return Err;
;
1040 RETURN_IF_ERROR(writeIntOrString(Ctl.Title))if (auto Err = (writeIntOrString(Ctl.Title))) return Err;;
1041
1042 // # bytes of extra creation data count. Don't pass any.
1043 writeInt<uint16_t>(0);
1044
1045 return Error::success();
1046}
1047
1048Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
1049 auto *Res = cast<DialogResource>(Base);
1050
1051 // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
1052 const uint32_t DefaultStyle = 0x80880000;
1053 const uint32_t StyleFontFlag = 0x40;
1054 const uint32_t StyleCaptionFlag = 0x00C00000;
1055
1056 uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
1057 if (ObjectData.Font)
1058 UsedStyle |= StyleFontFlag;
1059 else
1060 UsedStyle &= ~StyleFontFlag;
1061
1062 // Actually, in case of empty (but existent) caption, the examined field
1063 // is equal to "\"\"". That's why empty captions are still noticed.
1064 if (ObjectData.Caption != "")
1065 UsedStyle |= StyleCaptionFlag;
1066
1067 const uint16_t DialogExMagic = 0xFFFF;
1068
1069 // Write DIALOG(EX) header prefix. These are pretty different.
1070 if (!Res->IsExtended) {
1071 // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
1072 // In such a case, whole object (in .res file) is equivalent to a
1073 // DIALOGEX. It might lead to access violation/segmentation fault in
1074 // resource readers. For example,
1075 // 1 DIALOG 0, 0, 0, 65432
1076 // STYLE 0xFFFF0001 {}
1077 // would be compiled to a DIALOGEX with 65432 controls.
1078 if ((UsedStyle >> 16) == DialogExMagic)
1079 return createError("16 higher bits of DIALOG resource style cannot be"
1080 " equal to 0xFFFF");
1081
1082 struct {
1083 ulittle32_t Style;
1084 ulittle32_t ExtStyle;
1085 } Prefix{ulittle32_t(UsedStyle),
1086 ulittle32_t(0)}; // As of now, we don't keep EXSTYLE.
1087
1088 writeObject(Prefix);
1089 } else {
1090 struct {
1091 ulittle16_t Version;
1092 ulittle16_t Magic;
1093 ulittle32_t HelpID;
1094 ulittle32_t ExtStyle;
1095 ulittle32_t Style;
1096 } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
1097 ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)};
1098
1099 writeObject(Prefix);
1100 }
1101
1102 // Now, a common part. First, fixed-length fields.
1103 RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),if (auto Err = (checkNumberFits<uint16_t>(Res->Controls
.size(), "Number of dialog controls"))) return Err;
1104 "Number of dialog controls"))if (auto Err = (checkNumberFits<uint16_t>(Res->Controls
.size(), "Number of dialog controls"))) return Err;
;
1105 RETURN_IF_ERROR(if (auto Err = (checkSignedNumberFits<int16_t>(Res->
X, "Dialog x-coordinate", true))) return Err;
1106 checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true))if (auto Err = (checkSignedNumberFits<int16_t>(Res->
X, "Dialog x-coordinate", true))) return Err;
;
1107 RETURN_IF_ERROR(if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Y, "Dialog y-coordinate", true))) return Err;
1108 checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true))if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Y, "Dialog y-coordinate", true))) return Err;
;
1109 RETURN_IF_ERROR(if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Width, "Dialog width", false))) return Err;
1110 checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false))if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Width, "Dialog width", false))) return Err;
;
1111 RETURN_IF_ERROR(if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Height, "Dialog height", false))) return Err;
1112 checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false))if (auto Err = (checkSignedNumberFits<int16_t>(Res->
Height, "Dialog height", false))) return Err;
;
1113 struct {
1114 ulittle16_t Count;
1115 ulittle16_t PosX;
1116 ulittle16_t PosY;
1117 ulittle16_t DialogWidth;
1118 ulittle16_t DialogHeight;
1119 } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
1120 ulittle16_t(Res->Y), ulittle16_t(Res->Width),
1121 ulittle16_t(Res->Height)};
1122 writeObject(Middle);
1123
1124 // MENU field. As of now, we don't keep them in the state and can peacefully
1125 // think there is no menu attached to the dialog.
1126 writeInt<uint16_t>(0);
1127
1128 // Window CLASS field.
1129 RETURN_IF_ERROR(writeIntOrString(ObjectData.Class))if (auto Err = (writeIntOrString(ObjectData.Class))) return Err
;
;
1130
1131 // Window title or a single word equal to 0.
1132 RETURN_IF_ERROR(writeCString(ObjectData.Caption))if (auto Err = (writeCString(ObjectData.Caption))) return Err
;
;
1133
1134 // If there *is* a window font declared, output its data.
1135 auto &Font = ObjectData.Font;
1136 if (Font) {
1137 writeInt<uint16_t>(Font->Size);
1138 // Additional description occurs only in DIALOGEX.
1139 if (Res->IsExtended) {
1140 writeInt<uint16_t>(Font->Weight);
1141 writeInt<uint8_t>(Font->IsItalic);
1142 writeInt<uint8_t>(Font->Charset);
1143 }
1144 RETURN_IF_ERROR(writeCString(Font->Typeface))if (auto Err = (writeCString(Font->Typeface))) return Err;;
1145 }
1146
1147 auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
1148 if (!Err)
1149 return Error::success();
1150 return joinErrors(createError("Error in " + Twine(Ctl.Type) +
1151 " control (ID " + Twine(Ctl.ID) + "):"),
1152 std::move(Err));
1153 };
1154
1155 for (auto &Ctl : Res->Controls)
1156 RETURN_IF_ERROR(if (auto Err = (handleCtlError(writeSingleDialogControl(Ctl, Res
->IsExtended), Ctl))) return Err;
1157 handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl))if (auto Err = (handleCtlError(writeSingleDialogControl(Ctl, Res
->IsExtended), Ctl))) return Err;
;
1158
1159 return Error::success();
1160}
1161
1162// --- HTMLResource helpers. --- //
1163
1164Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
1165 return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
1166}
1167
1168// --- MenuResource helpers. --- //
1169
1170Error ResourceFileWriter::writeMenuDefinition(
1171 const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
1172 assert(Def)((Def) ? static_cast<void> (0) : __assert_fail ("Def", "/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp"
, 1172, __PRETTY_FUNCTION__))
;
1173 const MenuDefinition *DefPtr = Def.get();
1174
1175 if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
1176 writeInt<uint16_t>(Flags);
1177 RETURN_IF_ERROR(if (auto Err = (checkNumberFits<uint16_t>(MenuItemPtr->
Id, "MENUITEM action ID"))) return Err;
1178 checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"))if (auto Err = (checkNumberFits<uint16_t>(MenuItemPtr->
Id, "MENUITEM action ID"))) return Err;
;
1179 writeInt<uint16_t>(MenuItemPtr->Id);
1180 RETURN_IF_ERROR(writeCString(MenuItemPtr->Name))if (auto Err = (writeCString(MenuItemPtr->Name))) return Err
;
;
1181 return Error::success();
1182 }
1183
1184 if (isa<MenuSeparator>(DefPtr)) {
1185 writeInt<uint16_t>(Flags);
1186 writeInt<uint32_t>(0);
1187 return Error::success();
1188 }
1189
1190 auto *PopupPtr = cast<PopupItem>(DefPtr);
1191 writeInt<uint16_t>(Flags);
1192 RETURN_IF_ERROR(writeCString(PopupPtr->Name))if (auto Err = (writeCString(PopupPtr->Name))) return Err;;
1193 return writeMenuDefinitionList(PopupPtr->SubItems);
1194}
1195
1196Error ResourceFileWriter::writeMenuDefinitionList(
1197 const MenuDefinitionList &List) {
1198 for (auto &Def : List.Definitions) {
1199 uint16_t Flags = Def->getResFlags();
1200 // Last element receives an additional 0x80 flag.
1201 const uint16_t LastElementFlag = 0x0080;
1202 if (&Def == &List.Definitions.back())
1203 Flags |= LastElementFlag;
1204
1205 RETURN_IF_ERROR(writeMenuDefinition(Def, Flags))if (auto Err = (writeMenuDefinition(Def, Flags))) return Err;;
1206 }
1207 return Error::success();
1208}
1209
1210Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
1211 // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
1212 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
1213 writeInt<uint32_t>(0);
1214
1215 return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
1216}
1217
1218// --- StringTableResource helpers. --- //
1219
1220class BundleResource : public RCResource {
1221public:
1222 using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
1223 BundleType Bundle;
1224
1225 BundleResource(const BundleType &StrBundle)
1226 : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {}
1227 IntOrString getResourceType() const override { return 6; }
1228
1229 ResourceKind getKind() const override { return RkStringTableBundle; }
1230 static bool classof(const RCResource *Res) {
1231 return Res->getKind() == RkStringTableBundle;
1232 }
1233 Twine getResourceTypeName() const override { return "STRINGTABLE"; }
1234};
1235
1236Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
1237 return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
1238}
1239
1240Error ResourceFileWriter::insertStringIntoBundle(
1241 StringTableInfo::Bundle &Bundle, uint16_t StringID, StringRef String) {
1242 uint16_t StringLoc = StringID & 15;
1243 if (Bundle.Data[StringLoc])
1244 return createError("Multiple STRINGTABLE strings located under ID " +
1245 Twine(StringID));
1246 Bundle.Data[StringLoc] = String;
1247 return Error::success();
1248}
1249
1250Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
1251 auto *Res = cast<BundleResource>(Base);
1252 for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
1253 // The string format is a tiny bit different here. We
1254 // first output the size of the string, and then the string itself
1255 // (which is not null-terminated).
1256 bool IsLongString;
1257 SmallVector<UTF16, 128> Data;
1258 RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()),if (auto Err = (processString(Res->Bundle.Data[ID].getValueOr
(StringRef()), NullHandlingMethod::CutAtDoubleNull, IsLongString
, Data, Params.CodePage))) return Err;
1259 NullHandlingMethod::CutAtDoubleNull,if (auto Err = (processString(Res->Bundle.Data[ID].getValueOr
(StringRef()), NullHandlingMethod::CutAtDoubleNull, IsLongString
, Data, Params.CodePage))) return Err;
1260 IsLongString, Data, Params.CodePage))if (auto Err = (processString(Res->Bundle.Data[ID].getValueOr
(StringRef()), NullHandlingMethod::CutAtDoubleNull, IsLongString
, Data, Params.CodePage))) return Err;
;
1261 if (AppendNull && Res->Bundle.Data[ID])
1262 Data.push_back('\0');
1263 RETURN_IF_ERROR(if (auto Err = (checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"
))) return Err;
1264 checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"))if (auto Err = (checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"
))) return Err;
;
1265 writeInt<uint16_t>(Data.size());
1266 for (auto Char : Data)
1267 writeInt(Char);
1268 }
1269 return Error::success();
1270}
1271
1272Error ResourceFileWriter::dumpAllStringTables() {
1273 for (auto Key : StringTableData.BundleList) {
1274 auto Iter = StringTableData.BundleData.find(Key);
1275 assert(Iter != StringTableData.BundleData.end())((Iter != StringTableData.BundleData.end()) ? static_cast<
void> (0) : __assert_fail ("Iter != StringTableData.BundleData.end()"
, "/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp"
, 1275, __PRETTY_FUNCTION__))
;
1276
1277 // For a moment, revert the context info to moment of bundle declaration.
1278 ContextKeeper RAII(this);
1279 ObjectData = Iter->second.DeclTimeInfo;
1280
1281 BundleResource Res(Iter->second);
1282 // Bundle #(k+1) contains keys [16k, 16k + 15].
1283 Res.setName(Key.first + 1);
1284 RETURN_IF_ERROR(visitStringTableBundle(&Res))if (auto Err = (visitStringTableBundle(&Res))) return Err
;
;
1285 }
1286 return Error::success();
1287}
1288
1289// --- UserDefinedResource helpers. --- //
1290
1291Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
1292 auto *Res = cast<UserDefinedResource>(Base);
1293
1294 if (Res->IsFileResource)
1295 return appendFile(Res->FileLoc);
1296
1297 for (auto &Elem : Res->Contents) {
1298 if (Elem.isInt()) {
1299 RETURN_IF_ERROR(if (auto Err = (checkRCInt(Elem.getInt(), "Number in user-defined resource"
))) return Err;
1300 checkRCInt(Elem.getInt(), "Number in user-defined resource"))if (auto Err = (checkRCInt(Elem.getInt(), "Number in user-defined resource"
))) return Err;
;
1301 writeRCInt(Elem.getInt());
1302 continue;
1303 }
1304
1305 SmallVector<UTF16, 128> ProcessedString;
1306 bool IsLongString;
1307 RETURN_IF_ERROR(if (auto Err = (processString(Elem.getString(), NullHandlingMethod
::UserResource, IsLongString, ProcessedString, Params.CodePage
))) return Err;
1308 processString(Elem.getString(), NullHandlingMethod::UserResource,if (auto Err = (processString(Elem.getString(), NullHandlingMethod
::UserResource, IsLongString, ProcessedString, Params.CodePage
))) return Err;
1309 IsLongString, ProcessedString, Params.CodePage))if (auto Err = (processString(Elem.getString(), NullHandlingMethod
::UserResource, IsLongString, ProcessedString, Params.CodePage
))) return Err;
;
1310
1311 for (auto Ch : ProcessedString) {
1312 if (IsLongString) {
1313 writeInt(Ch);
1314 continue;
1315 }
1316
1317 RETURN_IF_ERROR(checkNumberFits<uint8_t>(if (auto Err = (checkNumberFits<uint8_t>( Ch, "Character in narrow string in user-defined resource"
))) return Err;
1318 Ch, "Character in narrow string in user-defined resource"))if (auto Err = (checkNumberFits<uint8_t>( Ch, "Character in narrow string in user-defined resource"
))) return Err;
;
1319 writeInt<uint8_t>(Ch);
1320 }
1321 }
1322
1323 return Error::success();
1324}
1325
1326// --- VersionInfoResourceResource helpers. --- //
1327
1328Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
1329 // Output the header if the block has name.
1330 bool OutputHeader = Blk.Name != "";
1331 uint64_t LengthLoc;
1332
1333 padStream(sizeof(uint32_t));
1334 if (OutputHeader) {
1335 LengthLoc = writeInt<uint16_t>(0);
1336 writeInt<uint16_t>(0);
1337 writeInt<uint16_t>(1); // true
1338 RETURN_IF_ERROR(writeCString(Blk.Name))if (auto Err = (writeCString(Blk.Name))) return Err;;
1339 padStream(sizeof(uint32_t));
1340 }
1341
1342 for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
1343 VersionInfoStmt *ItemPtr = Item.get();
1344
1345 if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
1346 RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr))if (auto Err = (writeVersionInfoBlock(*BlockPtr))) return Err
;
;
1347 continue;
1348 }
1349
1350 auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
1351 RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr))if (auto Err = (writeVersionInfoValue(*ValuePtr))) return Err
;
;
1352 }
1353
1354 if (OutputHeader) {
1355 uint64_t CurLoc = tell();
1356 writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1357 }
1358
1359 return Error::success();
1360}
1361
1362Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
1363 // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
1364 // is a mapping from the key (string) to the value (a sequence of ints or
1365 // a sequence of strings).
1366 //
1367 // If integers are to be written: width of each integer written depends on
1368 // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
1369 // ValueLength defined in structure referenced below is then the total
1370 // number of bytes taken by these integers.
1371 //
1372 // If strings are to be written: characters are always WORDs.
1373 // Moreover, '\0' character is written after the last string, and between
1374 // every two strings separated by comma (if strings are not comma-separated,
1375 // they're simply concatenated). ValueLength is equal to the number of WORDs
1376 // written (that is, half of the bytes written).
1377 //
1378 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
1379 bool HasStrings = false, HasInts = false;
1380 for (auto &Item : Val.Values)
1381 (Item.isInt() ? HasInts : HasStrings) = true;
1382
1383 assert((HasStrings || HasInts) && "VALUE must have at least one argument")(((HasStrings || HasInts) && "VALUE must have at least one argument"
) ? static_cast<void> (0) : __assert_fail ("(HasStrings || HasInts) && \"VALUE must have at least one argument\""
, "/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp"
, 1383, __PRETTY_FUNCTION__))
;
1384 if (HasStrings && HasInts)
1385 return createError(Twine("VALUE ") + Val.Key +
1386 " cannot contain both strings and integers");
1387
1388 padStream(sizeof(uint32_t));
1389 auto LengthLoc = writeInt<uint16_t>(0);
1390 auto ValLengthLoc = writeInt<uint16_t>(0);
1391 writeInt<uint16_t>(HasStrings);
1392 RETURN_IF_ERROR(writeCString(Val.Key))if (auto Err = (writeCString(Val.Key))) return Err;;
1393 padStream(sizeof(uint32_t));
1394
1395 auto DataLoc = tell();
1396 for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
1397 auto &Item = Val.Values[Id];
1398 if (Item.isInt()) {
1399 auto Value = Item.getInt();
1400 RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"))if (auto Err = (checkRCInt(Value, "VERSIONINFO integer value"
))) return Err;
;
1401 writeRCInt(Value);
1402 continue;
1403 }
1404
1405 bool WriteTerminator =
1406 Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
1407 RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator))if (auto Err = (writeCString(Item.getString(), WriteTerminator
))) return Err;
;
1408 }
1409
1410 auto CurLoc = tell();
1411 auto ValueLength = CurLoc - DataLoc;
1412 if (HasStrings) {
1413 assert(ValueLength % 2 == 0)((ValueLength % 2 == 0) ? static_cast<void> (0) : __assert_fail
("ValueLength % 2 == 0", "/build/llvm-toolchain-snapshot-8~svn345461/tools/llvm-rc/ResourceFileWriter.cpp"
, 1413, __PRETTY_FUNCTION__))
;
1414 ValueLength /= 2;
1415 }
1416 writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1417 writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
1418 return Error::success();
1419}
1420
1421template <typename Ty>
1422static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
1423 const Ty &Default) {
1424 auto Iter = Map.find(Key);
1425 if (Iter != Map.end())
1426 return Iter->getValue();
1427 return Default;
1428}
1429
1430Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
1431 auto *Res = cast<VersionInfoResource>(Base);
1432
1433 const auto &FixedData = Res->FixedData;
1434
1435 struct /* VS_FIXEDFILEINFO */ {
1436 ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
1437 ulittle32_t StructVersion = ulittle32_t(0x10000);
1438 // It's weird to have most-significant DWORD first on the little-endian
1439 // machines, but let it be this way.
1440 ulittle32_t FileVersionMS;
1441 ulittle32_t FileVersionLS;
1442 ulittle32_t ProductVersionMS;
1443 ulittle32_t ProductVersionLS;
1444 ulittle32_t FileFlagsMask;
1445 ulittle32_t FileFlags;
1446 ulittle32_t FileOS;
1447 ulittle32_t FileType;
1448 ulittle32_t FileSubtype;
1449 // MS implementation seems to always set these fields to 0.
1450 ulittle32_t FileDateMS = ulittle32_t(0);
1451 ulittle32_t FileDateLS = ulittle32_t(0);
1452 } FixedInfo;
1453
1454 // First, VS_VERSIONINFO.
1455 auto LengthLoc = writeInt<uint16_t>(0);
1456 writeInt<uint16_t>(sizeof(FixedInfo));
1457 writeInt<uint16_t>(0);
1458 cantFail(writeCString("VS_VERSION_INFO"));
1459 padStream(sizeof(uint32_t));
1460
1461 using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
1462 auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
1463 static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
1464 if (!FixedData.IsTypePresent[(int)Type])
1465 return DefaultOut;
1466 return FixedData.FixedInfo[(int)Type];
1467 };
1468
1469 auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
1470 RETURN_IF_ERROR(checkNumberFits<uint16_t>(if (auto Err = (checkNumberFits<uint16_t>( *std::max_element
(FileVer.begin(), FileVer.end()), "FILEVERSION fields"))) return
Err;
1471 *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"))if (auto Err = (checkNumberFits<uint16_t>( *std::max_element
(FileVer.begin(), FileVer.end()), "FILEVERSION fields"))) return
Err;
;
1472 FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
1473 FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
1474
1475 auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
1476 RETURN_IF_ERROR(checkNumberFits<uint16_t>(if (auto Err = (checkNumberFits<uint16_t>( *std::max_element
(ProdVer.begin(), ProdVer.end()), "PRODUCTVERSION fields"))) return
Err;
1477 *std::max_element(ProdVer.begin(), ProdVer.end()),if (auto Err = (checkNumberFits<uint16_t>( *std::max_element
(ProdVer.begin(), ProdVer.end()), "PRODUCTVERSION fields"))) return
Err;
1478 "PRODUCTVERSION fields"))if (auto Err = (checkNumberFits<uint16_t>( *std::max_element
(ProdVer.begin(), ProdVer.end()), "PRODUCTVERSION fields"))) return
Err;
;
1479 FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
1480 FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
1481
1482 FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
1483 FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
1484 FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
1485 FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
1486 FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
1487
1488 writeObject(FixedInfo);
1489 padStream(sizeof(uint32_t));
1490
1491 RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock))if (auto Err = (writeVersionInfoBlock(Res->MainBlock))) return
Err;
;
1492
1493 // FIXME: check overflow?
1494 writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
1495
1496 return Error::success();
1497}
1498
1499Expected<std::unique_ptr<MemoryBuffer>>
1500ResourceFileWriter::loadFile(StringRef File) const {
1501 SmallString<128> Path;
1502 SmallString<128> Cwd;
1503 std::unique_ptr<MemoryBuffer> Result;
1504
1505 // 1. The current working directory.
1506 sys::fs::current_path(Cwd);
1507 Path.assign(Cwd.begin(), Cwd.end());
1508 sys::path::append(Path, File);
1509 if (sys::fs::exists(Path))
1510 return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1511
1512 // 2. The directory of the input resource file, if it is different from the
1513 // current
1514 // working directory.
1515 StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
1516 Path.assign(InputFileDir.begin(), InputFileDir.end());
1517 sys::path::append(Path, File);
1518 if (sys::fs::exists(Path))
1519 return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1520
1521 // 3. All of the include directories specified on the command line.
1522 for (StringRef ForceInclude : Params.Include) {
1523 Path.assign(ForceInclude.begin(), ForceInclude.end());
1524 sys::path::append(Path, File);
1525 if (sys::fs::exists(Path))
1526 return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1527 }
1528
1529 if (auto Result =
1530 llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude))
1531 return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false));
1532
1533 return make_error<StringError>("error : file not found : " + Twine(File),
1534 inconvertibleErrorCode());
1535}
1536
1537} // namespace rc
1538} // namespace llvm

/build/llvm-toolchain-snapshot-8~svn345461/include/llvm/Support/BinaryStreamReader.h

1//===- BinaryStreamReader.h - Reads objects from a binary stream *- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef LLVM_SUPPORT_BINARYSTREAMREADER_H
11#define LLVM_SUPPORT_BINARYSTREAMREADER_H
12
13#include "llvm/ADT/ArrayRef.h"
14#include "llvm/ADT/STLExtras.h"
15#include "llvm/Support/BinaryStreamArray.h"
16#include "llvm/Support/BinaryStreamRef.h"
17#include "llvm/Support/ConvertUTF.h"
18#include "llvm/Support/Endian.h"
19#include "llvm/Support/Error.h"
20#include "llvm/Support/type_traits.h"
21
22#include <string>
23#include <type_traits>
24
25namespace llvm {
26
27/// Provides read only access to a subclass of `BinaryStream`. Provides
28/// bounds checking and helpers for writing certain common data types such as
29/// null-terminated strings, integers in various flavors of endianness, etc.
30/// Can be subclassed to provide reading of custom datatypes, although no
31/// are overridable.
32class BinaryStreamReader {
33public:
34 BinaryStreamReader() = default;
35 explicit BinaryStreamReader(BinaryStreamRef Ref);
36 explicit BinaryStreamReader(BinaryStream &Stream);
37 explicit BinaryStreamReader(ArrayRef<uint8_t> Data,
38 llvm::support::endianness Endian);
39 explicit BinaryStreamReader(StringRef Data, llvm::support::endianness Endian);
40
41 BinaryStreamReader(const BinaryStreamReader &Other)
42 : Stream(Other.Stream), Offset(Other.Offset) {}
43
44 BinaryStreamReader &operator=(const BinaryStreamReader &Other) {
45 Stream = Other.Stream;
46 Offset = Other.Offset;
47 return *this;
48 }
49
50 virtual ~BinaryStreamReader() {}
51
52 /// Read as much as possible from the underlying string at the current offset
53 /// without invoking a copy, and set \p Buffer to the resulting data slice.
54 /// Updates the stream's offset to point after the newly read data.
55 ///
56 /// \returns a success error code if the data was successfully read, otherwise
57 /// returns an appropriate error code.
58 Error readLongestContiguousChunk(ArrayRef<uint8_t> &Buffer);
59
60 /// Read \p Size bytes from the underlying stream at the current offset and
61 /// and set \p Buffer to the resulting data slice. Whether a copy occurs
62 /// depends on the implementation of the underlying stream. Updates the
63 /// stream's offset to point after the newly read data.
64 ///
65 /// \returns a success error code if the data was successfully read, otherwise
66 /// returns an appropriate error code.
67 Error readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size);
68
69 /// Read an integer of the specified endianness into \p Dest and update the
70 /// stream's offset. The data is always copied from the stream's underlying
71 /// buffer into \p Dest. Updates the stream's offset to point after the newly
72 /// read data.
73 ///
74 /// \returns a success error code if the data was successfully read, otherwise
75 /// returns an appropriate error code.
76 template <typename T> Error readInteger(T &Dest) {
77 static_assert(std::is_integral<T>::value,
78 "Cannot call readInteger with non-integral value!");
79
80 ArrayRef<uint8_t> Bytes;
81 if (auto EC = readBytes(Bytes, sizeof(T)))
82 return EC;
83
84 Dest = llvm::support::endian::read<T, llvm::support::unaligned>(
85 Bytes.data(), Stream.getEndian());
86 return Error::success();
87 }
88
89 /// Similar to readInteger.
90 template <typename T> Error readEnum(T &Dest) {
91 static_assert(std::is_enum<T>::value,
92 "Cannot call readEnum with non-enum value!");
93 typename std::underlying_type<T>::type N;
94 if (auto EC = readInteger(N))
95 return EC;
96 Dest = static_cast<T>(N);
97 return Error::success();
98 }
99
100 /// Read a null terminated string from \p Dest. Whether a copy occurs depends
101 /// on the implementation of the underlying stream. Updates the stream's
102 /// offset to point after the newly read data.
103 ///
104 /// \returns a success error code if the data was successfully read, otherwise
105 /// returns an appropriate error code.
106 Error readCString(StringRef &Dest);
107
108 /// Similar to readCString, however read a null-terminated UTF16 string
109 /// instead.
110 ///
111 /// \returns a success error code if the data was successfully read, otherwise
112 /// returns an appropriate error code.
113 Error readWideString(ArrayRef<UTF16> &Dest);
114
115 /// Read a \p Length byte string into \p Dest. Whether a copy occurs depends
116 /// on the implementation of the underlying stream. Updates the stream's
117 /// offset to point after the newly read data.
118 ///
119 /// \returns a success error code if the data was successfully read, otherwise
120 /// returns an appropriate error code.
121 Error readFixedString(StringRef &Dest, uint32_t Length);
122
123 /// Read the entire remainder of the underlying stream into \p Ref. This is
124 /// equivalent to calling getUnderlyingStream().slice(Offset). Updates the
125 /// stream's offset to point to the end of the stream. Never causes a copy.
126 ///
127 /// \returns a success error code if the data was successfully read, otherwise
128 /// returns an appropriate error code.
129 Error readStreamRef(BinaryStreamRef &Ref);
130
131 /// Read \p Length bytes from the underlying stream into \p Ref. This is
132 /// equivalent to calling getUnderlyingStream().slice(Offset, Length).
133 /// Updates the stream's offset to point after the newly read object. Never
134 /// causes a copy.
135 ///
136 /// \returns a success error code if the data was successfully read, otherwise
137 /// returns an appropriate error code.
138 Error readStreamRef(BinaryStreamRef &Ref, uint32_t Length);
139
140 /// Read \p Length bytes from the underlying stream into \p Stream. This is
141 /// equivalent to calling getUnderlyingStream().slice(Offset, Length).
142 /// Updates the stream's offset to point after the newly read object. Never
143 /// causes a copy.
144 ///
145 /// \returns a success error code if the data was successfully read, otherwise
146 /// returns an appropriate error code.
147 Error readSubstream(BinarySubstreamRef &Stream, uint32_t Size);
148
149 /// Get a pointer to an object of type T from the underlying stream, as if by
150 /// memcpy, and store the result into \p Dest. It is up to the caller to
151 /// ensure that objects of type T can be safely treated in this manner.
152 /// Updates the stream's offset to point after the newly read object. Whether
153 /// a copy occurs depends upon the implementation of the underlying
154 /// stream.
155 ///
156 /// \returns a success error code if the data was successfully read, otherwise
157 /// returns an appropriate error code.
158 template <typename T> Error readObject(const T *&Dest) {
159 ArrayRef<uint8_t> Buffer;
160 if (auto EC = readBytes(Buffer, sizeof(T)))
6
Taking true branch
161 return EC;
7
Returning without writing to 'Dest'
162 Dest = reinterpret_cast<const T *>(Buffer.data());
163 return Error::success();
164 }
165
166 /// Get a reference to a \p NumElements element array of objects of type T
167 /// from the underlying stream as if by memcpy, and store the resulting array
168 /// slice into \p array. It is up to the caller to ensure that objects of
169 /// type T can be safely treated in this manner. Updates the stream's offset
170 /// to point after the newly read object. Whether a copy occurs depends upon
171 /// the implementation of the underlying stream.
172 ///
173 /// \returns a success error code if the data was successfully read, otherwise
174 /// returns an appropriate error code.
175 template <typename T>
176 Error readArray(ArrayRef<T> &Array, uint32_t NumElements) {
177 ArrayRef<uint8_t> Bytes;
178 if (NumElements == 0) {
179 Array = ArrayRef<T>();
180 return Error::success();
181 }
182
183 if (NumElements > UINT32_MAX(4294967295U) / sizeof(T))
184 return make_error<BinaryStreamError>(
185 stream_error_code::invalid_array_size);
186
187 if (auto EC = readBytes(Bytes, NumElements * sizeof(T)))
188 return EC;
189
190 assert(alignmentAdjustment(Bytes.data(), alignof(T)) == 0 &&((alignmentAdjustment(Bytes.data(), alignof(T)) == 0 &&
"Reading at invalid alignment!") ? static_cast<void> (
0) : __assert_fail ("alignmentAdjustment(Bytes.data(), alignof(T)) == 0 && \"Reading at invalid alignment!\""
, "/build/llvm-toolchain-snapshot-8~svn345461/include/llvm/Support/BinaryStreamReader.h"
, 191, __PRETTY_FUNCTION__))
191 "Reading at invalid alignment!")((alignmentAdjustment(Bytes.data(), alignof(T)) == 0 &&
"Reading at invalid alignment!") ? static_cast<void> (
0) : __assert_fail ("alignmentAdjustment(Bytes.data(), alignof(T)) == 0 && \"Reading at invalid alignment!\""
, "/build/llvm-toolchain-snapshot-8~svn345461/include/llvm/Support/BinaryStreamReader.h"
, 191, __PRETTY_FUNCTION__))
;
192
193 Array = ArrayRef<T>(reinterpret_cast<const T *>(Bytes.data()), NumElements);
194 return Error::success();
195 }
196
197 /// Read a VarStreamArray of size \p Size bytes and store the result into
198 /// \p Array. Updates the stream's offset to point after the newly read
199 /// array. Never causes a copy (although iterating the elements of the
200 /// VarStreamArray may, depending upon the implementation of the underlying
201 /// stream).
202 ///
203 /// \returns a success error code if the data was successfully read, otherwise
204 /// returns an appropriate error code.
205 template <typename T, typename U>
206 Error readArray(VarStreamArray<T, U> &Array, uint32_t Size) {
207 BinaryStreamRef S;
208 if (auto EC = readStreamRef(S, Size))
209 return EC;
210 Array.setUnderlyingStream(S);
211 return Error::success();
212 }
213
214 /// Read a FixedStreamArray of \p NumItems elements and store the result into
215 /// \p Array. Updates the stream's offset to point after the newly read
216 /// array. Never causes a copy (although iterating the elements of the
217 /// FixedStreamArray may, depending upon the implementation of the underlying
218 /// stream).
219 ///
220 /// \returns a success error code if the data was successfully read, otherwise
221 /// returns an appropriate error code.
222 template <typename T>
223 Error readArray(FixedStreamArray<T> &Array, uint32_t NumItems) {
224 if (NumItems == 0) {
225 Array = FixedStreamArray<T>();
226 return Error::success();
227 }
228
229 if (NumItems > UINT32_MAX(4294967295U) / sizeof(T))
230 return make_error<BinaryStreamError>(
231 stream_error_code::invalid_array_size);
232
233 BinaryStreamRef View;
234 if (auto EC = readStreamRef(View, NumItems * sizeof(T)))
235 return EC;
236
237 Array = FixedStreamArray<T>(View);
238 return Error::success();
239 }
240
241 bool empty() const { return bytesRemaining() == 0; }
242 void setOffset(uint32_t Off) { Offset = Off; }
243 uint32_t getOffset() const { return Offset; }
244 uint32_t getLength() const { return Stream.getLength(); }
245 uint32_t bytesRemaining() const { return getLength() - getOffset(); }
246
247 /// Advance the stream's offset by \p Amount bytes.
248 ///
249 /// \returns a success error code if at least \p Amount bytes remain in the
250 /// stream, otherwise returns an appropriate error code.
251 Error skip(uint32_t Amount);
252
253 /// Examine the next byte of the underlying stream without advancing the
254 /// stream's offset. If the stream is empty the behavior is undefined.
255 ///
256 /// \returns the next byte in the stream.
257 uint8_t peek() const;
258
259 Error padToAlignment(uint32_t Align);
260
261 std::pair<BinaryStreamReader, BinaryStreamReader>
262 split(uint32_t Offset) const;
263
264private:
265 BinaryStreamRef Stream;
266 uint32_t Offset = 0;
267};
268} // namespace llvm
269
270#endif // LLVM_SUPPORT_BINARYSTREAMREADER_H