728 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			728 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- WindowsManifestMerger.cpp ------------------------------*- C++ -*-===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===---------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// This file implements the .manifest merger class.
 | 
						|
//
 | 
						|
//===---------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "llvm/WindowsManifest/WindowsManifestMerger.h"
 | 
						|
#include "llvm/Config/config.h"
 | 
						|
#include "llvm/Support/MemoryBuffer.h"
 | 
						|
 | 
						|
#if LLVM_ENABLE_LIBXML2
 | 
						|
#include <libxml/xmlreader.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#define TO_XML_CHAR(X) reinterpret_cast<const unsigned char *>(X)
 | 
						|
#define FROM_XML_CHAR(X) reinterpret_cast<const char *>(X)
 | 
						|
 | 
						|
using namespace llvm;
 | 
						|
using namespace windows_manifest;
 | 
						|
 | 
						|
char WindowsManifestError::ID = 0;
 | 
						|
 | 
						|
WindowsManifestError::WindowsManifestError(const Twine &Msg) : Msg(Msg.str()) {}
 | 
						|
 | 
						|
void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; }
 | 
						|
 | 
						|
class WindowsManifestMerger::WindowsManifestMergerImpl {
 | 
						|
public:
 | 
						|
  ~WindowsManifestMergerImpl();
 | 
						|
  Error merge(MemoryBufferRef Manifest);
 | 
						|
  std::unique_ptr<MemoryBuffer> getMergedManifest();
 | 
						|
 | 
						|
private:
 | 
						|
  static void errorCallback(void *Ctx, const char *Format, ...);
 | 
						|
  Error getParseError();
 | 
						|
#if LLVM_ENABLE_LIBXML2
 | 
						|
  xmlDocPtr CombinedDoc = nullptr;
 | 
						|
  std::vector<xmlDocPtr> MergedDocs;
 | 
						|
 | 
						|
  bool Merged = false;
 | 
						|
  struct XmlDeleter {
 | 
						|
    void operator()(xmlChar *Ptr) { xmlFree(Ptr); }
 | 
						|
    void operator()(xmlDoc *Ptr) { xmlFreeDoc(Ptr); }
 | 
						|
  };
 | 
						|
  int BufferSize = 0;
 | 
						|
  std::unique_ptr<xmlChar, XmlDeleter> Buffer;
 | 
						|
#endif
 | 
						|
  bool ParseErrorOccurred = false;
 | 
						|
};
 | 
						|
 | 
						|
#if LLVM_ENABLE_LIBXML2
 | 
						|
 | 
						|
static constexpr std::pair<StringLiteral, StringLiteral> MtNsHrefsPrefixes[] = {
 | 
						|
    {"urn:schemas-microsoft-com:asm.v1", "ms_asmv1"},
 | 
						|
    {"urn:schemas-microsoft-com:asm.v2", "ms_asmv2"},
 | 
						|
    {"urn:schemas-microsoft-com:asm.v3", "ms_asmv3"},
 | 
						|
    {"http://schemas.microsoft.com/SMI/2005/WindowsSettings",
 | 
						|
     "ms_windowsSettings"},
 | 
						|
    {"urn:schemas-microsoft-com:compatibility.v1", "ms_compatibilityv1"}};
 | 
						|
 | 
						|
static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) {
 | 
						|
  // Handle null pointers.  Comparison of 2 null pointers returns true because
 | 
						|
  // this indicates the prefix of a default namespace.
 | 
						|
  if (!A || !B)
 | 
						|
    return A == B;
 | 
						|
  return strcmp(FROM_XML_CHAR(A), FROM_XML_CHAR(B)) == 0;
 | 
						|
}
 | 
						|
 | 
						|
static bool isMergeableElement(const unsigned char *ElementName) {
 | 
						|
  for (StringRef S : {"application", "assembly", "assemblyIdentity",
 | 
						|
                      "compatibility", "noInherit", "requestedExecutionLevel",
 | 
						|
                      "requestedPrivileges", "security", "trustInfo"}) {
 | 
						|
    if (S == FROM_XML_CHAR(ElementName)) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static xmlNodePtr getChildWithName(xmlNodePtr Parent,
 | 
						|
                                   const unsigned char *ElementName) {
 | 
						|
  for (xmlNodePtr Child = Parent->children; Child; Child = Child->next) {
 | 
						|
    if (xmlStringsEqual(Child->name, ElementName)) {
 | 
						|
      return Child;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
static xmlAttrPtr getAttribute(xmlNodePtr Node,
 | 
						|
                               const unsigned char *AttributeName) {
 | 
						|
  for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr;
 | 
						|
       Attribute = Attribute->next) {
 | 
						|
    if (xmlStringsEqual(Attribute->name, AttributeName)) {
 | 
						|
      return Attribute;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
// Check if namespace specified by HRef1 overrides that of HRef2.
 | 
						|
static bool namespaceOverrides(const unsigned char *HRef1,
 | 
						|
                               const unsigned char *HRef2) {
 | 
						|
  auto HRef1Position = llvm::find_if(
 | 
						|
      MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) {
 | 
						|
        return xmlStringsEqual(HRef1, TO_XML_CHAR(Element.first.data()));
 | 
						|
      });
 | 
						|
  auto HRef2Position = llvm::find_if(
 | 
						|
      MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) {
 | 
						|
        return xmlStringsEqual(HRef2, TO_XML_CHAR(Element.first.data()));
 | 
						|
      });
 | 
						|
  return HRef1Position < HRef2Position;
 | 
						|
}
 | 
						|
 | 
						|
// Search for prefix-defined namespace specified by HRef, starting on Node and
 | 
						|
// continuing recursively upwards.  Returns the namespace or nullptr if not
 | 
						|
// found.
 | 
						|
static xmlNsPtr search(const unsigned char *HRef, xmlNodePtr Node) {
 | 
						|
  for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) {
 | 
						|
    if (Def->prefix && xmlStringsEqual(Def->href, HRef)) {
 | 
						|
      return Def;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (Node->parent) {
 | 
						|
    return search(HRef, Node->parent);
 | 
						|
  }
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
// Return the prefix that corresponds to the HRef.  If HRef is not a recognized
 | 
						|
// URI, then just return the HRef itself to use as the prefix.
 | 
						|
static const unsigned char *getPrefixForHref(const unsigned char *HRef) {
 | 
						|
  for (auto &Ns : MtNsHrefsPrefixes) {
 | 
						|
    if (xmlStringsEqual(HRef, TO_XML_CHAR(Ns.first.data()))) {
 | 
						|
      return TO_XML_CHAR(Ns.second.data());
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return HRef;
 | 
						|
}
 | 
						|
 | 
						|
// Search for prefix-defined namespace specified by HRef, starting on Node and
 | 
						|
// continuing recursively upwards.  If it is found, then return it.  If it is
 | 
						|
// not found, then prefix-define that namespace on the node and return a
 | 
						|
// reference to it.
 | 
						|
static Expected<xmlNsPtr> searchOrDefine(const unsigned char *HRef,
 | 
						|
                                         xmlNodePtr Node) {
 | 
						|
  if (xmlNsPtr Def = search(HRef, Node))
 | 
						|
    return Def;
 | 
						|
  if (xmlNsPtr Def = xmlNewNs(Node, HRef, getPrefixForHref(HRef)))
 | 
						|
    return Def;
 | 
						|
  return make_error<WindowsManifestError>("failed to create new namespace");
 | 
						|
}
 | 
						|
 | 
						|
// Set the namespace of OrigionalAttribute on OriginalNode to be that of
 | 
						|
// AdditionalAttribute's.
 | 
						|
static Error copyAttributeNamespace(xmlAttrPtr OriginalAttribute,
 | 
						|
                                    xmlNodePtr OriginalNode,
 | 
						|
                                    xmlAttrPtr AdditionalAttribute) {
 | 
						|
 | 
						|
  Expected<xmlNsPtr> ExplicitOrError =
 | 
						|
      searchOrDefine(AdditionalAttribute->ns->href, OriginalNode);
 | 
						|
  if (!ExplicitOrError)
 | 
						|
    return ExplicitOrError.takeError();
 | 
						|
  OriginalAttribute->ns = std::move(ExplicitOrError.get());
 | 
						|
  return Error::success();
 | 
						|
}
 | 
						|
 | 
						|
// Return the corresponding namespace definition for the prefix, defined on the
 | 
						|
// given Node.  Returns nullptr if there is no such definition.
 | 
						|
static xmlNsPtr getNamespaceWithPrefix(const unsigned char *Prefix,
 | 
						|
                                       xmlNodePtr Node) {
 | 
						|
  if (Node == nullptr)
 | 
						|
    return nullptr;
 | 
						|
  for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) {
 | 
						|
    if (xmlStringsEqual(Def->prefix, Prefix)) {
 | 
						|
      return Def;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
// Search for the closest inheritable default namespace, starting on (and
 | 
						|
// including) the Node and traveling upwards through parent nodes.  Returns
 | 
						|
// nullptr if there are no inheritable default namespaces.
 | 
						|
static xmlNsPtr getClosestDefault(xmlNodePtr Node) {
 | 
						|
  if (xmlNsPtr Ret = getNamespaceWithPrefix(nullptr, Node))
 | 
						|
    return Ret;
 | 
						|
  if (Node->parent == nullptr)
 | 
						|
    return nullptr;
 | 
						|
  return getClosestDefault(Node->parent);
 | 
						|
}
 | 
						|
 | 
						|
// Merge the attributes of AdditionalNode into OriginalNode.  If attributes
 | 
						|
// with identical types are present, they are not duplicated but rather if
 | 
						|
// their values are not consistent and error is thrown.  In addition, the
 | 
						|
// higher priority namespace is used for each attribute, EXCEPT in the case
 | 
						|
// of merging two default namespaces and the lower priority namespace
 | 
						|
// definition occurs closer than the higher priority one.
 | 
						|
static Error mergeAttributes(xmlNodePtr OriginalNode,
 | 
						|
                             xmlNodePtr AdditionalNode) {
 | 
						|
  xmlNsPtr ClosestDefault = getClosestDefault(OriginalNode);
 | 
						|
  for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute;
 | 
						|
       Attribute = Attribute->next) {
 | 
						|
    if (xmlAttrPtr OriginalAttribute =
 | 
						|
            getAttribute(OriginalNode, Attribute->name)) {
 | 
						|
      if (!xmlStringsEqual(OriginalAttribute->children->content,
 | 
						|
                           Attribute->children->content)) {
 | 
						|
        return make_error<WindowsManifestError>(
 | 
						|
            Twine("conflicting attributes for ") +
 | 
						|
            FROM_XML_CHAR(OriginalNode->name));
 | 
						|
      }
 | 
						|
      if (!Attribute->ns) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      if (!OriginalAttribute->ns) {
 | 
						|
        if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode,
 | 
						|
                                            Attribute)) {
 | 
						|
          return E;
 | 
						|
        }
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      if (namespaceOverrides(OriginalAttribute->ns->href,
 | 
						|
                             Attribute->ns->href)) {
 | 
						|
        // In this case, the original attribute has a higher priority namespace
 | 
						|
        // than the incomiing attribute, however the namespace definition of
 | 
						|
        // the lower priority namespace occurs first traveling upwards in the
 | 
						|
        // tree.  Therefore the lower priority namespace is applied.
 | 
						|
        if (!OriginalAttribute->ns->prefix && !Attribute->ns->prefix &&
 | 
						|
            ClosestDefault &&
 | 
						|
            xmlStringsEqual(Attribute->ns->href, ClosestDefault->href)) {
 | 
						|
          if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode,
 | 
						|
                                              Attribute)) {
 | 
						|
            return E;
 | 
						|
          }
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
        continue;
 | 
						|
        // This covers the case where the incoming attribute has the higher
 | 
						|
        // priority.  The higher priority namespace is applied in all cases
 | 
						|
        // EXCEPT when both of the namespaces are default inherited, and the
 | 
						|
        // closest inherited default is the lower priority one.
 | 
						|
      }
 | 
						|
      if (Attribute->ns->prefix || OriginalAttribute->ns->prefix ||
 | 
						|
          (ClosestDefault && !xmlStringsEqual(OriginalAttribute->ns->href,
 | 
						|
                                              ClosestDefault->href))) {
 | 
						|
        if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode,
 | 
						|
                                            Attribute)) {
 | 
						|
          return E;
 | 
						|
        }
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    // If the incoming attribute is not already found on the node, append it
 | 
						|
    // to the end of the properties list.  Also explicitly apply its
 | 
						|
    // namespace as a prefix because it might be contained in a separate
 | 
						|
    // namespace that doesn't use the attribute.
 | 
						|
    xmlAttrPtr NewProp =
 | 
						|
        xmlNewProp(OriginalNode, Attribute->name, Attribute->children->content);
 | 
						|
    Expected<xmlNsPtr> ExplicitOrError =
 | 
						|
        searchOrDefine(Attribute->ns->href, OriginalNode);
 | 
						|
    if (!ExplicitOrError)
 | 
						|
      return ExplicitOrError.takeError();
 | 
						|
    NewProp->ns = std::move(ExplicitOrError.get());
 | 
						|
  }
 | 
						|
  return Error::success();
 | 
						|
}
 | 
						|
 | 
						|
// Given two nodes, return the one with the higher priority namespace.
 | 
						|
static xmlNodePtr getDominantNode(xmlNodePtr Node1, xmlNodePtr Node2) {
 | 
						|
 | 
						|
  if (!Node1 || !Node1->ns)
 | 
						|
    return Node2;
 | 
						|
  if (!Node2 || !Node2->ns)
 | 
						|
    return Node1;
 | 
						|
  if (namespaceOverrides(Node1->ns->href, Node2->ns->href))
 | 
						|
    return Node1;
 | 
						|
  return Node2;
 | 
						|
}
 | 
						|
 | 
						|
// Checks if this Node's namespace is inherited or one it defined itself.
 | 
						|
static bool hasInheritedNs(xmlNodePtr Node) {
 | 
						|
  return Node->ns && Node->ns != getNamespaceWithPrefix(Node->ns->prefix, Node);
 | 
						|
}
 | 
						|
 | 
						|
// Check if this Node's namespace is a default namespace that it inherited, as
 | 
						|
// opposed to defining itself.
 | 
						|
static bool hasInheritedDefaultNs(xmlNodePtr Node) {
 | 
						|
  return hasInheritedNs(Node) && Node->ns->prefix == nullptr;
 | 
						|
}
 | 
						|
 | 
						|
// Check if this Node's namespace is a default namespace it defined itself.
 | 
						|
static bool hasDefinedDefaultNamespace(xmlNodePtr Node) {
 | 
						|
  return Node->ns && (Node->ns == getNamespaceWithPrefix(nullptr, Node));
 | 
						|
}
 | 
						|
 | 
						|
// For the given explicit prefix-definition of a namespace, travel downwards
 | 
						|
// from a node recursively, and for every implicit, inherited default usage of
 | 
						|
// that namespace replace it with that explicit prefix use.  This is important
 | 
						|
// when namespace overriding occurs when merging, so that elements unique to a
 | 
						|
// namespace will still stay in that namespace.
 | 
						|
static void explicateNamespace(xmlNsPtr PrefixDef, xmlNodePtr Node) {
 | 
						|
  // If a node as its own default namespace definition it clearly cannot have
 | 
						|
  // inherited the given default namespace, and neither will any of its
 | 
						|
  // children.
 | 
						|
  if (hasDefinedDefaultNamespace(Node))
 | 
						|
    return;
 | 
						|
  if (Node->ns && xmlStringsEqual(Node->ns->href, PrefixDef->href) &&
 | 
						|
      hasInheritedDefaultNs(Node))
 | 
						|
    Node->ns = PrefixDef;
 | 
						|
  for (xmlAttrPtr Attribute = Node->properties; Attribute;
 | 
						|
       Attribute = Attribute->next) {
 | 
						|
    if (Attribute->ns &&
 | 
						|
        xmlStringsEqual(Attribute->ns->href, PrefixDef->href)) {
 | 
						|
      Attribute->ns = PrefixDef;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {
 | 
						|
    explicateNamespace(PrefixDef, Child);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// Perform the namespace merge between two nodes.
 | 
						|
static Error mergeNamespaces(xmlNodePtr OriginalNode,
 | 
						|
                             xmlNodePtr AdditionalNode) {
 | 
						|
  // Save the original default namespace definition in case the incoming node
 | 
						|
  // overrides it.
 | 
						|
  const unsigned char *OriginalDefinedDefaultHref = nullptr;
 | 
						|
  if (xmlNsPtr OriginalDefinedDefaultNs =
 | 
						|
          getNamespaceWithPrefix(nullptr, OriginalNode)) {
 | 
						|
    OriginalDefinedDefaultHref = xmlStrdup(OriginalDefinedDefaultNs->href);
 | 
						|
  }
 | 
						|
  const unsigned char *NewDefinedDefaultHref = nullptr;
 | 
						|
  // Copy all namespace definitions.  There can only be one default namespace
 | 
						|
  // definition per node, so the higher priority one takes precedence in the
 | 
						|
  // case of collision.
 | 
						|
  for (xmlNsPtr Def = AdditionalNode->nsDef; Def; Def = Def->next) {
 | 
						|
    if (xmlNsPtr OriginalNsDef =
 | 
						|
            getNamespaceWithPrefix(Def->prefix, OriginalNode)) {
 | 
						|
      if (!Def->prefix) {
 | 
						|
        if (namespaceOverrides(Def->href, OriginalNsDef->href)) {
 | 
						|
          NewDefinedDefaultHref = TO_XML_CHAR(strdup(FROM_XML_CHAR(Def->href)));
 | 
						|
        }
 | 
						|
      } else if (!xmlStringsEqual(OriginalNsDef->href, Def->href)) {
 | 
						|
        return make_error<WindowsManifestError>(
 | 
						|
            Twine("conflicting namespace definitions for ") +
 | 
						|
            FROM_XML_CHAR(Def->prefix));
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      xmlNsPtr NewDef = xmlCopyNamespace(Def);
 | 
						|
      NewDef->next = OriginalNode->nsDef;
 | 
						|
      OriginalNode->nsDef = NewDef;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Check whether the original node or the incoming node has the higher
 | 
						|
  // priority namespace.  Depending on which one is dominant, we will have
 | 
						|
  // to recursively apply namespace changes down to children of the original
 | 
						|
  // node.
 | 
						|
  xmlNodePtr DominantNode = getDominantNode(OriginalNode, AdditionalNode);
 | 
						|
  xmlNodePtr NonDominantNode =
 | 
						|
      DominantNode == OriginalNode ? AdditionalNode : OriginalNode;
 | 
						|
  if (DominantNode == OriginalNode) {
 | 
						|
    if (OriginalDefinedDefaultHref) {
 | 
						|
      xmlNsPtr NonDominantDefinedDefault =
 | 
						|
          getNamespaceWithPrefix(nullptr, NonDominantNode);
 | 
						|
      // In this case, both the nodes defined a default namespace.  However
 | 
						|
      // the lower priority node ended up having a higher priority default
 | 
						|
      // definition.  This can occur if the higher priority node is prefix
 | 
						|
      // namespace defined.  In this case we have to define an explicit
 | 
						|
      // prefix for the overridden definition and apply it to all children
 | 
						|
      // who relied on that definition.
 | 
						|
      if (NonDominantDefinedDefault &&
 | 
						|
          namespaceOverrides(NonDominantDefinedDefault->href,
 | 
						|
                             OriginalDefinedDefaultHref)) {
 | 
						|
        Expected<xmlNsPtr> EC =
 | 
						|
            searchOrDefine(OriginalDefinedDefaultHref, DominantNode);
 | 
						|
        if (!EC) {
 | 
						|
          return EC.takeError();
 | 
						|
        }
 | 
						|
        xmlNsPtr PrefixDominantDefinedDefault = std::move(EC.get());
 | 
						|
        explicateNamespace(PrefixDominantDefinedDefault, DominantNode);
 | 
						|
      }
 | 
						|
      // In this case the node with a higher priority namespace did not have a
 | 
						|
      // default namespace definition, but the lower priority node did.  In this
 | 
						|
      // case the new default namespace definition is copied.  A side effect of
 | 
						|
      // this is that all children will suddenly find themselves in a different
 | 
						|
      // default namespace.  To maintain correctness we need to ensure that all
 | 
						|
      // children now explicitly refer to the namespace that they had previously
 | 
						|
      // implicitly inherited.
 | 
						|
    } else if (getNamespaceWithPrefix(nullptr, NonDominantNode)) {
 | 
						|
      if (DominantNode->parent) {
 | 
						|
        xmlNsPtr ClosestDefault = getClosestDefault(DominantNode->parent);
 | 
						|
        Expected<xmlNsPtr> EC =
 | 
						|
            searchOrDefine(ClosestDefault->href, DominantNode);
 | 
						|
        if (!EC) {
 | 
						|
          return EC.takeError();
 | 
						|
        }
 | 
						|
        xmlNsPtr ExplicitDefault = std::move(EC.get());
 | 
						|
        explicateNamespace(ExplicitDefault, DominantNode);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    // Covers case where the incoming node has a default namespace definition
 | 
						|
    // that overrides the original node's namespace.  This always leads to
 | 
						|
    // the original node receiving that new default namespace.
 | 
						|
    if (hasDefinedDefaultNamespace(DominantNode)) {
 | 
						|
      NonDominantNode->ns = getNamespaceWithPrefix(nullptr, NonDominantNode);
 | 
						|
    } else {
 | 
						|
      // This covers the case where the incoming node either has a prefix
 | 
						|
      // namespace, or an inherited default namespace.  Since the namespace
 | 
						|
      // may not yet be defined in the original tree we do a searchOrDefine
 | 
						|
      // for it, and then set the namespace equal to it.
 | 
						|
      Expected<xmlNsPtr> EC =
 | 
						|
          searchOrDefine(DominantNode->ns->href, NonDominantNode);
 | 
						|
      if (!EC) {
 | 
						|
        return EC.takeError();
 | 
						|
      }
 | 
						|
      xmlNsPtr Explicit = std::move(EC.get());
 | 
						|
      NonDominantNode->ns = Explicit;
 | 
						|
    }
 | 
						|
    // This covers cases where the incoming dominant node HAS a default
 | 
						|
    // namespace definition, but MIGHT NOT NECESSARILY be in that namespace.
 | 
						|
    if (xmlNsPtr DominantDefaultDefined =
 | 
						|
            getNamespaceWithPrefix(nullptr, DominantNode)) {
 | 
						|
      if (OriginalDefinedDefaultHref) {
 | 
						|
        if (namespaceOverrides(DominantDefaultDefined->href,
 | 
						|
                               OriginalDefinedDefaultHref)) {
 | 
						|
          // In this case, the incoming node's default definition overrides
 | 
						|
          // the original default definition, all children who relied on that
 | 
						|
          // definition must be updated accordingly.
 | 
						|
          Expected<xmlNsPtr> EC =
 | 
						|
              searchOrDefine(OriginalDefinedDefaultHref, NonDominantNode);
 | 
						|
          if (!EC) {
 | 
						|
            return EC.takeError();
 | 
						|
          }
 | 
						|
          xmlNsPtr ExplicitDefault = std::move(EC.get());
 | 
						|
          explicateNamespace(ExplicitDefault, NonDominantNode);
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        // The original did not define a default definition, however the new
 | 
						|
        // default definition still applies to all children, so they must be
 | 
						|
        // updated to explicitly refer to the namespace they had previously
 | 
						|
        // been inheriting implicitly.
 | 
						|
        xmlNsPtr ClosestDefault = getClosestDefault(NonDominantNode);
 | 
						|
        Expected<xmlNsPtr> EC =
 | 
						|
            searchOrDefine(ClosestDefault->href, NonDominantNode);
 | 
						|
        if (!EC) {
 | 
						|
          return EC.takeError();
 | 
						|
        }
 | 
						|
        xmlNsPtr ExplicitDefault = std::move(EC.get());
 | 
						|
        explicateNamespace(ExplicitDefault, NonDominantNode);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (NewDefinedDefaultHref) {
 | 
						|
    xmlNsPtr OriginalNsDef = getNamespaceWithPrefix(nullptr, OriginalNode);
 | 
						|
    xmlFree(const_cast<unsigned char *>(OriginalNsDef->href));
 | 
						|
    OriginalNsDef->href = NewDefinedDefaultHref;
 | 
						|
  }
 | 
						|
  xmlFree(const_cast<unsigned char *>(OriginalDefinedDefaultHref));
 | 
						|
  return Error::success();
 | 
						|
}
 | 
						|
 | 
						|
static bool isRecognizedNamespace(const unsigned char *NsHref) {
 | 
						|
  for (auto &Ns : MtNsHrefsPrefixes) {
 | 
						|
    if (xmlStringsEqual(NsHref, TO_XML_CHAR(Ns.first.data()))) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool hasRecognizedNamespace(xmlNodePtr Node) {
 | 
						|
  return isRecognizedNamespace(Node->ns->href);
 | 
						|
}
 | 
						|
 | 
						|
// Ensure a node's inherited namespace is actually defined in the tree it
 | 
						|
// resides in.
 | 
						|
static Error reconcileNamespaces(xmlNodePtr Node) {
 | 
						|
  if (!Node) {
 | 
						|
    return Error::success();
 | 
						|
  }
 | 
						|
  if (hasInheritedNs(Node)) {
 | 
						|
    Expected<xmlNsPtr> ExplicitOrError = searchOrDefine(Node->ns->href, Node);
 | 
						|
    if (!ExplicitOrError) {
 | 
						|
      return ExplicitOrError.takeError();
 | 
						|
    }
 | 
						|
    xmlNsPtr Explicit = std::move(ExplicitOrError.get());
 | 
						|
    Node->ns = Explicit;
 | 
						|
  }
 | 
						|
  for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {
 | 
						|
    if (auto E = reconcileNamespaces(Child)) {
 | 
						|
      return E;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return Error::success();
 | 
						|
}
 | 
						|
 | 
						|
// Recursively merge the two given manifest trees, depending on which elements
 | 
						|
// are of a mergeable type, and choose namespaces according to which have
 | 
						|
// higher priority.
 | 
						|
static Error treeMerge(xmlNodePtr OriginalRoot, xmlNodePtr AdditionalRoot) {
 | 
						|
  if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot))
 | 
						|
    return E;
 | 
						|
  if (auto E = mergeNamespaces(OriginalRoot, AdditionalRoot))
 | 
						|
    return E;
 | 
						|
  xmlNodePtr AdditionalFirstChild = AdditionalRoot->children;
 | 
						|
  xmlNode StoreNext;
 | 
						|
  for (xmlNodePtr Child = AdditionalFirstChild; Child; Child = Child->next) {
 | 
						|
    xmlNodePtr OriginalChildWithName;
 | 
						|
    if (!isMergeableElement(Child->name) ||
 | 
						|
        !(OriginalChildWithName =
 | 
						|
              getChildWithName(OriginalRoot, Child->name)) ||
 | 
						|
        !hasRecognizedNamespace(Child)) {
 | 
						|
      StoreNext.next = Child->next;
 | 
						|
      xmlUnlinkNode(Child);
 | 
						|
      if (!xmlAddChild(OriginalRoot, Child)) {
 | 
						|
        return make_error<WindowsManifestError>(Twine("could not merge ") +
 | 
						|
                                                FROM_XML_CHAR(Child->name));
 | 
						|
      }
 | 
						|
      if (auto E = reconcileNamespaces(Child)) {
 | 
						|
        return E;
 | 
						|
      }
 | 
						|
      Child = &StoreNext;
 | 
						|
    } else if (auto E = treeMerge(OriginalChildWithName, Child)) {
 | 
						|
      return E;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return Error::success();
 | 
						|
}
 | 
						|
 | 
						|
static void stripComments(xmlNodePtr Root) {
 | 
						|
  xmlNode StoreNext;
 | 
						|
  for (xmlNodePtr Child = Root->children; Child; Child = Child->next) {
 | 
						|
    if (!xmlStringsEqual(Child->name, TO_XML_CHAR("comment"))) {
 | 
						|
      stripComments(Child);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    StoreNext.next = Child->next;
 | 
						|
    xmlNodePtr Remove = Child;
 | 
						|
    Child = &StoreNext;
 | 
						|
    xmlUnlinkNode(Remove);
 | 
						|
    xmlFreeNode(Remove);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// libxml2 assumes that attributes do not inherit default namespaces, whereas
 | 
						|
// the original  mt.exe does make this assumption.  This function reconciles
 | 
						|
// this by setting all attributes to have the inherited default namespace.
 | 
						|
static void setAttributeNamespaces(xmlNodePtr Node) {
 | 
						|
  for (xmlAttrPtr Attribute = Node->properties; Attribute;
 | 
						|
       Attribute = Attribute->next) {
 | 
						|
    if (!Attribute->ns) {
 | 
						|
      Attribute->ns = getClosestDefault(Node);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {
 | 
						|
    setAttributeNamespaces(Child);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// The merging process may create too many prefix defined namespaces.  This
 | 
						|
// function removes all unnecessary ones from the tree.
 | 
						|
static void checkAndStripPrefixes(xmlNodePtr Node,
 | 
						|
                                  std::vector<xmlNsPtr> &RequiredPrefixes) {
 | 
						|
  for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {
 | 
						|
    checkAndStripPrefixes(Child, RequiredPrefixes);
 | 
						|
  }
 | 
						|
  if (Node->ns && Node->ns->prefix != nullptr) {
 | 
						|
    xmlNsPtr ClosestDefault = getClosestDefault(Node);
 | 
						|
    if (ClosestDefault &&
 | 
						|
        xmlStringsEqual(ClosestDefault->href, Node->ns->href)) {
 | 
						|
      Node->ns = ClosestDefault;
 | 
						|
    } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) {
 | 
						|
      RequiredPrefixes.push_back(Node->ns);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  for (xmlAttrPtr Attribute = Node->properties; Attribute;
 | 
						|
       Attribute = Attribute->next) {
 | 
						|
    if (Attribute->ns && Attribute->ns->prefix != nullptr) {
 | 
						|
      xmlNsPtr ClosestDefault = getClosestDefault(Node);
 | 
						|
      if (ClosestDefault &&
 | 
						|
          xmlStringsEqual(ClosestDefault->href, Attribute->ns->href)) {
 | 
						|
        Attribute->ns = ClosestDefault;
 | 
						|
      } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) {
 | 
						|
        RequiredPrefixes.push_back(Attribute->ns);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  xmlNsPtr Prev;
 | 
						|
  xmlNs Temp;
 | 
						|
  for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) {
 | 
						|
    if (!Def->prefix || llvm::is_contained(RequiredPrefixes, Def)) {
 | 
						|
      Prev = Def;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    if (Def == Node->nsDef) {
 | 
						|
      Node->nsDef = Def->next;
 | 
						|
    } else {
 | 
						|
      Prev->next = Def->next;
 | 
						|
    }
 | 
						|
    Temp.next = Def->next;
 | 
						|
    xmlFreeNs(Def);
 | 
						|
    Def = &Temp;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() {
 | 
						|
  for (auto &Doc : MergedDocs)
 | 
						|
    xmlFreeDoc(Doc);
 | 
						|
}
 | 
						|
 | 
						|
Error WindowsManifestMerger::WindowsManifestMergerImpl::merge(
 | 
						|
    MemoryBufferRef Manifest) {
 | 
						|
  if (Merged)
 | 
						|
    return make_error<WindowsManifestError>(
 | 
						|
        "merge after getMergedManifest is not supported");
 | 
						|
  if (Manifest.getBufferSize() == 0)
 | 
						|
    return make_error<WindowsManifestError>(
 | 
						|
        "attempted to merge empty manifest");
 | 
						|
  xmlSetGenericErrorFunc((void *)this,
 | 
						|
                         WindowsManifestMergerImpl::errorCallback);
 | 
						|
  xmlDocPtr ManifestXML = xmlReadMemory(
 | 
						|
      Manifest.getBufferStart(), Manifest.getBufferSize(), "manifest.xml",
 | 
						|
      nullptr, XML_PARSE_NOBLANKS | XML_PARSE_NODICT);
 | 
						|
  xmlSetGenericErrorFunc(nullptr, nullptr);
 | 
						|
  if (auto E = getParseError())
 | 
						|
    return E;
 | 
						|
  xmlNodePtr AdditionalRoot = xmlDocGetRootElement(ManifestXML);
 | 
						|
  stripComments(AdditionalRoot);
 | 
						|
  setAttributeNamespaces(AdditionalRoot);
 | 
						|
  if (CombinedDoc == nullptr) {
 | 
						|
    CombinedDoc = ManifestXML;
 | 
						|
  } else {
 | 
						|
    xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc);
 | 
						|
    if (!xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) ||
 | 
						|
        !isMergeableElement(AdditionalRoot->name) ||
 | 
						|
        !hasRecognizedNamespace(AdditionalRoot)) {
 | 
						|
      return make_error<WindowsManifestError>("multiple root nodes");
 | 
						|
    }
 | 
						|
    if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) {
 | 
						|
      return E;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  MergedDocs.push_back(ManifestXML);
 | 
						|
  return Error::success();
 | 
						|
}
 | 
						|
 | 
						|
std::unique_ptr<MemoryBuffer>
 | 
						|
WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() {
 | 
						|
  if (!Merged) {
 | 
						|
    Merged = true;
 | 
						|
 | 
						|
    if (!CombinedDoc)
 | 
						|
      return nullptr;
 | 
						|
 | 
						|
    xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc);
 | 
						|
    std::vector<xmlNsPtr> RequiredPrefixes;
 | 
						|
    checkAndStripPrefixes(CombinedRoot, RequiredPrefixes);
 | 
						|
    std::unique_ptr<xmlDoc, XmlDeleter> OutputDoc(
 | 
						|
        xmlNewDoc((const unsigned char *)"1.0"));
 | 
						|
    xmlDocSetRootElement(OutputDoc.get(), CombinedRoot);
 | 
						|
    assert(nullptr == xmlDocGetRootElement(CombinedDoc));
 | 
						|
 | 
						|
    xmlKeepBlanksDefault(0);
 | 
						|
    xmlChar *Buff = nullptr;
 | 
						|
    xmlDocDumpFormatMemoryEnc(OutputDoc.get(), &Buff, &BufferSize, "UTF-8", 1);
 | 
						|
    Buffer.reset(Buff);
 | 
						|
  }
 | 
						|
 | 
						|
  return BufferSize ? MemoryBuffer::getMemBufferCopy(StringRef(
 | 
						|
                          FROM_XML_CHAR(Buffer.get()), (size_t)BufferSize))
 | 
						|
                    : nullptr;
 | 
						|
}
 | 
						|
 | 
						|
bool windows_manifest::isAvailable() { return true; }
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() {
 | 
						|
}
 | 
						|
 | 
						|
Error WindowsManifestMerger::WindowsManifestMergerImpl::merge(
 | 
						|
    MemoryBufferRef Manifest) {
 | 
						|
  return make_error<WindowsManifestError>("no libxml2");
 | 
						|
}
 | 
						|
 | 
						|
std::unique_ptr<MemoryBuffer>
 | 
						|
WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() {
 | 
						|
  return nullptr;
 | 
						|
}
 | 
						|
 | 
						|
bool windows_manifest::isAvailable() { return false; }
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
WindowsManifestMerger::WindowsManifestMerger()
 | 
						|
    : Impl(std::make_unique<WindowsManifestMergerImpl>()) {}
 | 
						|
 | 
						|
WindowsManifestMerger::~WindowsManifestMerger() = default;
 | 
						|
 | 
						|
Error WindowsManifestMerger::merge(MemoryBufferRef Manifest) {
 | 
						|
  return Impl->merge(Manifest);
 | 
						|
}
 | 
						|
 | 
						|
std::unique_ptr<MemoryBuffer> WindowsManifestMerger::getMergedManifest() {
 | 
						|
  return Impl->getMergedManifest();
 | 
						|
}
 | 
						|
 | 
						|
void WindowsManifestMerger::WindowsManifestMergerImpl::errorCallback(
 | 
						|
    void *Ctx, const char *Format, ...) {
 | 
						|
  auto *Merger = (WindowsManifestMergerImpl *)Ctx;
 | 
						|
  Merger->ParseErrorOccurred = true;
 | 
						|
}
 | 
						|
 | 
						|
Error WindowsManifestMerger::WindowsManifestMergerImpl::getParseError() {
 | 
						|
  if (!ParseErrorOccurred)
 | 
						|
    return Error::success();
 | 
						|
  return make_error<WindowsManifestError>("invalid xml document");
 | 
						|
}
 |