Rework class loader context implementation.

The old representation consisted of a list of libraries (UsesLibraries),
a list of optional libraries (OptionalUsesLibraries) and a mapping from
library name to its build/install paths (LibraryPaths). The separation
into lists and map was necessary because of special handling of
compatibility libraries, which is now unified with normal libraries.

The new representation is a mapping from target SDK version to a tree
structure ClassLoaderContext. Each node of the tree represents a library
and contains library name, build/install paths and a slice of
subcontexts for dependencies. The same library may occur in the tree
multiple times in case it is a dependency of multiple libraries. The
order in which libraries are added matters (the resulting tree shape may
be different).

Test results have to be updated, as the resulting <uses-library> list is
reodered (previously it was a sorted list of map keys, and now it is
formed by a depth-first preorder traversal of the class loader tree).

Test: lunch aosp_cf_x86_phone-userdebug && m
Bug: 132357300
Bug: 168686456
Change-Id: I11be8cd2967f004fd58753d7c5fb99fed179cd63
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 6d77812..8d61901 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -16,7 +16,7 @@
 
 import (
 	"fmt"
-	"path/filepath"
+	"strconv"
 	"strings"
 
 	"android/soong/android"
@@ -49,39 +49,39 @@
 
 const UnknownInstallLibraryPath = "error"
 
-const AnySdkVersion int = 9999 // should go last in class loader context
+// AnySdkVersion means that the class loader context is needed regardless of the targetSdkVersion
+// of the app. The numeric value affects the key order in the map and, as a result, the order of
+// arguments passed to construct_context.py (high value means that the unconditional context goes
+// last). We use the converntional "current" SDK level (10000), but any big number would do as well.
+const AnySdkVersion int = android.FutureApiLevelInt
 
-// LibraryPath contains paths to the library DEX jar on host and on device.
-type LibraryPath struct {
-	Host   android.Path
+// ClassLoaderContext is a tree of libraries used by the dexpreopted module with their dependencies.
+// The context is used by dex2oat to compile the module and recorded in the AOT-compiled files, so
+// that it can be checked agains the run-time class loader context on device. If there is a mismatch
+// at runtime, AOT-compiled code is rejected.
+type ClassLoaderContext struct {
+	// The name of the library (same as the name of the module that contains it).
+	Name string
+
+	// On-host build path to the library dex file (used in dex2oat argument --class-loader-context).
+	Host android.Path
+
+	// On-device install path (used in dex2oat argument --stored-class-loader-context).
 	Device string
+
+	// Nested class loader subcontexts for dependencies.
+	Subcontexts []*ClassLoaderContext
 }
 
-// LibraryPaths is a map from library name to on-host and on-device paths to its DEX jar.
-type LibraryPaths map[string]*LibraryPath
+// ClassLoaderContextMap is a map from SDK version to a class loader context.
+// There is a special entry with key AnySdkVersion that stores unconditional class loader context.
+// Other entries store conditional contexts that should be added for some apps that have
+// targetSdkVersion in the manifest lower than the key SDK version.
+type ClassLoaderContextMap map[int][]*ClassLoaderContext
 
-type classLoaderContext struct {
-	// Library names
-	Names []string
-
-	// The class loader context using paths in the build.
-	Host android.Paths
-
-	// The class loader context using paths as they will be on the device.
-	Target []string
-}
-
-// A map of class loader contexts for each SDK version.
-// A map entry for "any" version contains libraries that are unconditionally added to class loader
-// context. Map entries for existing versions contains libraries that were in the default classpath
-// until that API version, and should be added to class loader context if and only if the
-// targetSdkVersion in the manifest or APK is less than that API version.
-type classLoaderContextMap map[int]*classLoaderContext
-
-// Add a new library path to the map, unless a path for this library already exists.
-// If necessary, check that the build and install paths exist.
-func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleInstallPathContext, lib string,
-	hostPath, installPath android.Path, strict bool) error {
+// Add class loader context for the given library to the map entry for the given SDK version.
+func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
+	hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) error {
 
 	// If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
 	// not found. However, this is likely to result is disabling dexpreopt, as it won't be
@@ -89,263 +89,254 @@
 	strict = strict && !ctx.Config().AllowMissingDependencies()
 
 	if hostPath == nil && strict {
-		return fmt.Errorf("unknown build path to <uses-library> '%s'", lib)
+		return fmt.Errorf("unknown build path to <uses-library> \"%s\"", lib)
 	}
 
+	devicePath := UnknownInstallLibraryPath
 	if installPath == nil {
 		if android.InList(lib, CompatUsesLibs) || android.InList(lib, OptionalCompatUsesLibs) {
 			// Assume that compatibility libraries are installed in /system/framework.
 			installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
 		} else if strict {
-			return fmt.Errorf("unknown install path to <uses-library> '%s'", lib)
-		}
-	}
-
-	// Add a library only if the build and install path to it is known.
-	if _, present := libPaths[lib]; !present {
-		var devicePath string
-		if installPath != nil {
-			devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
+			return fmt.Errorf("unknown install path to <uses-library> \"%s\"", lib)
 		} else {
 			// For some stub libraries the only known thing is the name of their implementation
 			// library, but the library itself is unavailable (missing or part of a prebuilt). In
 			// such cases we still need to add the library to <uses-library> tags in the manifest,
-			// but we cannot use if for dexpreopt.
-			devicePath = UnknownInstallLibraryPath
+			// but we cannot use it for dexpreopt.
 		}
-		libPaths[lib] = &LibraryPath{hostPath, devicePath}
 	}
+	if installPath != nil {
+		devicePath = android.InstallPathToOnDevicePath(ctx, installPath.(android.InstallPath))
+	}
+
+	subcontexts := nestedClcMap[AnySdkVersion]
+
+	// If the library with this name is already present as one of the unconditional top-level
+	// components, do not re-add it.
+	for _, clc := range clcMap[sdkVer] {
+		if clc.Name == lib {
+			return nil
+		}
+	}
+
+	clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
+		Name:        lib,
+		Host:        hostPath,
+		Device:      devicePath,
+		Subcontexts: subcontexts,
+	})
 	return nil
 }
 
-// Wrapper around addLibraryPath that does error reporting.
-func (libPaths LibraryPaths) addLibraryPathOrReportError(ctx android.ModuleInstallPathContext, lib string,
-	hostPath, installPath android.Path, strict bool) {
+// Wrapper around addContext that reports errors.
+func (clcMap ClassLoaderContextMap) addContextOrReportError(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
+	hostPath, installPath android.Path, strict bool, nestedClcMap ClassLoaderContextMap) {
 
-	err := libPaths.addLibraryPath(ctx, lib, hostPath, installPath, strict)
+	err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, strict, nestedClcMap)
 	if err != nil {
+		ctx.ModuleErrorf(err.Error())
 		android.ReportPathErrorf(ctx, err.Error())
 	}
 }
 
-// Add a new library path to the map. Enforce checks that the library paths exist.
-func (libPaths LibraryPaths) AddLibraryPath(ctx android.ModuleInstallPathContext, lib string, hostPath, installPath android.Path) {
-	libPaths.addLibraryPathOrReportError(ctx, lib, hostPath, installPath, true)
+// Add class loader context. Fail on unknown build/install paths.
+func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, lib string,
+	hostPath, installPath android.Path) {
+
+	clcMap.addContextOrReportError(ctx, AnySdkVersion, lib, hostPath, installPath, true, nil)
 }
 
-// Add a new library path to the map, if the library exists (name is not nil).
-// Don't enforce checks that the library paths exist. Some libraries may be missing from the build,
-// but their names still need to be added to <uses-library> tags in the manifest.
-func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.ModuleInstallPathContext, lib *string, hostPath, installPath android.Path) {
+// Add class loader context if the library exists. Don't fail on unknown build/install paths.
+func (clcMap ClassLoaderContextMap) MaybeAddContext(ctx android.ModuleInstallPathContext, lib *string,
+	hostPath, installPath android.Path) {
+
 	if lib != nil {
-		libPaths.addLibraryPathOrReportError(ctx, *lib, hostPath, installPath, false)
+		clcMap.addContextOrReportError(ctx, AnySdkVersion, *lib, hostPath, installPath, false, nil)
 	}
 }
 
-// Add library paths from the second map to the first map (do not override existing entries).
-func (libPaths LibraryPaths) AddLibraryPaths(otherPaths LibraryPaths) {
-	for lib, path := range otherPaths {
-		if _, present := libPaths[lib]; !present {
-			libPaths[lib] = path
-		}
-	}
+// Add class loader context for the given SDK version. Fail on unknown build/install paths.
+func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
+	lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
+
+	clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap)
 }
 
-func (m classLoaderContextMap) getValue(sdkVer int) *classLoaderContext {
-	if _, ok := m[sdkVer]; !ok {
-		m[sdkVer] = &classLoaderContext{}
-	}
-	return m[sdkVer]
-}
-
-func (clc *classLoaderContext) addLib(lib string, hostPath android.Path, targetPath string) {
-	clc.Names = append(clc.Names, lib)
-	clc.Host = append(clc.Host, hostPath)
-	clc.Target = append(clc.Target, targetPath)
-}
-
-func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, module *ModuleConfig,
-	libs ...string) (bool, error) {
-
-	clc := m.getValue(sdkVer)
-	for _, lib := range libs {
-		if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath {
-			clc.addLib(lib, p.Host, p.Device)
-		} else {
-			if sdkVer == AnySdkVersion {
-				// Fail the build if dexpreopt doesn't know paths to one of the <uses-library>
-				// dependencies. In the future we may need to relax this and just disable dexpreopt.
-				return false, fmt.Errorf("dexpreopt cannot find path for <uses-library> '%s'", lib)
-			} else {
-				// No error for compatibility libraries, as Soong doesn't know if they are needed
-				// (this depends on the targetSdkVersion in the manifest).
-				return false, nil
+// Merge the other class loader context map into this one, do not override existing entries.
+func (clcMap ClassLoaderContextMap) AddContextMap(otherClcMap ClassLoaderContextMap) {
+	for sdkVer, otherClcs := range otherClcMap {
+		for _, otherClc := range otherClcs {
+			alreadyHave := false
+			for _, clc := range clcMap[sdkVer] {
+				if clc.Name == otherClc.Name {
+					alreadyHave = true
+					break
+				}
+			}
+			if !alreadyHave {
+				clcMap[sdkVer] = append(clcMap[sdkVer], otherClc)
 			}
 		}
 	}
-	return true, nil
 }
 
-func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
-	clc := m.getValue(sdkVer)
-	for _, lib := range libs {
-		clc.addLib(lib, SystemServerDexJarHostPath(ctx, lib), filepath.Join("/system/framework", lib+".jar"))
+// List of libraries in the unconditional class loader context, excluding dependencies of shared libraries.
+func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
+	if clcMap != nil {
+		// compatibility libraries (those in conditional context) are not added to <uses-library> tags
+		ulibs = usesLibsRec(clcMap[AnySdkVersion])
+		ulibs = android.FirstUniqueStrings(ulibs)
 	}
+	return ulibs
 }
 
-func (m classLoaderContextMap) usesLibs() []string {
-	if clc, ok := m[AnySdkVersion]; ok {
-		return clc.Names
+func usesLibsRec(clcs []*ClassLoaderContext) (ulibs []string) {
+	for _, clc := range clcs {
+		ulibs = append(ulibs, clc.Name)
+		ulibs = append(ulibs, usesLibsRec(clc.Subcontexts)...)
 	}
-	return nil
-}
-
-// genClassLoaderContext generates host and target class loader context to be passed to the dex2oat
-// command for the dexpreopted module. There are three possible cases:
-//
-// 1. System server jars. They have a special class loader context that includes other system
-//    server jars.
-//
-// 2. Library jars or APKs which have precise list of their <uses-library> libs. Their class loader
-//    context includes build and on-device paths to these libs. In some cases it may happen that
-//    the path to a <uses-library> is unknown (e.g. the dexpreopted module may depend on stubs
-//    library, whose implementation library is missing from the build altogether). In such case
-//    dexpreopting with the <uses-library> is impossible, and dexpreopting without it is pointless,
-//    as the runtime classpath won't match and the dexpreopted code will be discarded. Therefore in
-//    such cases the function returns nil, which disables dexpreopt.
-//
-// 3. All other library jars or APKs for which the exact <uses-library> list is unknown. They use
-//    the unsafe &-classpath workaround that means empty class loader context and absence of runtime
-//    check that the class loader context provided by the PackageManager agrees with the stored
-//    class loader context recorded in the .odex file.
-//
-func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) (*classLoaderContextMap, error) {
-	classLoaderContexts := make(classLoaderContextMap)
-	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
-
-	if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
-		// System server jars should be dexpreopted together: class loader context of each jar
-		// should include all preceding jars on the system server classpath.
-		classLoaderContexts.addSystemServerLibs(AnySdkVersion, ctx, module, systemServerJars[:jarIndex]...)
-
-	} else if module.EnforceUsesLibraries {
-		// Unconditional class loader context.
-		usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
-		if ok, err := classLoaderContexts.addLibs(ctx, AnySdkVersion, module, usesLibs...); !ok {
-			return nil, err
-		}
-
-		// Conditional class loader context for API version < 28.
-		const httpLegacy = "org.apache.http.legacy"
-		if ok, err := classLoaderContexts.addLibs(ctx, 28, module, httpLegacy); !ok {
-			return nil, err
-		}
-
-		// Conditional class loader context for API version < 29.
-		usesLibs29 := []string{
-			"android.hidl.base-V1.0-java",
-			"android.hidl.manager-V1.0-java",
-		}
-		if ok, err := classLoaderContexts.addLibs(ctx, 29, module, usesLibs29...); !ok {
-			return nil, err
-		}
-
-		// Conditional class loader context for API version < 30.
-		if ok, err := classLoaderContexts.addLibs(ctx, 30, module, OptionalCompatUsesLibs30...); !ok {
-			return nil, err
-		}
-
-	} else {
-		// Pass special class loader context to skip the classpath and collision check.
-		// This will get removed once LOCAL_USES_LIBRARIES is enforced.
-		// Right now LOCAL_USES_LIBRARIES is opt in, for the case where it's not specified we still default
-		// to the &.
-	}
-
-	fixConditionalClassLoaderContext(classLoaderContexts)
-
-	return &classLoaderContexts, nil
+	return ulibs
 }
 
 // Now that the full unconditional context is known, reconstruct conditional context.
 // Apply filters for individual libraries, mirroring what the PackageManager does when it
 // constructs class loader context on device.
 //
-// TODO(b/132357300):
-//   - remove android.hidl.manager and android.hidl.base unless the app is a system app.
+// TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
 //
-func fixConditionalClassLoaderContext(clcMap classLoaderContextMap) {
-	usesLibs := clcMap.usesLibs()
+func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
+	usesLibs := clcMap.UsesLibs()
 
-	for sdkVer, clc := range clcMap {
+	for sdkVer, clcs := range clcMap {
 		if sdkVer == AnySdkVersion {
 			continue
 		}
-		clcMap[sdkVer] = &classLoaderContext{}
-		for i, lib := range clc.Names {
-			if android.InList(lib, usesLibs) {
+		fixedClcs := []*ClassLoaderContext{}
+		for _, clc := range clcs {
+			if android.InList(clc.Name, usesLibs) {
 				// skip compatibility libraries that are already included in unconditional context
-			} else if lib == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
+			} else if clc.Name == AndroidTestMock && !android.InList("android.test.runner", usesLibs) {
 				// android.test.mock is only needed as a compatibility library (in conditional class
 				// loader context) if android.test.runner is used, otherwise skip it
 			} else {
-				clcMap[sdkVer].addLib(lib, clc.Host[i], clc.Target[i])
+				fixedClcs = append(fixedClcs, clc)
 			}
+			clcMap[sdkVer] = fixedClcs
 		}
 	}
 }
 
-// Return the class loader context as a string and a slice of build paths for all dependencies.
-func computeClassLoaderContext(ctx android.PathContext, clcMap classLoaderContextMap) (clcStr string, paths android.Paths) {
-	for _, ver := range android.SortedIntKeys(clcMap) {
-		clc := clcMap.getValue(ver)
-
-		clcLen := len(clc.Names)
-		if clcLen != len(clc.Host) || clcLen != len(clc.Target) {
-			android.ReportPathErrorf(ctx, "ill-formed class loader context")
+// Return true if all build/install library paths are valid (including recursive subcontexts),
+// otherwise return false. A build path is valid if it's not nil. An install path is valid if it's
+// not equal to a special "error" value.
+func validateClassLoaderContext(clcMap ClassLoaderContextMap) (bool, error) {
+	for sdkVer, clcs := range clcMap {
+		if valid, err := validateClassLoaderContextRec(sdkVer, clcs); !valid || err != nil {
+			return valid, err
 		}
+	}
+	return true, nil
+}
 
-		var hostClc, targetClc []string
-		var hostPaths android.Paths
-
-		for i := 0; i < clcLen; i++ {
-			hostStr := "PCL[" + clc.Host[i].String() + "]"
-			targetStr := "PCL[" + clc.Target[i] + "]"
-
-			hostClc = append(hostClc, hostStr)
-			targetClc = append(targetClc, targetStr)
-			hostPaths = append(hostPaths, clc.Host[i])
+func validateClassLoaderContextRec(sdkVer int, clcs []*ClassLoaderContext) (bool, error) {
+	for _, clc := range clcs {
+		if clc.Host == nil || clc.Device == UnknownInstallLibraryPath {
+			if sdkVer == AnySdkVersion {
+				// Return error if dexpreopt doesn't know paths to one of the <uses-library>
+				// dependencies. In the future we may need to relax this and just disable dexpreopt.
+				return false, fmt.Errorf("invalid path for <uses-library> \"%s\"", clc.Name)
+			} else {
+				// No error for compatibility libraries, as Soong doesn't know if they are needed
+				// (this depends on the targetSdkVersion in the manifest), but the CLC is invalid.
+				return false, nil
+			}
 		}
+		if valid, err := validateClassLoaderContextRec(sdkVer, clc.Subcontexts); !valid || err != nil {
+			return valid, err
+		}
+	}
+	return true, nil
+}
 
+// Return the class loader context as a string, and a slice of build paths for all dependencies.
+// Perform a depth-first preorder traversal of the class loader context tree for each SDK version.
+// Return the resulting string and a slice of on-host build paths to all library dependencies.
+func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) {
+	for _, sdkVer := range android.SortedIntKeys(clcMap) { // determinisitc traversal order
+		sdkVerStr := fmt.Sprintf("%d", sdkVer)
+		if sdkVer == AnySdkVersion {
+			sdkVerStr = "any" // a special keyword that means any SDK version
+		}
+		hostClc, targetClc, hostPaths := computeClassLoaderContextRec(clcMap[sdkVer])
 		if hostPaths != nil {
-			sdkVerStr := fmt.Sprintf("%d", ver)
-			if ver == AnySdkVersion {
-				sdkVerStr = "any" // a special keyword that means any SDK version
-			}
-			clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, strings.Join(hostClc, "#"))
-			clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, strings.Join(targetClc, "#"))
-			paths = append(paths, hostPaths...)
+			clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc)
+			clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc)
 		}
+		paths = append(paths, hostPaths...)
 	}
-
-	return clcStr, paths
+	return clcStr, android.FirstUniquePaths(paths)
 }
 
+func computeClassLoaderContextRec(clcs []*ClassLoaderContext) (string, string, android.Paths) {
+	var paths android.Paths
+	var clcsHost, clcsTarget []string
+
+	for _, clc := range clcs {
+		subClcHost, subClcTarget, subPaths := computeClassLoaderContextRec(clc.Subcontexts)
+		if subPaths != nil {
+			subClcHost = "{" + subClcHost + "}"
+			subClcTarget = "{" + subClcTarget + "}"
+		}
+
+		clcsHost = append(clcsHost, "PCL["+clc.Host.String()+"]"+subClcHost)
+		clcsTarget = append(clcsTarget, "PCL["+clc.Device+"]"+subClcTarget)
+
+		paths = append(paths, clc.Host)
+		paths = append(paths, subPaths...)
+	}
+
+	clcHost := strings.Join(clcsHost, "#")
+	clcTarget := strings.Join(clcsTarget, "#")
+
+	return clcHost, clcTarget, paths
+}
+
+// Paths to a <uses-library> on host and on device.
 type jsonLibraryPath struct {
 	Host   string
 	Device string
 }
 
-type jsonLibraryPaths map[string]jsonLibraryPath
+// Class loader contexts that come from Make (via JSON dexpreopt.config) files have simpler
+// structure than Soong class loader contexts: they are flat maps from a <uses-library> name to its
+// on-host and on-device paths. There are no nested subcontexts. It is a limitation of the current
+// Make implementation.
+type jsonClassLoaderContext map[string]jsonLibraryPath
 
-// convert JSON map of library paths to LibraryPaths
-func constructLibraryPaths(ctx android.PathContext, paths jsonLibraryPaths) LibraryPaths {
-	m := LibraryPaths{}
-	for lib, path := range paths {
-		m[lib] = &LibraryPath{
-			constructPath(ctx, path.Host),
-			path.Device,
+// A map from SDK version (represented with a JSON string) to JSON class loader context.
+type jsonClassLoaderContextMap map[string]jsonClassLoaderContext
+
+// Convert JSON class loader context map to ClassLoaderContextMap.
+func fromJsonClassLoaderContext(ctx android.PathContext, jClcMap jsonClassLoaderContextMap) ClassLoaderContextMap {
+	clcMap := make(ClassLoaderContextMap)
+	for sdkVerStr, clc := range jClcMap {
+		sdkVer, ok := strconv.Atoi(sdkVerStr)
+		if ok != nil {
+			if sdkVerStr == "any" {
+				sdkVer = AnySdkVersion
+			} else {
+				android.ReportPathErrorf(ctx, "failed to parse SDK version in dexpreopt.config: '%s'", sdkVerStr)
+			}
+		}
+		for lib, path := range clc {
+			clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
+				Name:        lib,
+				Host:        constructPath(ctx, path.Host),
+				Device:      path.Device,
+				Subcontexts: nil,
+			})
 		}
 	}
-	return m
+	return clcMap
 }