getAddonInstance(parent, addonPkgInfo) {
    // If the new addon is itself a bundle host (i.e. lazy engine), there is only one
    // instance of the bundle host, and it's in the entries of the bundleHostCache, outside
    // of the 'regular' addon caches. Because 'setupBundleHostCache' ran during construction,
    // we know that an entry is in the cache with this engine name.
    if (addonPkgInfo.isForBundleHost()) {
      let cacheEntry = this._getBundleHostCacheEntry(addonPkgInfo);

      if (cacheEntry[TARGET_INSTANCE]) {
        logger.debug(`About to construct BR PROXY to cache entry for addon at: ${addonPkgInfo.realPath}`);
        this.numProxies++;
        return getAddonProxy(cacheEntry, parent);
      } else {
        // create an instance, put it in the pre-existing cache entry, then
        // return it (as the first instance of the lazy engine.)
        logger.debug(`About to fill in BR EXISTING cache entry for addon at: ${addonPkgInfo.realPath}`);
        this.numAddonInstances++;
        let addon = addonPkgInfo.constructAddonInstance(parent, this.project);
        cacheEntry[TARGET_INSTANCE] = addon; // cache BEFORE initializing child addons
        addonPkgInfo.initChildAddons(addon);
        return addon;
      }
    }

    // We know now we're asking for a 'regular' (non-bundle-host) addon instance.

    let bundleHost = this.findBundleHost(parent, addonPkgInfo);

    // if the bundle host "owns" the new addon instance
    //   * Do we already have an instance of the addon cached?
    //     * If so, make a proxy for it.
    //     * If not, make a new instance of the addon and cache it in the
    //       bundle host's addon cache.
    // If not, it means the bundle host is a lazy engine but the LCA host also uses
    // the addon and deduping is enabled
    //   * If the LCA host already has a cached entry, return a proxy to that
    //   * If it does not, create a 'blank' cache entry and return a proxy to that.
    //     When the addon is encountered later when processing the LCA host's addons,
    //     fill in the instance.
    if (this.bundleHostOwnsInstance(bundleHost, addonPkgInfo)) {
      let bundleHostCacheEntry = this._getBundleHostCacheEntry(bundleHost._packageInfo);
      let addonInstanceCache = bundleHostCacheEntry.addonInstanceCache;
      let addonCacheEntry = addonInstanceCache.get(addonPkgInfo.realPath);
      let addonInstance;

      if (addonCacheEntry) {
        if (addonCacheEntry[TARGET_INSTANCE]) {
          logger.debug(`About to construct REGULAR ADDON PROXY for addon at: ${addonPkgInfo.realPath}`);
          this.numProxies++;
          return getAddonProxy(addonCacheEntry, parent);
        } else {
          // the cache entry was created 'empty' by an earlier call, indicating
          // an addon that is used in a lazy engine but also used by its LCA host,
          // and we're now creating the instance for the LCA host.
          // Fill in the entry and return the new instance.
          logger.debug(`About to fill in REGULAR ADDON EXISTING cache entry for addon at: ${addonPkgInfo.realPath}`);
          this.numAddonInstances++;
          addonInstance = addonPkgInfo.constructAddonInstance(parent, this.project);
          addonCacheEntry[TARGET_INSTANCE] = addonInstance; // cache BEFORE initializing child addons
          addonPkgInfo.initChildAddons(addonInstance);
          return addonInstance;
        }
      }

      // There is no entry for this addon in the bundleHost's addon cache. Create a new
      // instance, cache it in the addon cache, and return it.
      logger.debug(`About to construct REGULAR ADDON NEW cache entry for addon at: ${addonPkgInfo.realPath}`);
      this.numAddonInstances++;
      addonInstance = addonPkgInfo.constructAddonInstance(parent, this.project);
      addonCacheEntry = this.createAddonCacheEntry(addonInstance, addonPkgInfo.realPath);
      addonInstanceCache.set(addonPkgInfo.realPath, addonCacheEntry); // cache BEFORE initializing child addons
      addonPkgInfo.initChildAddons(addonInstance);
      return addonInstance;
    } else {
      // The bundleHost is not the project but the some ancestor bundles the addon and
      // deduping is enabled, so the cache entry needs to go in the bundle owner's cache.
      // Get/create an empty cache entry and return a proxy to it. The bundle owner will
      // set the instance later (see above).
      let bundleHostCacheEntry = this._getBundleHostCacheEntry(
        this.findBundleOwner(bundleHost._packageInfo, addonPkgInfo)
      );
      let addonCacheEntry = bundleHostCacheEntry.addonInstanceCache.get(addonPkgInfo.realPath);

      if (!addonCacheEntry) {
        logger.debug(`About to construct REGULAR ADDON EMPTY cache entry for addon at: ${addonPkgInfo.realPath}`);
        addonCacheEntry = this.createAddonCacheEntry(null, addonPkgInfo.realPath);
        bundleHostCacheEntry.addonInstanceCache.set(addonPkgInfo.realPath, addonCacheEntry);
      }

      logger.debug(`About to construct REGULAR ADDON PROXY for EMPTY addon at: ${addonPkgInfo.realPath}`);
      this.numProxies++;
      return getAddonProxy(addonCacheEntry, parent);
    }
  }
Called from PackageInfo.getAddonInstance(), return an instance of the requested
addon or a Proxy, based on the type of addon and its bundle host.

@method getAddonInstance
@param {Addon|Project} parent the parent Addon or Project this addon instance is
a child of.
@param {*} addonPkgInfo the PackageInfo for the addon being created.
@return {Addon|Proxy} An addon instance (for the first copy of the addon) or a Proxy.
An addon that is a lazy engine will only ever have a single copy in the cache.
An addon that is not will have 1 copy per bundle host (Project or lazy engine),
except if it is an addon that's also owned by a given LCA host and transitive
dedupe is enabled (`engineAddonTransitiveDedupeEnabled`), in which case it will
only have a single copy in the project's addon cache.