public static int validateCompilationUnits()

in modules/compiler/src/java/flex2/compiler/CompilerAPI.java [1863:2391]


    public static int validateCompilationUnits(FileSpec fileSpec, SourceList sourceList, SourcePath sourcePath,
                                               ResourceBundlePath bundlePath, ResourceContainer resources,
                                               CompilerSwcContext swcContext, Map<String, Source> includedClasses,
                                               ContextStatics perCompileData, Configuration configuration)
    {
        final LocalizationManager l10n = ThreadLocalToolkit.getLocalizationManager();

        final boolean strict = configuration.getCompilerConfiguration().strict();

        final Map<String, Source>
                  updated                     = new HashMap<String, Source>(), // VirtualFile.getName() -> Source
                  updatedWithStableSignature  = new HashMap<String, Source>(), // VirtualFile.getName() -> Source
                  affected                    = new HashMap<String, Source>(); // VirtualFile.getName() -> Source

        final Map<QName, Source> deleted = new HashMap<QName, Source>();

        final Map<String, String> reasons = new HashMap<String, String>(); // VirtualFile.getName() -> String
        final Map<QName, Source> qNames = new HashMap<QName, Source>();

        final Set<String> includeUpdated      = new HashSet<String>(),         // VirtualFile.getName()
                  resourceDelegates           = new HashSet<String>(),         // VirtualFile.getNameForReporting()
                  namespaces                  = new HashSet<String>();

        final Map<QName, Map<String, Source>> dependents = new HashMap<QName, Map<String, Source>>();

        Set<Source> swcSources = swcContext.cachedSources();
        Context ascContext = null;

        if (perCompileData != null)
        {
            ascContext = new Context(perCompileData);
        }

        // put all the Source objects together
        final Set<Source> sources = new HashSet<Source>();
        {
            sources.addAll(swcSources);
            if (fileSpec != null)
                sources.addAll(fileSpec.sources());
            if (sourceList != null)
                sources.addAll(sourceList.sources().values());
            if (sourcePath != null)
                sources.addAll(sourcePath.sources().values());
            if (bundlePath != null)
                sources.addAll(bundlePath.sources().values());
            if (includedClasses != null)
                sources.addAll(includedClasses.values());
        }

        // build a dependency graph
        for (Source source : sources)
        {
            if (source.getName() == null)
            {
                continue;
            }

            CompilationUnit u = source.getCompilationUnit();

            if (u == null)
            {
                continue;
            }

            // collect the names of all the update file includes...
            for (Iterator j = source.getUpdatedFileIncludes(); j != null && j.hasNext();)
            {
                VirtualFile f = (VirtualFile) j.next();
                includeUpdated.add(f.getNameForReporting());
            }

            // register QName --> VirtualFile.getName()
            for (QName qName : u.topLevelDefinitions)
            {
                qNames.put(qName, source);
                dependents.put(qName, new HashMap<String, Source>());
            }
        }

        for (Source source : resources.sources().values())
        {
            if (source.getName() == null)
            {
                continue;
            }

            CompilationUnit u = source.getCompilationUnit();

            if (u == null)
            {
                continue;
            }

            // register QName --> VirtualFile.getName()
            for (QName qName : u.topLevelDefinitions)
            {
                qNames.put(qName, source);
            }
        }

        // setup inheritance-based dependencies...
        for (Source source : sources)
        {
            if (source == null) continue;

            CompilationUnit u = source.getCompilationUnit();
            if (u == null) continue;

            addDependents(source, u.inheritance, dependents);
            addDependents(source, u.namespaces, dependents);
            addDependents(source, u.types, dependents);
            addDependents(source, u.expressions, dependents);
        }

        Logger logger = ThreadLocalToolkit.getLogger();

        // if any of the Source objects in ResourceContainer is bad, obsolete the originating Source.
        for (Source source : resources.sources().values())
        {
            CompilationUnit u = source.getCompilationUnit();
            if (source.hasError() ||
                (u != null && !u.isDone() && !u.hasTypeInfo) ||
                source.isUpdated() ||
                (u != null && u.hasAssets() && u.getAssets().isUpdated()))
            {
                resourceDelegates.add(source.getNameForReporting());
                source.removeCompilationUnit();
            }
        }

        reportObsoletedSwcSources(swcContext, l10n, logger);
        reportShadowedSwcSources(swcSources, sourceList, sourcePath, resources, l10n, logger, sources);

        // identify obsolete CompilationUnit
        //   - NotFullyCompiled
        //   - SourceNoLongerExists
        //   - SourceFileUpdated
        //   - AssedUpdated
        for (Iterator<Source> iterator = sources.iterator(); iterator.hasNext();)
        {
            Source s = iterator.next();
            CompilationUnit u = s.getCompilationUnit();

            // Sources for internal classes like Object never reach the done state or have typeInfo.
            if (s.hasError() ||
                (!s.isInternal() && (u != null && !u.isDone() && !u.hasTypeInfo)) ||
                resourceDelegates.contains(s.getName()))
            {
                affected.put(s.getName(), s);
                reasons.put(s.getName(), l10n.getLocalizedTextString(new NotFullyCompiled()));
                iterator.remove();
            }
            else if (!s.exists())
            {
                updated.put(s.getName(), s);
                reasons.put(s.getName(), l10n.getLocalizedTextString(new SourceNoLongerExists()));

                if (u != null)
                {
                    for (QName qName : u.topLevelDefinitions)
                    {
                        namespaces.add(qName.toString());
                        deleted.put(qName, s);
                    }
                }

                iterator.remove();
            }
            else if (s.isUpdated())
            {
                // signature optimization:
                //     read the old signature from the incremental cache
                //     generate a new signature from the current source
                //     compare -- if stable, we don't have to recompile dependencies
                boolean signatureIsStable = false;
                if ((u != null) &&
                    (!configuration.getCompilerConfiguration().getDisableIncrementalOptimizations()) &&
                    // skip MXML sources:
                    //      MXML is too complicated to parse/codegen at this point in
                    //      order to generate and compare a new checksum
                    (!s.getMimeType().equals(MimeMappings.MXML)))
                {
                    final Long persistedCRC = u.getSignatureChecksum();
                    if (persistedCRC != null)
                    {
                        assert (s.getMimeType().equals(MimeMappings.ABC) ||
                                s.getMimeType().equals(MimeMappings.AS));

                        //TODO if we calculate a new checksum that does not match,
                        //     can we store this checksum and not recompute it later?
                        final Long currentCRC = computeSignatureChecksum(configuration, s);
                        signatureIsStable = (currentCRC != null) &&
                                            (persistedCRC.compareTo(currentCRC) == 0);

                        // if (SignatureExtension.debug)
                        // {
                        //     final String name = u.getSource().getName();
                        //     SignatureExtension.debug("*** FILE UPDATED: Signature "
                        //                                    + (signatureIsStable ? "IS" : "IS NOT")
                        //                                    + " stable ***");
                        //     SignatureExtension.debug("PERSISTED CRC32: " + persistedCRC + "\t--> " + name);
                        //     SignatureExtension.debug("CURRENT   CRC32: " + currentCRC   + "\t--> " + name);
                        // }
                    }
                }

                // if the class signature is stable (it has not changed since the last compile)
                // then we can invalidate and recompile the updated unit alone
                // otherwise we default to a chain reaction, invalidating _all_ dependent units
                if (signatureIsStable)
                {
                    updatedWithStableSignature.put(s.getName(), s);
                }
                else
                {
                    updated.put(s.getName(), s);
                }

                reasons.put(s.getName(), l10n.getLocalizedTextString(new SourceFileUpdated()));
                iterator.remove();
            }
            else if (u != null && u.hasAssets() && u.getAssets().isUpdated())
            {
                updated.put(s.getName(), s);
                reasons.put(s.getName(), l10n.getLocalizedTextString(new AssetUpdated()));
                iterator.remove();
            }
        }

        // permanently remove the deleted Source objects from SourcePath
        //
        // Note: this step is currently necessary because the location-updating loop that follows iterates over
        // 'sources', which has had deleted entries remove. So here we iterate directly over the deleted
        // entries. (Note also that 'reasons' already has an entry for this source.)
        //
        for (Source source : deleted.values())
        {
            if (source.isSourcePathOwner())
            {
                SourcePath sp = (SourcePath) source.getOwner();
                sp.removeSource(source);

                if (ascContext != null)
                {
                    CompilationUnit u = source.getCompilationUnit();

                    if (u != null)
                    {
                        for (QName defName : u.topLevelDefinitions)
                        {
                            ascContext.removeUserDefined(defName.toString());
                        }
                    }
                }
            }
        }

        // Examine each Source object in SourcePath or ResourceBundlePath...
        // if a Source object in SourcePath or ResourceBundlePath is no longer the
        // first choice according to findFile, it should be removed... i.e. look for ambiguous sources
        // - NotSourcePathFirstPreference
        for (Iterator<Source> iterator = sources.iterator(); iterator.hasNext();)
        {
            Source s = iterator.next();

            if (s.isSourcePathOwner() || s.isResourceBundlePathOwner())
            {
                SourcePathBase sp = (SourcePathBase) s.getOwner();
                if (!sp.checkPreference(s))
                {
                    affected.put(s.getName(), s);
                    reasons.put(s.getName(), l10n.getLocalizedTextString(new NotSourcePathFirstPreference()));
                    iterator.remove();
                }
            }
        }

        // invalidate the compilation unit if its dependencies are updated or not cached.
        // - DependencyUpdated
        // - DependencyNotCached
        // - InvalidImportStatement
        for (Iterator<Source> iterator = sources.iterator(); iterator.hasNext();)
        {
            Source s = iterator.next();
            CompilationUnit u = s.getCompilationUnit();
            if (u == null) continue;

            Set<Name> dependencies = new HashSet<Name>();
            dependencies.addAll(u.inheritance);
            dependencies.addAll(u.namespaces);
            dependencies.addAll(u.expressions);
            dependencies.addAll(u.types);

            // Every CompilationUnit has "Object" at the top of it's
            // inheritance chain.  As a result, in
            // As3Compiler.analyze2(), we call inheritSlots() on
            // Object's frame, which unfortunately includes lots of
            // other builtins, like String, Number, Namespace, etc.
            // By inheriting slots for these other builtins, they are
            // not reported as unresolved, so they are not recorded as
            // dependencies.  When switching between airglobal.swc and
            // playerglobal.swc in the same workspace, the builtins
            // change, so we need to check for that.  We use
            // "Namespace" to represent the set of builtins.  See
            // SDK-25206.
            dependencies.add(new QName("", "Namespace"));
            boolean valid = true;

            for (Name dependentName : dependencies)
            {
                QName qName = toQName(dependentName);

                if (qName != null)
                {
                    Source dependentSource = qNames.get(qName);

                    if (dependentSource != null)
                    {
                        CompilationUnit dependentCompilationUnit = dependentSource.getCompilationUnit();

                        if (u.hasTypeInfo && !dependentCompilationUnit.hasTypeInfo && !dependentSource.isInternal())
                        {
                            reasons.put(s.getName(), l10n.getLocalizedTextString(new DependencyNotCached(dependentName.toString())));
                            valid = false;
                        }
                        else
                        {
                            // If the dependency hasn't been updated with
                            // a stable signature, check that the two
                            // ObjectValues references the same Slot.  If
                            // they are not, then the referencing
                            // CompilationUnit needs to be recompiled.
                            if (!updatedWithStableSignature.containsKey(dependentSource.getName()) &&
                                (ascContext != null) &&
                                u.hasTypeInfo &&
                                dependentCompilationUnit.hasTypeInfo &&
                                referencesDifferentSlots(ascContext, u.typeInfo, qName, dependentCompilationUnit.typeInfo))
                            {
                                reasons.put(s.getName(), l10n.getLocalizedTextString(new DependencyUpdated(dependentName.toString())));
                                valid = false;
                            }
                        }
                    }
                    else if (u.hasTypeInfo)
                    {
                        reasons.put(s.getName(), l10n.getLocalizedTextString(new DependencyNotCached(dependentName.toString())));
                        valid = false;
                    }
                }

                if (!valid)
                {
                    affected.put(s.getName(), s);
                    iterator.remove();
                    break;
                }
            }

            if (!swcSources.contains(s))
            {
                // only check the following when strict is enabled.
                valid = valid && strict;

                for (Iterator k = u.importPackageStatements.iterator(); valid && k.hasNext();)
                {
                    String packageName = (String) k.next();

                    if (!hasPackage(sourcePath, swcContext, packageName))
                    {
                        affected.put(s.getName(), s);
                        reasons.put(s.getName(), l10n.getLocalizedTextString(new InvalidImportStatement(packageName)));
                        iterator.remove();
                        namespaces.add(packageName);
                        valid = false;
                        break;
                    }
                }

                for (Iterator k = u.importDefinitionStatements.iterator(); valid && k.hasNext();)
                {
                    QName defName = (QName) k.next();

                    if (!hasDefinition(sourcePath, swcContext, defName))
                    {
                        affected.put(s.getName(), s);
                        reasons.put(s.getName(), l10n.getLocalizedTextString(new InvalidImportStatement(defName.toString())));
                        iterator.remove();
                        namespaces.add(defName.toString());
                        valid = false;
                        break;
                    }
                }
            }
        }

        // - DependentFileModified
        if (strict)
        {
            Map<String, Source> updatedAndAffected = new HashMap<String, Source>(updated);
            updatedAndAffected.putAll(affected);

            for (Source source : updatedAndAffected.values())
            {
                dependentFileModified(source, dependents, updated, affected, reasons, sources);
            }
        }

        for (Iterator<String> i = includeUpdated.iterator(); i.hasNext();)
        {
            ThreadLocalToolkit.getLogger().includedFileUpdated(i.next());
        }

        int affectedCount = affected.size();
        logReasonAndRemoveCompilationUnit(affected, reasons, includeUpdated, swcContext);
        logReasonAndRemoveCompilationUnit(updated, reasons, includeUpdated, swcContext);


        // If a source was updated with a stable signature, then we need to seed ASCs userDefined
        // with the definitions from that source.  This is because we will recompile only the source with the stable
        // signature, and none of it's dependents.  Those dependencies will point at the old TypeValues,
        // but new TypeValues will be created when we recompile the source because they weren't in userDefined.  By putting
        // the old TypeValues in userDefined, when the Source is recompiled it will reuse that same TypeValue
        // instance, instead of creating a new one.
        // We do not have to do this for the updated or affected maps, because anything in those will force
        // their dependencies to be recompiled.
        seedUserDefined(updatedWithStableSignature.values(), ascContext, perCompileData);

        logReasonAndRemoveCompilationUnit(updatedWithStableSignature, reasons, includeUpdated, swcContext);

		// if a compilation unit becomes obsolete, its satellite compilation units in ResourceContainer
		// must go away too.
		for (Source s : resources.sources().values())
		{
			if (s != null)
			{
				String name = s.getNameForReporting();
				if (affected.containsKey(name) || updated.containsKey(name))
				{
					s.removeCompilationUnit();
				}
			}
		}

        affected.clear();

        // validate multinames
        // - MultiNameMeaningChanged
        for (Iterator<Source> iterator = sources.iterator(); iterator.hasNext();)
        {
            Source s = iterator.next();
            CompilationUnit u = s.getCompilationUnit();
            if (u == null) continue;

            for (Entry<MultiName, QName> entry : u.inheritanceHistory.entrySet())
            {
                MultiName multiName = entry.getKey();
                QName qName = entry.getValue();

                try
                {
                    if (!validateMultiName(multiName, qName, sourcePath))
                    {
                        affected.put(s.getName(), s);
                        reasons.put(s.getName(), l10n.getLocalizedTextString(new MultiNameMeaningChanged(multiName, qName)));
                        iterator.remove();
                    }
                }
                catch (CompilerException ex)
                {
                    affected.put(s.getName(), s);
                    reasons.put(s.getName(), ex.getMessage());
                    iterator.remove();
                }
            }
        }

        affectedCount += affected.size();

        // remove CompilationUnits from affected Map
        logReasonAndRemoveCompilationUnit(affected, reasons, includeUpdated, swcContext);

		// if a compilation unit becomes obsolete, its satellite compilation units in ResourceContainer
		// must go away too.
		for (Source s : resources.sources().values())
		{
			if (s != null)
			{
				String name = s.getNameForReporting();
				if (affected.containsKey(name))
				{
					s.removeCompilationUnit();
				}
			}
		}

        // refresh the state of ResourceContainer
        resources.refresh();

        // finally, remove the deleted namespaces from SymbolTable...
        if (perCompileData != null)
        {
            for (String ns : namespaces)
            {
                perCompileData.removeNamespace(ns);
            }
        }

        final int updateCount = updated.size() + updatedWithStableSignature.size();
        if (updateCount + affectedCount > 0)
        {
            ThreadLocalToolkit.log(new FilesChangedAffected(updateCount, affectedCount));
        }

        // Any sources left are valid and if they have type info, we
        // need to seed ASC's userDefined with them, so we don't end
        // up with multiple TypeValue copies floating around.  The
        // global SWC cache requires this to be run.
        seedUserDefined(sources, ascContext, perCompileData);

        if (configuration.getBenchmarkCompilerDetails() > 0 &&
            ThreadLocalToolkit.getBenchmark() != null)
        {
            ThreadLocalToolkit.getBenchmark().benchmark2("validateCompilationUnits");
        }

        int count = updateCount + affectedCount;

        return count;
    }