X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fcompiler%2Fmodule%2Frepository%2FModuleRepository.java;h=23491235ce3289c46fd254009cde5963774374d6;hp=40c69f4348861e2d4bd58877d762f4677d90c7f1;hb=3a31aa451eae6bc9fa359ada9df47a354605f4b6;hpb=90b5d2ee1cfe82a75a097116f2f34ddb1cdff067 diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java index 40c69f434..23491235c 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java @@ -7,8 +7,10 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.simantics.scl.compiler.common.exceptions.InternalCompilerError; +import org.simantics.scl.compiler.compilation.CompilationContext; import org.simantics.scl.compiler.elaboration.modules.SCLValue; import org.simantics.scl.compiler.environment.ConcreteEnvironment; +import org.simantics.scl.compiler.environment.EmptyEnvironment; import org.simantics.scl.compiler.environment.Environment; import org.simantics.scl.compiler.environment.NamespaceImpl.ModuleImport; import org.simantics.scl.compiler.environment.NamespaceSpec; @@ -19,6 +21,7 @@ import org.simantics.scl.compiler.errors.CompilationError; import org.simantics.scl.compiler.errors.DoesNotExist; import org.simantics.scl.compiler.errors.Failable; import org.simantics.scl.compiler.errors.Failure; +import org.simantics.scl.compiler.errors.Locations; import org.simantics.scl.compiler.errors.Success; import org.simantics.scl.compiler.module.ImportDeclaration; import org.simantics.scl.compiler.module.Module; @@ -34,8 +37,11 @@ import org.simantics.scl.compiler.top.ModuleInitializer; import org.simantics.scl.compiler.top.SCLCompilerConfiguration; import org.simantics.scl.compiler.top.ValueNotFound; import org.simantics.scl.compiler.types.Types; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import gnu.trove.map.hash.THashMap; +import gnu.trove.map.hash.TObjectLongHashMap; import gnu.trove.procedure.TObjectObjectProcedure; import gnu.trove.set.hash.THashSet; @@ -45,6 +51,9 @@ import gnu.trove.set.hash.THashSet; * @author Hannu Niemistö */ public class ModuleRepository { + + private static final Logger LOGGER = LoggerFactory.getLogger(ModuleRepository.class); + private final ModuleRepository parentRepository; private final ModuleSourceRepository sourceRepository; private ConcurrentHashMap moduleCache = new ConcurrentHashMap(); @@ -66,10 +75,11 @@ public class ModuleRepository { private static void finishModuleCompilation(String moduleName) { PENDING_MODULES.get().remove(moduleName); } - + private class ModuleEntry extends UpdateListener implements Observable { final String moduleName; - THashSet listeners = new THashSet(); + THashSet listeners = new THashSet(); // listeners == null is used as a marker that this entry is disposed + // should be handled only inside synchronized code ModuleSource source; Failable compilationResult; @@ -94,33 +104,38 @@ public class ModuleRepository { @Override public void notifyAboutUpdate() { + // There is a chance that another observable calls notifyAboutUpdate() before stopListening has been completed, + // but notifyAboutUpdate(ArrayList) lets only one thread to do the notification of dependencies + // by clearing listeners field. + stopListening(); ArrayList externalListeners = new ArrayList(); notifyAboutUpdate(externalListeners); for(UpdateListener listener : externalListeners) listener.notifyAboutUpdate(); } - synchronized void notifyAboutUpdate(ArrayList externalListeners) { - stopListening(); - if (listeners == null) - return; + void notifyAboutUpdate(ArrayList externalListeners) { + THashSet listenersCopy; + synchronized(this) { + listenersCopy = listeners; + if (listenersCopy == null) + return; + listeners = null; + } if(moduleCache.get(moduleName) == this) { moduleCache.remove(moduleName); if(SCLCompilerConfiguration.TRACE_MODULE_UPDATE) { - System.out.println("Invalidate " + moduleName); - for(UpdateListener l : listeners) - System.out.println(" " + l); + LOGGER.info("Invalidate " + moduleName); + for(UpdateListener l : listenersCopy) + LOGGER.info(" " + l); } - THashSet listenersCopy = listeners; - listeners = null; - for(UpdateListener l : listenersCopy) - l.stopListening(); for(UpdateListener l : listenersCopy) - if(l instanceof ModuleEntry) + if(!l.stopListening()) + ; + else if(l instanceof ModuleEntry) ((ModuleEntry)l).notifyAboutUpdate(externalListeners); - else { + else externalListeners.add(l); - } } } @@ -131,7 +146,7 @@ public class ModuleRepository { compilationResult = DoesNotExist.getInstance(); else { if(SCLCompilerConfiguration.TRACE_MODULE_UPDATE) - System.out.println("Compile " + source); + LOGGER.info("Compile " + source); beginModuleCompilation(moduleName); compilationResult = source.compileModule(ModuleRepository.this, this, advisor == null ? null : advisor.getOptions(moduleName)); finishModuleCompilation(moduleName); @@ -159,7 +174,7 @@ public class ModuleRepository { Collection dependencies = module.getDependencies(); THashMap moduleEntries; try { - moduleEntries = getModuleEntries(dependencies.toArray(new ImportDeclaration[dependencies.size()]), null); + moduleEntries = getModuleEntries(null, dependencies.toArray(new ImportDeclaration[dependencies.size()]), null, false); } catch (ImportFailureException e) { throw new InternalCompilerError(e); } @@ -191,8 +206,6 @@ public class ModuleRepository { } public synchronized void dispose() { - if (listeners != null) - listeners.clear(); listeners = null; stopListening(); source = null; @@ -227,6 +240,10 @@ public class ModuleRepository { return getModule(moduleName, null); } + public void update(String moduleName) { + getModuleEntry(moduleName, null).notifyAboutUpdate(); + } + public Failable getRuntimeModule(String moduleName, UpdateListener listener) { return getModuleEntry(moduleName, listener).getRuntimeModule(); } @@ -254,25 +271,35 @@ public class ModuleRepository { } private THashMap getModuleEntries( + CompilationContext compilationContext, ImportDeclaration[] imports, - UpdateListener listener) throws ImportFailureException { + UpdateListener listener, + boolean robustly) throws ImportFailureException { THashMap result = new THashMap(); Collection failures = null; - THashSet originalImports = new THashSet(); + TObjectLongHashMap originalImports = new TObjectLongHashMap(); ArrayList stack = new ArrayList(imports.length); for(ImportDeclaration import_ : imports) { stack.add(import_); - originalImports.add(import_.moduleName); + originalImports.put(import_.moduleName, import_.location); } while(!stack.isEmpty()) { ImportDeclaration import_ = stack.remove(stack.size()-1); if(!result.containsKey(import_.moduleName)) { - ModuleEntry entry = getModuleEntry(import_.moduleName, originalImports.contains(import_.moduleName) ? listener : null); + boolean originalImport = originalImports.contains(import_.moduleName); + ModuleEntry entry = getModuleEntry(import_.moduleName, originalImport ? listener : null); Failable compilationResult = entry.compilationResult; if(compilationResult.didSucceed()) { result.put(import_.moduleName, entry); stack.addAll(compilationResult.getResult().getDependencies()); + if(originalImport) { + String deprecation = compilationResult.getResult().getDeprecation(); + if(deprecation != null && compilationContext != null) { + long location = originalImport ? originalImports.get(import_.moduleName) : Locations.NO_LOCATION; + compilationContext.errorLog.logWarning(location, "Deprecated module " + import_.moduleName + (deprecation.isEmpty() ? "." : ": " + deprecation)); + } + } } else { if(failures == null) @@ -285,7 +312,7 @@ public class ModuleRepository { } } - if(failures != null) + if(failures != null && !robustly) throw new ImportFailureException(failures); return result; @@ -318,11 +345,32 @@ public class ModuleRepository { public Environment createEnvironment( ImportDeclaration[] imports, UpdateListener listener) throws ImportFailureException { - THashMap entries = getModuleEntries(imports, listener); + return createEnvironment(null, imports, listener); + } + + public Environment createEnvironment( + CompilationContext compilationContext, + ImportDeclaration[] imports, + UpdateListener listener) throws ImportFailureException { + THashMap entries = getModuleEntries(compilationContext, imports, listener, false); THashMap moduleMap = mapEntriesToModules(entries); return createEnvironment(moduleMap, imports); } + public Environment createEnvironmentRobustly( + CompilationContext compilationContext, + ImportDeclaration[] imports, + UpdateListener listener) { + try { + THashMap entries = getModuleEntries(compilationContext, imports, listener, true); + THashMap moduleMap = mapEntriesToModules(entries); + return createEnvironment(moduleMap, imports); + } catch(ImportFailureException e) { + // Should not happen because of robust flag + return EmptyEnvironment.INSTANCE; + } + } + public Environment createEnvironment( EnvironmentSpecification specification, UpdateListener listener) throws ImportFailureException { @@ -334,6 +382,10 @@ public class ModuleRepository { return createRuntimeEnvironment(environmentSpecification, parentClassLoader, null); } + public RuntimeEnvironment createRuntimeEnvironment(EnvironmentSpecification environmentSpecification) throws ImportFailureException { + return createRuntimeEnvironment(environmentSpecification, getClass().getClassLoader()); + } + public RuntimeEnvironment createRuntimeEnvironment( EnvironmentSpecification environmentSpecification, ClassLoader parentClassLoader, @@ -348,14 +400,15 @@ public class ModuleRepository { ImportDeclaration[] imports, ClassLoader parentClassLoader, UpdateListener listener) throws ImportFailureException { - THashMap entries = getModuleEntries(imports, listener); + THashMap entries = getModuleEntries(null, imports, listener, false); THashMap moduleMap = mapEntriesToModules(entries); Environment environment = createEnvironment(moduleMap, imports); THashMap runtimeModuleMap = mapEntriesToRuntimeModules(entries); return new RuntimeEnvironmentImpl(environment, parentClassLoader, runtimeModuleMap); } - private static Environment createEnvironment(THashMap moduleMap, + private static Environment createEnvironment( + THashMap moduleMap, ImportDeclaration[] imports) { NamespaceSpec spec = new NamespaceSpec(); for(ImportDeclaration import_ : imports) @@ -450,18 +503,24 @@ public class ModuleRepository { return documentation; } + /** + * Flush clears module repository cache completely. It should not be called in + * normal operation, but may be useful during testing for clearing repositories + * that are statically defined. + */ public void flush() { if (parentRepository != null) parentRepository.flush(); - if (moduleCache != null) { - for (ModuleEntry entry : moduleCache.values()) { + if (moduleCache != null) + for (ModuleEntry entry : moduleCache.values()) entry.dispose(); - } - moduleCache.clear(); - } - moduleCache = null; + moduleCache = new ConcurrentHashMap(); } + /** + * Gets the map of all modules that have been currently compiled successfully. + * Not that the method does not return all possible modules in the source repository. + */ public Map getModules() { Map result = new HashMap<>(moduleCache.size()); for (Map.Entry entry : moduleCache.entrySet()) {