File: | build/source/llvm/lib/WindowsManifest/WindowsManifestMerger.cpp |
Warning: | line 607, column 18 Access to field 'next' results in a dereference of an undefined pointer value (loaded from variable 'Prev') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-- WindowsManifestMerger.cpp ------------------------------*- C++ -*-===// | |||
2 | // | |||
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||
4 | // See https://llvm.org/LICENSE.txt for license information. | |||
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||
6 | // | |||
7 | //===---------------------------------------------------------------------===// | |||
8 | // | |||
9 | // This file implements the .manifest merger class. | |||
10 | // | |||
11 | //===---------------------------------------------------------------------===// | |||
12 | ||||
13 | #include "llvm/WindowsManifest/WindowsManifestMerger.h" | |||
14 | #include "llvm/Config/config.h" | |||
15 | #include "llvm/Support/MemoryBuffer.h" | |||
16 | ||||
17 | #if LLVM_ENABLE_LIBXML21 | |||
18 | #include <libxml/xmlreader.h> | |||
19 | #endif | |||
20 | ||||
21 | #define TO_XML_CHAR(X)reinterpret_cast<const unsigned char *>(X) reinterpret_cast<const unsigned char *>(X) | |||
22 | #define FROM_XML_CHAR(X)reinterpret_cast<const char *>(X) reinterpret_cast<const char *>(X) | |||
23 | ||||
24 | using namespace llvm; | |||
25 | using namespace windows_manifest; | |||
26 | ||||
27 | char WindowsManifestError::ID = 0; | |||
28 | ||||
29 | WindowsManifestError::WindowsManifestError(const Twine &Msg) : Msg(Msg.str()) {} | |||
30 | ||||
31 | void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; } | |||
32 | ||||
33 | class WindowsManifestMerger::WindowsManifestMergerImpl { | |||
34 | public: | |||
35 | ~WindowsManifestMergerImpl(); | |||
36 | Error merge(MemoryBufferRef Manifest); | |||
37 | std::unique_ptr<MemoryBuffer> getMergedManifest(); | |||
38 | ||||
39 | private: | |||
40 | static void errorCallback(void *Ctx, const char *Format, ...); | |||
41 | Error getParseError(); | |||
42 | #if LLVM_ENABLE_LIBXML21 | |||
43 | xmlDocPtr CombinedDoc = nullptr; | |||
44 | std::vector<xmlDocPtr> MergedDocs; | |||
45 | ||||
46 | bool Merged = false; | |||
47 | struct XmlDeleter { | |||
48 | void operator()(xmlChar *Ptr) { xmlFree(Ptr); } | |||
49 | void operator()(xmlDoc *Ptr) { xmlFreeDoc(Ptr); } | |||
50 | }; | |||
51 | int BufferSize = 0; | |||
52 | std::unique_ptr<xmlChar, XmlDeleter> Buffer; | |||
53 | #endif | |||
54 | bool ParseErrorOccurred = false; | |||
55 | }; | |||
56 | ||||
57 | #if LLVM_ENABLE_LIBXML21 | |||
58 | ||||
59 | static constexpr std::pair<StringLiteral, StringLiteral> MtNsHrefsPrefixes[] = { | |||
60 | {"urn:schemas-microsoft-com:asm.v1", "ms_asmv1"}, | |||
61 | {"urn:schemas-microsoft-com:asm.v2", "ms_asmv2"}, | |||
62 | {"urn:schemas-microsoft-com:asm.v3", "ms_asmv3"}, | |||
63 | {"http://schemas.microsoft.com/SMI/2005/WindowsSettings", | |||
64 | "ms_windowsSettings"}, | |||
65 | {"urn:schemas-microsoft-com:compatibility.v1", "ms_compatibilityv1"}}; | |||
66 | ||||
67 | static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) { | |||
68 | // Handle null pointers. Comparison of 2 null pointers returns true because | |||
69 | // this indicates the prefix of a default namespace. | |||
70 | if (!A || !B) | |||
71 | return A == B; | |||
72 | return strcmp(FROM_XML_CHAR(A)reinterpret_cast<const char *>(A), FROM_XML_CHAR(B)reinterpret_cast<const char *>(B)) == 0; | |||
73 | } | |||
74 | ||||
75 | static bool isMergeableElement(const unsigned char *ElementName) { | |||
76 | for (StringRef S : {"application", "assembly", "assemblyIdentity", | |||
77 | "compatibility", "noInherit", "requestedExecutionLevel", | |||
78 | "requestedPrivileges", "security", "trustInfo"}) { | |||
79 | if (S == FROM_XML_CHAR(ElementName)reinterpret_cast<const char *>(ElementName)) { | |||
80 | return true; | |||
81 | } | |||
82 | } | |||
83 | return false; | |||
84 | } | |||
85 | ||||
86 | static xmlNodePtr getChildWithName(xmlNodePtr Parent, | |||
87 | const unsigned char *ElementName) { | |||
88 | for (xmlNodePtr Child = Parent->children; Child; Child = Child->next) { | |||
89 | if (xmlStringsEqual(Child->name, ElementName)) { | |||
90 | return Child; | |||
91 | } | |||
92 | } | |||
93 | return nullptr; | |||
94 | } | |||
95 | ||||
96 | static xmlAttrPtr getAttribute(xmlNodePtr Node, | |||
97 | const unsigned char *AttributeName) { | |||
98 | for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr; | |||
99 | Attribute = Attribute->next) { | |||
100 | if (xmlStringsEqual(Attribute->name, AttributeName)) { | |||
101 | return Attribute; | |||
102 | } | |||
103 | } | |||
104 | return nullptr; | |||
105 | } | |||
106 | ||||
107 | // Check if namespace specified by HRef1 overrides that of HRef2. | |||
108 | static bool namespaceOverrides(const unsigned char *HRef1, | |||
109 | const unsigned char *HRef2) { | |||
110 | auto HRef1Position = llvm::find_if( | |||
111 | MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) { | |||
112 | return xmlStringsEqual(HRef1, TO_XML_CHAR(Element.first.data())reinterpret_cast<const unsigned char *>(Element.first.data ())); | |||
113 | }); | |||
114 | auto HRef2Position = llvm::find_if( | |||
115 | MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) { | |||
116 | return xmlStringsEqual(HRef2, TO_XML_CHAR(Element.first.data())reinterpret_cast<const unsigned char *>(Element.first.data ())); | |||
117 | }); | |||
118 | return HRef1Position < HRef2Position; | |||
119 | } | |||
120 | ||||
121 | // Search for prefix-defined namespace specified by HRef, starting on Node and | |||
122 | // continuing recursively upwards. Returns the namespace or nullptr if not | |||
123 | // found. | |||
124 | static xmlNsPtr search(const unsigned char *HRef, xmlNodePtr Node) { | |||
125 | for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { | |||
126 | if (Def->prefix && xmlStringsEqual(Def->href, HRef)) { | |||
127 | return Def; | |||
128 | } | |||
129 | } | |||
130 | if (Node->parent) { | |||
131 | return search(HRef, Node->parent); | |||
132 | } | |||
133 | return nullptr; | |||
134 | } | |||
135 | ||||
136 | // Return the prefix that corresponds to the HRef. If HRef is not a recognized | |||
137 | // URI, then just return the HRef itself to use as the prefix. | |||
138 | static const unsigned char *getPrefixForHref(const unsigned char *HRef) { | |||
139 | for (auto &Ns : MtNsHrefsPrefixes) { | |||
140 | if (xmlStringsEqual(HRef, TO_XML_CHAR(Ns.first.data())reinterpret_cast<const unsigned char *>(Ns.first.data() ))) { | |||
141 | return TO_XML_CHAR(Ns.second.data())reinterpret_cast<const unsigned char *>(Ns.second.data( )); | |||
142 | } | |||
143 | } | |||
144 | return HRef; | |||
145 | } | |||
146 | ||||
147 | // Search for prefix-defined namespace specified by HRef, starting on Node and | |||
148 | // continuing recursively upwards. If it is found, then return it. If it is | |||
149 | // not found, then prefix-define that namespace on the node and return a | |||
150 | // reference to it. | |||
151 | static Expected<xmlNsPtr> searchOrDefine(const unsigned char *HRef, | |||
152 | xmlNodePtr Node) { | |||
153 | if (xmlNsPtr Def = search(HRef, Node)) | |||
154 | return Def; | |||
155 | if (xmlNsPtr Def = xmlNewNs(Node, HRef, getPrefixForHref(HRef))) | |||
156 | return Def; | |||
157 | return make_error<WindowsManifestError>("failed to create new namespace"); | |||
158 | } | |||
159 | ||||
160 | // Set the namespace of OrigionalAttribute on OriginalNode to be that of | |||
161 | // AdditionalAttribute's. | |||
162 | static Error copyAttributeNamespace(xmlAttrPtr OriginalAttribute, | |||
163 | xmlNodePtr OriginalNode, | |||
164 | xmlAttrPtr AdditionalAttribute) { | |||
165 | ||||
166 | Expected<xmlNsPtr> ExplicitOrError = | |||
167 | searchOrDefine(AdditionalAttribute->ns->href, OriginalNode); | |||
168 | if (!ExplicitOrError) | |||
169 | return ExplicitOrError.takeError(); | |||
170 | OriginalAttribute->ns = std::move(ExplicitOrError.get()); | |||
171 | return Error::success(); | |||
172 | } | |||
173 | ||||
174 | // Return the corresponding namespace definition for the prefix, defined on the | |||
175 | // given Node. Returns nullptr if there is no such definition. | |||
176 | static xmlNsPtr getNamespaceWithPrefix(const unsigned char *Prefix, | |||
177 | xmlNodePtr Node) { | |||
178 | if (Node == nullptr) | |||
179 | return nullptr; | |||
180 | for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { | |||
181 | if (xmlStringsEqual(Def->prefix, Prefix)) { | |||
182 | return Def; | |||
183 | } | |||
184 | } | |||
185 | return nullptr; | |||
186 | } | |||
187 | ||||
188 | // Search for the closest inheritable default namespace, starting on (and | |||
189 | // including) the Node and traveling upwards through parent nodes. Returns | |||
190 | // nullptr if there are no inheritable default namespaces. | |||
191 | static xmlNsPtr getClosestDefault(xmlNodePtr Node) { | |||
192 | if (xmlNsPtr Ret = getNamespaceWithPrefix(nullptr, Node)) | |||
193 | return Ret; | |||
194 | if (Node->parent == nullptr) | |||
195 | return nullptr; | |||
196 | return getClosestDefault(Node->parent); | |||
197 | } | |||
198 | ||||
199 | // Merge the attributes of AdditionalNode into OriginalNode. If attributes | |||
200 | // with identical types are present, they are not duplicated but rather if | |||
201 | // their values are not consistent and error is thrown. In addition, the | |||
202 | // higher priority namespace is used for each attribute, EXCEPT in the case | |||
203 | // of merging two default namespaces and the lower priority namespace | |||
204 | // definition occurs closer than the higher priority one. | |||
205 | static Error mergeAttributes(xmlNodePtr OriginalNode, | |||
206 | xmlNodePtr AdditionalNode) { | |||
207 | xmlNsPtr ClosestDefault = getClosestDefault(OriginalNode); | |||
208 | for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute; | |||
209 | Attribute = Attribute->next) { | |||
210 | if (xmlAttrPtr OriginalAttribute = | |||
211 | getAttribute(OriginalNode, Attribute->name)) { | |||
212 | if (!xmlStringsEqual(OriginalAttribute->children->content, | |||
213 | Attribute->children->content)) { | |||
214 | return make_error<WindowsManifestError>( | |||
215 | Twine("conflicting attributes for ") + | |||
216 | FROM_XML_CHAR(OriginalNode->name)reinterpret_cast<const char *>(OriginalNode->name)); | |||
217 | } | |||
218 | if (!Attribute->ns) { | |||
219 | continue; | |||
220 | } | |||
221 | if (!OriginalAttribute->ns) { | |||
222 | if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, | |||
223 | Attribute)) { | |||
224 | return E; | |||
225 | } | |||
226 | continue; | |||
227 | } | |||
228 | if (namespaceOverrides(OriginalAttribute->ns->href, | |||
229 | Attribute->ns->href)) { | |||
230 | // In this case, the original attribute has a higher priority namespace | |||
231 | // than the incomiing attribute, however the namespace definition of | |||
232 | // the lower priority namespace occurs first traveling upwards in the | |||
233 | // tree. Therefore the lower priority namespace is applied. | |||
234 | if (!OriginalAttribute->ns->prefix && !Attribute->ns->prefix && | |||
235 | ClosestDefault && | |||
236 | xmlStringsEqual(Attribute->ns->href, ClosestDefault->href)) { | |||
237 | if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, | |||
238 | Attribute)) { | |||
239 | return E; | |||
240 | } | |||
241 | continue; | |||
242 | } | |||
243 | continue; | |||
244 | // This covers the case where the incoming attribute has the higher | |||
245 | // priority. The higher priority namespace is applied in all cases | |||
246 | // EXCEPT when both of the namespaces are default inherited, and the | |||
247 | // closest inherited default is the lower priority one. | |||
248 | } | |||
249 | if (Attribute->ns->prefix || OriginalAttribute->ns->prefix || | |||
250 | (ClosestDefault && !xmlStringsEqual(OriginalAttribute->ns->href, | |||
251 | ClosestDefault->href))) { | |||
252 | if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, | |||
253 | Attribute)) { | |||
254 | return E; | |||
255 | } | |||
256 | continue; | |||
257 | } | |||
258 | continue; | |||
259 | } | |||
260 | // If the incoming attribute is not already found on the node, append it | |||
261 | // to the end of the properties list. Also explicitly apply its | |||
262 | // namespace as a prefix because it might be contained in a separate | |||
263 | // namespace that doesn't use the attribute. | |||
264 | xmlAttrPtr NewProp = | |||
265 | xmlNewProp(OriginalNode, Attribute->name, Attribute->children->content); | |||
266 | Expected<xmlNsPtr> ExplicitOrError = | |||
267 | searchOrDefine(Attribute->ns->href, OriginalNode); | |||
268 | if (!ExplicitOrError) | |||
269 | return ExplicitOrError.takeError(); | |||
270 | NewProp->ns = std::move(ExplicitOrError.get()); | |||
271 | } | |||
272 | return Error::success(); | |||
273 | } | |||
274 | ||||
275 | // Given two nodes, return the one with the higher priority namespace. | |||
276 | static xmlNodePtr getDominantNode(xmlNodePtr Node1, xmlNodePtr Node2) { | |||
277 | ||||
278 | if (!Node1 || !Node1->ns) | |||
279 | return Node2; | |||
280 | if (!Node2 || !Node2->ns) | |||
281 | return Node1; | |||
282 | if (namespaceOverrides(Node1->ns->href, Node2->ns->href)) | |||
283 | return Node1; | |||
284 | return Node2; | |||
285 | } | |||
286 | ||||
287 | // Checks if this Node's namespace is inherited or one it defined itself. | |||
288 | static bool hasInheritedNs(xmlNodePtr Node) { | |||
289 | return Node->ns && Node->ns != getNamespaceWithPrefix(Node->ns->prefix, Node); | |||
290 | } | |||
291 | ||||
292 | // Check if this Node's namespace is a default namespace that it inherited, as | |||
293 | // opposed to defining itself. | |||
294 | static bool hasInheritedDefaultNs(xmlNodePtr Node) { | |||
295 | return hasInheritedNs(Node) && Node->ns->prefix == nullptr; | |||
296 | } | |||
297 | ||||
298 | // Check if this Node's namespace is a default namespace it defined itself. | |||
299 | static bool hasDefinedDefaultNamespace(xmlNodePtr Node) { | |||
300 | return Node->ns && (Node->ns == getNamespaceWithPrefix(nullptr, Node)); | |||
301 | } | |||
302 | ||||
303 | // For the given explicit prefix-definition of a namespace, travel downwards | |||
304 | // from a node recursively, and for every implicit, inherited default usage of | |||
305 | // that namespace replace it with that explicit prefix use. This is important | |||
306 | // when namespace overriding occurs when merging, so that elements unique to a | |||
307 | // namespace will still stay in that namespace. | |||
308 | static void explicateNamespace(xmlNsPtr PrefixDef, xmlNodePtr Node) { | |||
309 | // If a node as its own default namespace definition it clearly cannot have | |||
310 | // inherited the given default namespace, and neither will any of its | |||
311 | // children. | |||
312 | if (hasDefinedDefaultNamespace(Node)) | |||
313 | return; | |||
314 | if (Node->ns && xmlStringsEqual(Node->ns->href, PrefixDef->href) && | |||
315 | hasInheritedDefaultNs(Node)) | |||
316 | Node->ns = PrefixDef; | |||
317 | for (xmlAttrPtr Attribute = Node->properties; Attribute; | |||
318 | Attribute = Attribute->next) { | |||
319 | if (Attribute->ns && | |||
320 | xmlStringsEqual(Attribute->ns->href, PrefixDef->href)) { | |||
321 | Attribute->ns = PrefixDef; | |||
322 | } | |||
323 | } | |||
324 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { | |||
325 | explicateNamespace(PrefixDef, Child); | |||
326 | } | |||
327 | } | |||
328 | ||||
329 | // Perform the namespace merge between two nodes. | |||
330 | static Error mergeNamespaces(xmlNodePtr OriginalNode, | |||
331 | xmlNodePtr AdditionalNode) { | |||
332 | // Save the original default namespace definition in case the incoming node | |||
333 | // overrides it. | |||
334 | const unsigned char *OriginalDefinedDefaultHref = nullptr; | |||
335 | if (xmlNsPtr OriginalDefinedDefaultNs = | |||
336 | getNamespaceWithPrefix(nullptr, OriginalNode)) { | |||
337 | OriginalDefinedDefaultHref = xmlStrdup(OriginalDefinedDefaultNs->href); | |||
338 | } | |||
339 | const unsigned char *NewDefinedDefaultHref = nullptr; | |||
340 | // Copy all namespace definitions. There can only be one default namespace | |||
341 | // definition per node, so the higher priority one takes precedence in the | |||
342 | // case of collision. | |||
343 | for (xmlNsPtr Def = AdditionalNode->nsDef; Def; Def = Def->next) { | |||
344 | if (xmlNsPtr OriginalNsDef = | |||
345 | getNamespaceWithPrefix(Def->prefix, OriginalNode)) { | |||
346 | if (!Def->prefix) { | |||
347 | if (namespaceOverrides(Def->href, OriginalNsDef->href)) { | |||
348 | NewDefinedDefaultHref = TO_XML_CHAR(strdup(FROM_XML_CHAR(Def->href)))reinterpret_cast<const unsigned char *>(strdup(reinterpret_cast <const char *>(Def->href))); | |||
349 | } | |||
350 | } else if (!xmlStringsEqual(OriginalNsDef->href, Def->href)) { | |||
351 | return make_error<WindowsManifestError>( | |||
352 | Twine("conflicting namespace definitions for ") + | |||
353 | FROM_XML_CHAR(Def->prefix)reinterpret_cast<const char *>(Def->prefix)); | |||
354 | } | |||
355 | } else { | |||
356 | xmlNsPtr NewDef = xmlCopyNamespace(Def); | |||
357 | NewDef->next = OriginalNode->nsDef; | |||
358 | OriginalNode->nsDef = NewDef; | |||
359 | } | |||
360 | } | |||
361 | ||||
362 | // Check whether the original node or the incoming node has the higher | |||
363 | // priority namespace. Depending on which one is dominant, we will have | |||
364 | // to recursively apply namespace changes down to children of the original | |||
365 | // node. | |||
366 | xmlNodePtr DominantNode = getDominantNode(OriginalNode, AdditionalNode); | |||
367 | xmlNodePtr NonDominantNode = | |||
368 | DominantNode == OriginalNode ? AdditionalNode : OriginalNode; | |||
369 | if (DominantNode == OriginalNode) { | |||
370 | if (OriginalDefinedDefaultHref) { | |||
371 | xmlNsPtr NonDominantDefinedDefault = | |||
372 | getNamespaceWithPrefix(nullptr, NonDominantNode); | |||
373 | // In this case, both the nodes defined a default namespace. However | |||
374 | // the lower priority node ended up having a higher priority default | |||
375 | // definition. This can occur if the higher priority node is prefix | |||
376 | // namespace defined. In this case we have to define an explicit | |||
377 | // prefix for the overridden definition and apply it to all children | |||
378 | // who relied on that definition. | |||
379 | if (NonDominantDefinedDefault && | |||
380 | namespaceOverrides(NonDominantDefinedDefault->href, | |||
381 | OriginalDefinedDefaultHref)) { | |||
382 | Expected<xmlNsPtr> EC = | |||
383 | searchOrDefine(OriginalDefinedDefaultHref, DominantNode); | |||
384 | if (!EC) { | |||
385 | return EC.takeError(); | |||
386 | } | |||
387 | xmlNsPtr PrefixDominantDefinedDefault = std::move(EC.get()); | |||
388 | explicateNamespace(PrefixDominantDefinedDefault, DominantNode); | |||
389 | } | |||
390 | // In this case the node with a higher priority namespace did not have a | |||
391 | // default namespace definition, but the lower priority node did. In this | |||
392 | // case the new default namespace definition is copied. A side effect of | |||
393 | // this is that all children will suddenly find themselves in a different | |||
394 | // default namespace. To maintain correctness we need to ensure that all | |||
395 | // children now explicitly refer to the namespace that they had previously | |||
396 | // implicitly inherited. | |||
397 | } else if (getNamespaceWithPrefix(nullptr, NonDominantNode)) { | |||
398 | if (DominantNode->parent) { | |||
399 | xmlNsPtr ClosestDefault = getClosestDefault(DominantNode->parent); | |||
400 | Expected<xmlNsPtr> EC = | |||
401 | searchOrDefine(ClosestDefault->href, DominantNode); | |||
402 | if (!EC) { | |||
403 | return EC.takeError(); | |||
404 | } | |||
405 | xmlNsPtr ExplicitDefault = std::move(EC.get()); | |||
406 | explicateNamespace(ExplicitDefault, DominantNode); | |||
407 | } | |||
408 | } | |||
409 | } else { | |||
410 | // Covers case where the incoming node has a default namespace definition | |||
411 | // that overrides the original node's namespace. This always leads to | |||
412 | // the original node receiving that new default namespace. | |||
413 | if (hasDefinedDefaultNamespace(DominantNode)) { | |||
414 | NonDominantNode->ns = getNamespaceWithPrefix(nullptr, NonDominantNode); | |||
415 | } else { | |||
416 | // This covers the case where the incoming node either has a prefix | |||
417 | // namespace, or an inherited default namespace. Since the namespace | |||
418 | // may not yet be defined in the original tree we do a searchOrDefine | |||
419 | // for it, and then set the namespace equal to it. | |||
420 | Expected<xmlNsPtr> EC = | |||
421 | searchOrDefine(DominantNode->ns->href, NonDominantNode); | |||
422 | if (!EC) { | |||
423 | return EC.takeError(); | |||
424 | } | |||
425 | xmlNsPtr Explicit = std::move(EC.get()); | |||
426 | NonDominantNode->ns = Explicit; | |||
427 | } | |||
428 | // This covers cases where the incoming dominant node HAS a default | |||
429 | // namespace definition, but MIGHT NOT NECESSARILY be in that namespace. | |||
430 | if (xmlNsPtr DominantDefaultDefined = | |||
431 | getNamespaceWithPrefix(nullptr, DominantNode)) { | |||
432 | if (OriginalDefinedDefaultHref) { | |||
433 | if (namespaceOverrides(DominantDefaultDefined->href, | |||
434 | OriginalDefinedDefaultHref)) { | |||
435 | // In this case, the incoming node's default definition overrides | |||
436 | // the original default definition, all children who relied on that | |||
437 | // definition must be updated accordingly. | |||
438 | Expected<xmlNsPtr> EC = | |||
439 | searchOrDefine(OriginalDefinedDefaultHref, NonDominantNode); | |||
440 | if (!EC) { | |||
441 | return EC.takeError(); | |||
442 | } | |||
443 | xmlNsPtr ExplicitDefault = std::move(EC.get()); | |||
444 | explicateNamespace(ExplicitDefault, NonDominantNode); | |||
445 | } | |||
446 | } else { | |||
447 | // The original did not define a default definition, however the new | |||
448 | // default definition still applies to all children, so they must be | |||
449 | // updated to explicitly refer to the namespace they had previously | |||
450 | // been inheriting implicitly. | |||
451 | xmlNsPtr ClosestDefault = getClosestDefault(NonDominantNode); | |||
452 | Expected<xmlNsPtr> EC = | |||
453 | searchOrDefine(ClosestDefault->href, NonDominantNode); | |||
454 | if (!EC) { | |||
455 | return EC.takeError(); | |||
456 | } | |||
457 | xmlNsPtr ExplicitDefault = std::move(EC.get()); | |||
458 | explicateNamespace(ExplicitDefault, NonDominantNode); | |||
459 | } | |||
460 | } | |||
461 | } | |||
462 | if (NewDefinedDefaultHref) { | |||
463 | xmlNsPtr OriginalNsDef = getNamespaceWithPrefix(nullptr, OriginalNode); | |||
464 | xmlFree(const_cast<unsigned char *>(OriginalNsDef->href)); | |||
465 | OriginalNsDef->href = NewDefinedDefaultHref; | |||
466 | } | |||
467 | xmlFree(const_cast<unsigned char *>(OriginalDefinedDefaultHref)); | |||
468 | return Error::success(); | |||
469 | } | |||
470 | ||||
471 | static bool isRecognizedNamespace(const unsigned char *NsHref) { | |||
472 | for (auto &Ns : MtNsHrefsPrefixes) { | |||
473 | if (xmlStringsEqual(NsHref, TO_XML_CHAR(Ns.first.data())reinterpret_cast<const unsigned char *>(Ns.first.data() ))) { | |||
474 | return true; | |||
475 | } | |||
476 | } | |||
477 | return false; | |||
478 | } | |||
479 | ||||
480 | static bool hasRecognizedNamespace(xmlNodePtr Node) { | |||
481 | return isRecognizedNamespace(Node->ns->href); | |||
482 | } | |||
483 | ||||
484 | // Ensure a node's inherited namespace is actually defined in the tree it | |||
485 | // resides in. | |||
486 | static Error reconcileNamespaces(xmlNodePtr Node) { | |||
487 | if (!Node) { | |||
488 | return Error::success(); | |||
489 | } | |||
490 | if (hasInheritedNs(Node)) { | |||
491 | Expected<xmlNsPtr> ExplicitOrError = searchOrDefine(Node->ns->href, Node); | |||
492 | if (!ExplicitOrError) { | |||
493 | return ExplicitOrError.takeError(); | |||
494 | } | |||
495 | xmlNsPtr Explicit = std::move(ExplicitOrError.get()); | |||
496 | Node->ns = Explicit; | |||
497 | } | |||
498 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { | |||
499 | if (auto E = reconcileNamespaces(Child)) { | |||
500 | return E; | |||
501 | } | |||
502 | } | |||
503 | return Error::success(); | |||
504 | } | |||
505 | ||||
506 | // Recursively merge the two given manifest trees, depending on which elements | |||
507 | // are of a mergeable type, and choose namespaces according to which have | |||
508 | // higher priority. | |||
509 | static Error treeMerge(xmlNodePtr OriginalRoot, xmlNodePtr AdditionalRoot) { | |||
510 | if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot)) | |||
511 | return E; | |||
512 | if (auto E = mergeNamespaces(OriginalRoot, AdditionalRoot)) | |||
513 | return E; | |||
514 | xmlNodePtr AdditionalFirstChild = AdditionalRoot->children; | |||
515 | xmlNode StoreNext; | |||
516 | for (xmlNodePtr Child = AdditionalFirstChild; Child; Child = Child->next) { | |||
517 | xmlNodePtr OriginalChildWithName; | |||
518 | if (!isMergeableElement(Child->name) || | |||
519 | !(OriginalChildWithName = | |||
520 | getChildWithName(OriginalRoot, Child->name)) || | |||
521 | !hasRecognizedNamespace(Child)) { | |||
522 | StoreNext.next = Child->next; | |||
523 | xmlUnlinkNode(Child); | |||
524 | if (!xmlAddChild(OriginalRoot, Child)) { | |||
525 | return make_error<WindowsManifestError>(Twine("could not merge ") + | |||
526 | FROM_XML_CHAR(Child->name)reinterpret_cast<const char *>(Child->name)); | |||
527 | } | |||
528 | if (auto E = reconcileNamespaces(Child)) { | |||
529 | return E; | |||
530 | } | |||
531 | Child = &StoreNext; | |||
532 | } else if (auto E = treeMerge(OriginalChildWithName, Child)) { | |||
533 | return E; | |||
534 | } | |||
535 | } | |||
536 | return Error::success(); | |||
537 | } | |||
538 | ||||
539 | static void stripComments(xmlNodePtr Root) { | |||
540 | xmlNode StoreNext; | |||
541 | for (xmlNodePtr Child = Root->children; Child; Child = Child->next) { | |||
542 | if (!xmlStringsEqual(Child->name, TO_XML_CHAR("comment")reinterpret_cast<const unsigned char *>("comment"))) { | |||
543 | stripComments(Child); | |||
544 | continue; | |||
545 | } | |||
546 | StoreNext.next = Child->next; | |||
547 | xmlNodePtr Remove = Child; | |||
548 | Child = &StoreNext; | |||
549 | xmlUnlinkNode(Remove); | |||
550 | xmlFreeNode(Remove); | |||
551 | } | |||
552 | } | |||
553 | ||||
554 | // libxml2 assumes that attributes do not inherit default namespaces, whereas | |||
555 | // the original mt.exe does make this assumption. This function reconciles | |||
556 | // this by setting all attributes to have the inherited default namespace. | |||
557 | static void setAttributeNamespaces(xmlNodePtr Node) { | |||
558 | for (xmlAttrPtr Attribute = Node->properties; Attribute; | |||
559 | Attribute = Attribute->next) { | |||
560 | if (!Attribute->ns) { | |||
561 | Attribute->ns = getClosestDefault(Node); | |||
562 | } | |||
563 | } | |||
564 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { | |||
565 | setAttributeNamespaces(Child); | |||
566 | } | |||
567 | } | |||
568 | ||||
569 | // The merging process may create too many prefix defined namespaces. This | |||
570 | // function removes all unnecessary ones from the tree. | |||
571 | static void checkAndStripPrefixes(xmlNodePtr Node, | |||
572 | std::vector<xmlNsPtr> &RequiredPrefixes) { | |||
573 | for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { | |||
574 | checkAndStripPrefixes(Child, RequiredPrefixes); | |||
575 | } | |||
576 | if (Node->ns && Node->ns->prefix != nullptr) { | |||
577 | xmlNsPtr ClosestDefault = getClosestDefault(Node); | |||
578 | if (ClosestDefault && | |||
579 | xmlStringsEqual(ClosestDefault->href, Node->ns->href)) { | |||
580 | Node->ns = ClosestDefault; | |||
581 | } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) { | |||
582 | RequiredPrefixes.push_back(Node->ns); | |||
583 | } | |||
584 | } | |||
585 | for (xmlAttrPtr Attribute = Node->properties; Attribute; | |||
586 | Attribute = Attribute->next) { | |||
587 | if (Attribute->ns && Attribute->ns->prefix != nullptr) { | |||
588 | xmlNsPtr ClosestDefault = getClosestDefault(Node); | |||
589 | if (ClosestDefault && | |||
590 | xmlStringsEqual(ClosestDefault->href, Attribute->ns->href)) { | |||
591 | Attribute->ns = ClosestDefault; | |||
592 | } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) { | |||
593 | RequiredPrefixes.push_back(Attribute->ns); | |||
594 | } | |||
595 | } | |||
596 | } | |||
597 | xmlNsPtr Prev; | |||
598 | xmlNs Temp; | |||
599 | for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { | |||
600 | if (!Def->prefix || llvm::is_contained(RequiredPrefixes, Def)) { | |||
601 | Prev = Def; | |||
602 | continue; | |||
603 | } | |||
604 | if (Def == Node->nsDef) { | |||
605 | Node->nsDef = Def->next; | |||
606 | } else { | |||
607 | Prev->next = Def->next; | |||
| ||||
608 | } | |||
609 | Temp.next = Def->next; | |||
610 | xmlFreeNs(Def); | |||
611 | Def = &Temp; | |||
612 | } | |||
613 | } | |||
614 | ||||
615 | WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() { | |||
616 | for (auto &Doc : MergedDocs) | |||
617 | xmlFreeDoc(Doc); | |||
618 | } | |||
619 | ||||
620 | Error WindowsManifestMerger::WindowsManifestMergerImpl::merge( | |||
621 | MemoryBufferRef Manifest) { | |||
622 | if (Merged) | |||
623 | return make_error<WindowsManifestError>( | |||
624 | "merge after getMergedManifest is not supported"); | |||
625 | if (Manifest.getBufferSize() == 0) | |||
626 | return make_error<WindowsManifestError>( | |||
627 | "attempted to merge empty manifest"); | |||
628 | xmlSetGenericErrorFunc((void *)this, | |||
629 | WindowsManifestMergerImpl::errorCallback); | |||
630 | xmlDocPtr ManifestXML = xmlReadMemory( | |||
631 | Manifest.getBufferStart(), Manifest.getBufferSize(), "manifest.xml", | |||
632 | nullptr, XML_PARSE_NOBLANKS | XML_PARSE_NODICT); | |||
633 | xmlSetGenericErrorFunc(nullptr, nullptr); | |||
634 | if (auto E = getParseError()) | |||
635 | return E; | |||
636 | xmlNodePtr AdditionalRoot = xmlDocGetRootElement(ManifestXML); | |||
637 | stripComments(AdditionalRoot); | |||
638 | setAttributeNamespaces(AdditionalRoot); | |||
639 | if (CombinedDoc == nullptr) { | |||
640 | CombinedDoc = ManifestXML; | |||
641 | } else { | |||
642 | xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc); | |||
643 | if (!xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) || | |||
644 | !isMergeableElement(AdditionalRoot->name) || | |||
645 | !hasRecognizedNamespace(AdditionalRoot)) { | |||
646 | return make_error<WindowsManifestError>("multiple root nodes"); | |||
647 | } | |||
648 | if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) { | |||
649 | return E; | |||
650 | } | |||
651 | } | |||
652 | MergedDocs.push_back(ManifestXML); | |||
653 | return Error::success(); | |||
654 | } | |||
655 | ||||
656 | std::unique_ptr<MemoryBuffer> | |||
657 | WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { | |||
658 | if (!Merged) { | |||
659 | Merged = true; | |||
660 | ||||
661 | if (!CombinedDoc) | |||
662 | return nullptr; | |||
663 | ||||
664 | xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc); | |||
665 | std::vector<xmlNsPtr> RequiredPrefixes; | |||
666 | checkAndStripPrefixes(CombinedRoot, RequiredPrefixes); | |||
667 | std::unique_ptr<xmlDoc, XmlDeleter> OutputDoc( | |||
668 | xmlNewDoc((const unsigned char *)"1.0")); | |||
669 | xmlDocSetRootElement(OutputDoc.get(), CombinedRoot); | |||
670 | assert(nullptr == xmlDocGetRootElement(CombinedDoc))(static_cast <bool> (nullptr == xmlDocGetRootElement(CombinedDoc )) ? void (0) : __assert_fail ("nullptr == xmlDocGetRootElement(CombinedDoc)" , "llvm/lib/WindowsManifest/WindowsManifestMerger.cpp", 670, __extension__ __PRETTY_FUNCTION__)); | |||
671 | ||||
672 | xmlKeepBlanksDefault(0); | |||
673 | xmlChar *Buff = nullptr; | |||
674 | xmlDocDumpFormatMemoryEnc(OutputDoc.get(), &Buff, &BufferSize, "UTF-8", 1); | |||
675 | Buffer.reset(Buff); | |||
676 | } | |||
677 | ||||
678 | return BufferSize ? MemoryBuffer::getMemBufferCopy(StringRef( | |||
679 | FROM_XML_CHAR(Buffer.get())reinterpret_cast<const char *>(Buffer.get()), (size_t)BufferSize)) | |||
680 | : nullptr; | |||
681 | } | |||
682 | ||||
683 | bool windows_manifest::isAvailable() { return true; } | |||
684 | ||||
685 | #else | |||
686 | ||||
687 | WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() { | |||
688 | } | |||
689 | ||||
690 | Error WindowsManifestMerger::WindowsManifestMergerImpl::merge( | |||
691 | MemoryBufferRef Manifest) { | |||
692 | return make_error<WindowsManifestError>("no libxml2"); | |||
693 | } | |||
694 | ||||
695 | std::unique_ptr<MemoryBuffer> | |||
696 | WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { | |||
697 | return nullptr; | |||
698 | } | |||
699 | ||||
700 | bool windows_manifest::isAvailable() { return false; } | |||
701 | ||||
702 | #endif | |||
703 | ||||
704 | WindowsManifestMerger::WindowsManifestMerger() | |||
705 | : Impl(std::make_unique<WindowsManifestMergerImpl>()) {} | |||
706 | ||||
707 | WindowsManifestMerger::~WindowsManifestMerger() = default; | |||
708 | ||||
709 | Error WindowsManifestMerger::merge(MemoryBufferRef Manifest) { | |||
710 | return Impl->merge(Manifest); | |||
711 | } | |||
712 | ||||
713 | std::unique_ptr<MemoryBuffer> WindowsManifestMerger::getMergedManifest() { | |||
714 | return Impl->getMergedManifest(); | |||
| ||||
715 | } | |||
716 | ||||
717 | void WindowsManifestMerger::WindowsManifestMergerImpl::errorCallback( | |||
718 | void *Ctx, const char *Format, ...) { | |||
719 | auto *Merger = (WindowsManifestMergerImpl *)Ctx; | |||
720 | Merger->ParseErrorOccurred = true; | |||
721 | } | |||
722 | ||||
723 | Error WindowsManifestMerger::WindowsManifestMergerImpl::getParseError() { | |||
724 | if (!ParseErrorOccurred) | |||
725 | return Error::success(); | |||
726 | return make_error<WindowsManifestError>("invalid xml document"); | |||
727 | } |