OLD | NEW |
---|---|
(Empty) | |
1 //===- llvm/unittest/Bitcode/NaClMungeWriteErrorTests.cpp -----------------===// | |
2 // Tests parser for PNaCl bitcode instructions. | |
3 // | |
4 // The LLVM Compiler Infrastructure | |
5 // | |
6 // This file is distributed under the University of Illinois Open Source | |
7 // License. See LICENSE.TXT for details. | |
8 // | |
9 //===----------------------------------------------------------------------===// | |
10 | |
11 // Tests write errors for munged bitcode. | |
12 | |
13 #include "llvm/ADT/STLExtras.h" | |
14 #include "llvm/Bitcode/NaCl/NaClBitcodeMunge.h" | |
15 #include "llvm/Bitcode/NaCl/NaClBitcodeParser.h" | |
16 #include "llvm/Bitcode/NaCl/NaClLLVMBitCodes.h" | |
17 | |
18 #include "gtest/gtest.h" | |
19 | |
20 using namespace llvm; | |
21 | |
22 namespace { | |
23 | |
24 // Test list of bitcode records. | |
25 static const uint64_t Terminator = 0x5768798008978675LL; | |
26 const uint64_t BitcodeRecords[] = { | |
27 1, naclbitc::BLK_CODE_ENTER, naclbitc::MODULE_BLOCK_ID, 2, Terminator, | |
28 1, naclbitc::BLK_CODE_ENTER, naclbitc::TYPE_BLOCK_ID_NEW, 3, Terminator, | |
29 3, naclbitc::TYPE_CODE_NUMENTRY, 2, Terminator, | |
30 3, naclbitc::TYPE_CODE_VOID, Terminator, | |
31 3, naclbitc::TYPE_CODE_FUNCTION, 0, 0, Terminator, | |
32 0, naclbitc::BLK_CODE_EXIT, Terminator, | |
33 3, naclbitc::MODULE_CODE_FUNCTION, 1, 0, 0, 0, Terminator, | |
34 1, naclbitc::BLK_CODE_ENTER, naclbitc::FUNCTION_BLOCK_ID, 2, Terminator, | |
35 3, naclbitc::FUNC_CODE_DECLAREBLOCKS, 1, Terminator, | |
36 3, naclbitc::FUNC_CODE_INST_RET, Terminator, | |
37 0, naclbitc::BLK_CODE_EXIT, Terminator, | |
38 0, naclbitc::BLK_CODE_EXIT, Terminator | |
39 }; | |
40 | |
41 // Expected output when bitcode records are dumped. | |
42 const char* ExpectedDump = | |
43 " 0:0|<65532, 80, 69, 88, 69, 1, 0,|Magic Number: 'PEXE' (80, 69, " | |
44 "88, 69)\n" | |
45 " | 8, 0, 17, 0, 4, 0, 2, 0, 0, |PNaCl Version: 2\n" | |
46 " | 0> |\n" | |
47 " 16:0|1: <65535, 8, 2> |module { // BlockID = 8\n" | |
48 " 24:0| 1: <65535, 17, 3> | types { // BlockID = 17\n" | |
49 " 32:0| 3: <1, 2> | count 2;\n" | |
50 " 34:5| 3: <2> | @t0 = void;\n" | |
51 " 36:4| 3: <21, 0, 0> | @t1 = void ();\n" | |
52 " 39:7| 0: <65534> | }\n" | |
53 " 44:0| 3: <8, 1, 0, 0, 0> | define external void @f0();\n" | |
54 " 48:6| 1: <65535, 12, 2> | function void @f0() { \n" | |
55 " | | // BlockID " | |
56 "= 12\n" | |
57 " 56:0| 3: <1, 1> | blocks 1;\n" | |
58 " | | %b0:\n" | |
59 " 58:4| 3: <10> | ret void;\n" | |
60 " 60:2| 0: <65534> | }\n" | |
61 " 64:0|0: <65534> |}\n" | |
62 ; | |
63 | |
64 // Edit to change void type with an illegal abbreviation index. | |
65 const uint64_t VoidTypeIndex = 3; // Index for "@t0 = void". | |
66 const uint64_t AbbrevIndex4VoidTypeEdit[] = { | |
67 VoidTypeIndex, NaClMungedBitcode::Replace, | |
68 4, naclbitc::TYPE_CODE_VOID, Terminator, | |
69 }; | |
70 | |
71 // Edit to add local abbreviation for "ret void", and then use on that | |
72 // instruction. | |
73 const uint64_t RetVoidIndex = 9; // return void; | |
74 const uint64_t UseLocalRetVoidAbbrevEdits[] = { | |
75 RetVoidIndex, NaClMungedBitcode::AddBefore, | |
76 2, naclbitc::BLK_CODE_DEFINE_ABBREV, 1, 1, | |
77 naclbitc::FUNC_CODE_INST_RET, Terminator, | |
78 RetVoidIndex, NaClMungedBitcode::Replace, | |
79 4, naclbitc::FUNC_CODE_INST_RET, Terminator | |
80 }; | |
81 | |
82 #define ARRAY_ARGS(Records) Records, array_lengthof(Records) | |
83 | |
84 #define ARRAY_ARGS_TERM(Records) ARRAY_ARGS(Records), Terminator | |
85 | |
86 std::string stringify(NaClBitcodeMunger &Munger) { | |
87 std::string Buffer; | |
88 raw_string_ostream StrBuf(Buffer); | |
89 Munger.getMungedBitcode().print(StrBuf); | |
90 return StrBuf.str(); | |
91 } | |
92 | |
93 // Show that we can dump the bitcode records | |
94 TEST(NaClMungeWriteErrorTests, DumpBitcodeRecords) { | |
95 NaClObjDumpMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
96 EXPECT_TRUE(Munger.runTest("DumpBitcodeRecords")); | |
97 EXPECT_EQ(ExpectedDump, Munger.getTestResults()); | |
98 } | |
99 | |
100 // Show that by default, one can't write a bad abbreviation index. | |
101 TEST(NaClMungeWriteErrorTests, CantWriteBadAbbrevIndex) { | |
102 NaClWriteMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
103 EXPECT_FALSE(Munger.runTest("CantWriteBadAbbrevIndex", | |
104 ARRAY_ARGS(AbbrevIndex4VoidTypeEdit))); | |
105 EXPECT_EQ( | |
106 "Error (Block 17): Uses illegal abbreviation index: 4: [2]\n" | |
107 "Error: Unable to generate bitcode file due to write errors\n", | |
108 Munger.getTestResults()); | |
109 } | |
110 | |
111 // Show that we can't write more local abbreviations than specified in | |
112 // the corresponding enclosing block. | |
113 TEST(NaClMungeWriteErrorTests, CantWriteTooManyLocalAbbreviations) { | |
114 NaClWriteMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
115 Munger.munge(ARRAY_ARGS(UseLocalRetVoidAbbrevEdits)); | |
116 EXPECT_EQ( | |
117 " 1: [65535, 8, 2]\n" | |
118 " 1: [65535, 17, 3]\n" | |
119 " 3: [1, 2]\n" | |
120 " 3: [2]\n" | |
121 " 3: [21, 0, 0]\n" | |
122 " 0: [65534]\n" | |
123 " 3: [8, 1, 0, 0, 0]\n" | |
124 " 1: [65535, 12, 2]\n" | |
125 " 3: [1, 1]\n" | |
126 " 2: [65533, 1, 1, 10]\n" | |
127 " 4: [10]\n" | |
128 " 0: [65534]\n" | |
129 " 0: [65534]\n", | |
130 stringify(Munger)); | |
131 | |
132 EXPECT_FALSE(Munger.runTest("CantWriteTooManyLocalAbbreviations")); | |
133 EXPECT_EQ( | |
134 "Error (Block 12): Exceeds abbreviation index limit of 3: 2: [65533," | |
135 " 1, 1, 10]\n" | |
136 "Error: Unable to generate bitcode file due to write errors\n", | |
137 Munger.getTestResults()); | |
138 } | |
139 | |
140 // Show what happens when there are more enter blocks then exit blocks. | |
141 TEST(NaClMungeWriteErrorTests, CantWriteTooManyEnterBlocks) { | |
142 NaClWriteMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
143 // Remove all but first two records (i.e. two enter blocks). | |
144 NaClMungedBitcode &MungedBitcode = Munger.getMungedBitcode(); | |
145 for (size_t i = 2; i < MungedBitcode.getBaseRecords().size(); ++i) { | |
146 MungedBitcode.remove(i); | |
147 } | |
148 | |
149 EXPECT_FALSE(Munger.runTest("TooManyEnterBlocks")); | |
jvoung (off chromium)
2015/05/13 22:24:06
CantWriteTooManyEnterBlocks -- I wasn't too sure t
Karl
2015/05/14 17:08:12
I agree that these names are no longer useful. Cha
| |
150 EXPECT_EQ( | |
151 "Error (Block 17): Missing close block.\n" | |
152 "Error (Block 8): Missing close block.\n" | |
153 "Error: Unable to generate bitcode file due to write errors\n", | |
154 Munger.getTestResults()); | |
155 } | |
156 | |
157 // Show what happens when there are fewer enter blocks than exit | |
158 // blocks. | |
159 TEST(NaClMungeWriteErrorTests, CantWriteTooManyExitBlocks) { | |
160 NaClWriteMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
161 // Add two exit blocks. | |
162 NaClMungedBitcode &MungedBitcode = Munger.getMungedBitcode(); | |
163 NaClRecordVector Values; | |
164 NaClBitcodeAbbrevRecord Record(0, naclbitc::BLK_CODE_EXIT, Values); | |
165 for (size_t i = 0; i < 2; ++i) | |
166 MungedBitcode.addAfter(MungedBitcode.getBaseRecords().size() - 1, Record); | |
167 | |
168 EXPECT_FALSE(Munger.runTest("CantWriteTooManyExitBlocks")); | |
169 EXPECT_EQ( | |
170 "Error (Block unknown): Extraneous exit block: 0: [65534]\n" | |
171 "Error: Unable to generate bitcode file due to write errors\n", | |
172 Munger.getTestResults()); | |
173 } | |
174 | |
175 // Show that an error occurs when writing a bitcode record that isn't | |
176 // in any block. | |
177 TEST(NaClMungeWriteErrorTests, CantWriteRecordOutsideBlock) { | |
178 NaClWriteMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
179 NaClMungedBitcode &MungedBitcode = Munger.getMungedBitcode(); | |
180 NaClRecordVector Values; | |
181 Values.push_back(4); | |
182 NaClBitcodeAbbrevRecord Record(naclbitc::UNABBREV_RECORD, | |
183 naclbitc::MODULE_CODE_VERSION, | |
184 Values); | |
185 | |
186 MungedBitcode.addAfter(MungedBitcode.getBaseRecords().size() - 1, Record); | |
187 EXPECT_FALSE(Munger.runTest("CantWriteRecordOutsideBlock")); | |
188 EXPECT_EQ( | |
189 "Error (Block unknown): Record outside block: 3: [1, 4]\n" | |
190 "Error: Unable to generate bitcode file due to write errors\n", | |
191 Munger.getTestResults()); | |
192 } | |
193 | |
194 // Show that no error occurs if we write out the maximum allowable | |
195 // block abbreviation index bit limit. | |
196 TEST(NaClMungerWriteErrorTests, CantWriteBlockWithMaxLimit) { | |
Karl
2015/05/13 21:39:25
Added this test.
jvoung (off chromium)
2015/05/13 22:24:06
Seems like Cant should be Can, since this doesn't
Karl
2015/05/14 17:08:12
Good point. Fixing name.
| |
197 // Replace initial block enter with maximum bit size. | |
198 const uint64_t Edit[] = { | |
199 0, NaClMungedBitcode::Replace, | |
200 1, naclbitc::BLK_CODE_ENTER, naclbitc::MODULE_BLOCK_ID, | |
201 naclbitc::MaxAbbrevWidth, Terminator | |
202 }; | |
203 NaClWriteMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
204 EXPECT_TRUE(Munger.runTest("CantWriteBlockWithMaxLimit", ARRAY_ARGS(Edit))); | |
205 EXPECT_EQ( | |
206 " 1: [65535, 8, 32]\n" | |
207 " 1: [65535, 17, 3]\n" | |
208 " 3: [1, 2]\n" | |
209 " 3: [2]\n" | |
210 " 3: [21, 0, 0]\n" | |
211 " 0: [65534]\n" | |
212 " 3: [8, 1, 0, 0, 0]\n" | |
213 " 1: [65535, 12, 2]\n" | |
214 " 3: [1, 1]\n" | |
215 " 3: [10]\n" | |
216 " 0: [65534]\n" | |
217 " 0: [65534]\n", | |
218 Munger.getTestResults()); | |
219 } | |
220 | |
221 // Show that an error occurs if the block abbreviation index bit limit is | |
222 // greater than the maximum allowable. | |
223 TEST(NaClMungerWriteErrorTests, CantWriteBlockWithBadBitLimit) { | |
Karl
2015/05/13 21:39:25
Added this test.
| |
224 // Replace initial block enter with value out of range. | |
225 const uint64_t Edit[] = { | |
226 0, NaClMungedBitcode::Replace, | |
227 1, naclbitc::BLK_CODE_ENTER, naclbitc::MODULE_BLOCK_ID, | |
228 naclbitc::MaxAbbrevWidth + 1, Terminator | |
229 }; | |
230 NaClWriteMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
231 EXPECT_FALSE(Munger.runTest("CantWriteBlockWithBadBitLimit", | |
232 ARRAY_ARGS(Edit))); | |
233 EXPECT_EQ( | |
234 "Error (Block unknown): Block index bit limit 33 invalid. Must be in" | |
235 " [2..32]: 1: [65535, 8, 33]\n" | |
236 "Error: Unable to generate bitcode file due to write errors\n", | |
237 Munger.getTestResults()); | |
238 } | |
239 | |
240 // Show that we can't write an enter block with a very large block id. | |
241 TEST(NaClMungerWriteErrorTests, CantWriteBlockWithLargeBlockID) { | |
Karl
2015/05/13 21:39:25
Added this test.
| |
242 // Replace initial block enter with value out of range. | |
243 const uint64_t Edit[] = { | |
244 0, NaClMungedBitcode::Replace, | |
245 1, naclbitc::BLK_CODE_ENTER, (uint64_t)1 << 33, 2, Terminator | |
246 }; | |
247 NaClWriteMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
248 EXPECT_FALSE(Munger.runTest("CantWriteBlockWithLargeBlockID", | |
249 ARRAY_ARGS(Edit))); | |
250 EXPECT_EQ( | |
251 "Error (Block unknown): Block id must be less than << 4294967295: 1:" | |
jvoung (off chromium)
2015/05/13 22:24:06
"less than << 4294967295" -- why the "<<" ?
Karl
2015/05/14 17:08:11
Done.
| |
252 " [65535, 8589934592, 2]\n" | |
253 "Error: Unable to generate bitcode file due to write errors\n", | |
254 Munger.getTestResults()); | |
255 } | |
256 | |
257 // Show that parsing successfully recovers (i.e. not crash) if the | |
jvoung (off chromium)
2015/05/13 22:24:06
"parsing successfully recovers" ? I thought it was
Karl
2015/05/14 17:08:11
Good point. Rewrote to better fit test.
| |
258 // bitcode has a bad abbreviation index. Show this by forcing | |
259 // the bad abbreviation index into the bitcode. | |
260 TEST(MyNaClMungerWriteErrorTests, DieOnWriteBadAbbreviationIndex) { | |
Karl
2015/05/13 21:39:25
Added this test.
jvoung (off chromium)
2015/05/13 22:24:06
My... -> NaClMungerWriteErrorTests
| |
261 NaClWriteMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
262 Munger.setWriteBadAbbrevIndex(true); | |
263 Munger.setRunAsDeathTest(true); | |
264 EXPECT_DEATH( | |
265 Munger.runTest("DieOnWriteBadAbbrevIndex", | |
266 ARRAY_ARGS(AbbrevIndex4VoidTypeEdit)), | |
267 ".*" | |
268 // Report problem while writing. | |
269 "Error \\(Block 17\\)\\: Uses illegal abbreviation index\\: 4\\: \\[2\\]" | |
270 ".*" | |
271 // Corresponding error while parsing. | |
272 "Fatal\\(35\\:0)\\: Invalid abbreviation \\# 4 defined for record" | |
273 ".*" | |
274 // Output of report_fatal_error. | |
275 "LLVM ERROR\\: Unable to continue" | |
276 ".*"); | |
277 } | |
278 | |
279 // Show that error recovery works when writing an illegal abbreviation | |
280 // index. Show success by parsing fixed bitcode. | |
281 TEST(NaClMungeWriteErrorTests, RecoverWhenParsingBadAbbrevIndex) { | |
282 NaClParseBitcodeMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
283 Munger.setTryToRecoverOnWrite(true); | |
284 EXPECT_TRUE( | |
285 Munger.runTest("RecoverWhenParsingBadAbbrevIndex", | |
286 ARRAY_ARGS(AbbrevIndex4VoidTypeEdit), true)); | |
287 EXPECT_EQ( | |
288 "Error (Block 17): Uses illegal abbreviation index: 4: [2]\n" | |
289 "Successful parse!\n", | |
290 Munger.getTestResults()); | |
291 } | |
292 | |
293 // Show that error recovery works when writing an illegal abbreviation | |
294 // index. Show success by Dumping fixed bitcode. | |
295 TEST(NaClMungeWriteErrorTests, RecoverWhenParsingBadAbbreviationIndex) { | |
296 NaClObjDumpMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
297 Munger.setTryToRecoverOnWrite(true); | |
298 EXPECT_TRUE(Munger.runTest("RecoverWhenParsingBadAbbreviationIndex", | |
299 ARRAY_ARGS(AbbrevIndex4VoidTypeEdit))); | |
300 std::string Results( | |
301 "Error (Block 17): Uses illegal abbreviation index: 4: [2]\n"); | |
302 Results.append(ExpectedDump); | |
303 EXPECT_EQ(Results, Munger.getTestResults()); | |
304 } | |
305 | |
306 // Show that error recovery works when writing too many locally | |
307 // defined abbreviations for the corresponding number of bits defined | |
308 // in the corresponding enter block. Show success by dumping the fixed | |
309 // bitcode. | |
310 TEST(NaClMungeWriteErrorTests, RecoverTooManyLocalAbbreviations) { | |
311 NaClObjDumpMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
312 Munger.setTryToRecoverOnWrite(true); | |
313 Munger.munge(ARRAY_ARGS(UseLocalRetVoidAbbrevEdits)); | |
314 | |
315 EXPECT_TRUE(Munger.runTest("RecoverTooManyLocalAbbreviations")); | |
316 std::string Results( | |
317 "Error (Block 12): Exceeds abbreviation index limit of 3: 2:" | |
318 " [65533, 1, 1, 10]\n" | |
319 "Error (Block 12): Uses illegal abbreviation index: 4: [10]\n"); | |
320 Results.append(ExpectedDump); | |
321 EXPECT_EQ( | |
322 Results, | |
323 Munger.getTestResults()); | |
324 } | |
325 | |
326 // Show that error recovery works when writing and there are more | |
327 // enter blocks than exit blocks. Show success by dumping fixed | |
328 // bitcode. | |
329 TEST(NaClMungeWriteErrorTests, RecoverTooManyEnterBlocks) { | |
330 NaClObjDumpMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
331 // Remove all but first two records (i.e. two enter blocks). | |
332 NaClMungedBitcode &MungedBitcode = Munger.getMungedBitcode(); | |
333 for (size_t i = 2; i < MungedBitcode.getBaseRecords().size(); ++i) { | |
334 MungedBitcode.remove(i); | |
335 } | |
336 | |
337 Munger.setTryToRecoverOnWrite(true); | |
338 EXPECT_TRUE(Munger.runTest("RecoverTooManyEnterBlocks")); | |
339 EXPECT_EQ( | |
340 "Error (Block 17): Missing close block.\n" | |
341 "Error (Block 8): Missing close block.\n" | |
342 " 0:0|<65532, 80, 69, 88, 69, 1, 0,|Magic Number: 'PEXE' (80, 69," | |
343 " 88, 69)\n" | |
344 " | 8, 0, 17, 0, 4, 0, 2, 0, 0, |PNaCl Version: 2\n" | |
345 " | 0> |\n" | |
346 " 16:0|1: <65535, 8, 2> |module { // BlockID = 8\n" | |
347 " 24:0| 1: <65535, 17, 3> | types { // BlockID = 17\n" | |
348 " 32:0| 0: <65534> | }\n" | |
349 " 36:0|0: <65534> |}\n", | |
350 Munger.getTestResults()); | |
351 } | |
352 | |
353 // Show that error recovery works when writing and there are fewer | |
354 // enter blocks than exit blocks. Show success by dumping the fixed | |
355 // bitcode. | |
356 TEST(NaClMungeWriteErrorTests, RecoverTooManyExitBlocks) { | |
357 NaClObjDumpMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
358 // Add two exit blocks. | |
359 NaClMungedBitcode &MungedBitcode = Munger.getMungedBitcode(); | |
360 NaClRecordVector Values; | |
361 NaClBitcodeAbbrevRecord Record(0, naclbitc::BLK_CODE_EXIT, Values); | |
362 for (size_t i = 0; i < 2; ++i) | |
363 MungedBitcode.addAfter(MungedBitcode.getBaseRecords().size() - 1, Record); | |
364 | |
365 Munger.setTryToRecoverOnWrite(true); | |
366 EXPECT_TRUE(Munger.runTest("RecoverTooManyExitBlocks")); | |
367 std::string Results( | |
368 "Error (Block unknown): Extraneous exit block: 0: [65534]\n" | |
369 "Error (Block unknown): Extraneous exit block: 0: [65534]\n"); | |
370 Results.append(ExpectedDump); | |
371 EXPECT_EQ( | |
372 Results, | |
373 Munger.getTestResults()); | |
374 } | |
375 | |
376 // Show that error recovery works when writing a bitcode record that | |
377 // isn't in any block. Show success by showing fixed bitcode records. | |
378 TEST(NaClMungeWriteErrorTests, RecoverWriteRecordOutsideBlock) { | |
379 NaClWriteMunger Munger(ARRAY_ARGS_TERM(BitcodeRecords)); | |
380 NaClMungedBitcode &MungedBitcode = Munger.getMungedBitcode(); | |
381 NaClRecordVector Values; | |
382 Values.push_back(4); | |
383 NaClBitcodeAbbrevRecord Record(naclbitc::UNABBREV_RECORD, | |
384 naclbitc::MODULE_CODE_VERSION, | |
385 Values); | |
386 MungedBitcode.addAfter(MungedBitcode.getBaseRecords().size() - 1, Record); | |
387 | |
388 Munger.setTryToRecoverOnWrite(true); | |
389 EXPECT_TRUE(Munger.runTest("RecoverWriteRecordOutsideBlock")); | |
390 EXPECT_EQ( | |
391 "Error (Block unknown): Record outside block: 3: [1, 4]\n" | |
392 "Error (Block unknown): Missing close block.\n" | |
393 " 1: [65535, 8, 2]\n" | |
394 " 1: [65535, 17, 3]\n" | |
395 " 3: [1, 2]\n" | |
396 " 3: [2]\n" | |
397 " 3: [21, 0, 0]\n" | |
398 " 0: [65534]\n" | |
399 " 3: [8, 1, 0, 0, 0]\n" | |
400 " 1: [65535, 12, 2]\n" | |
401 " 3: [1, 1]\n" | |
402 " 3: [10]\n" | |
403 " 0: [65534]\n" | |
404 " 0: [65534]\n" | |
405 " 1: [65535, 4294967295, 3]\n" | |
406 " 3: [1, 4]\n" | |
407 " 0: [65534]\n", | |
408 Munger.getTestResults()); | |
409 } | |
410 | |
411 } // end of anonymous namespace. | |
OLD | NEW |