]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/module/repository/ModuleRepository.java
Method for manually recompling a SCL Module
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / module / repository / ModuleRepository.java
index 86b1ba7663d472b722bd313136e019f7e5475a6a..23491235ce3289c46fd254009cde5963774374d6 100644 (file)
@@ -4,12 +4,13 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.WeakHashMap;
 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;
@@ -20,10 +21,12 @@ 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;
 import org.simantics.scl.compiler.module.options.ModuleCompilationOptionsAdvisor;
+import org.simantics.scl.compiler.module.repository.UpdateListener.Observable;
 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
 import org.simantics.scl.compiler.runtime.RuntimeEnvironmentImpl;
 import org.simantics.scl.compiler.runtime.RuntimeModule;
@@ -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<String, ModuleEntry> moduleCache = new ConcurrentHashMap<String, ModuleEntry>();
@@ -66,10 +75,11 @@ public class ModuleRepository {
     private static void finishModuleCompilation(String moduleName) {
         PENDING_MODULES.get().remove(moduleName);
     }
-    
-    private class ModuleEntry implements UpdateListener {
+            
+    private class ModuleEntry extends UpdateListener implements Observable {
         final String moduleName;
-        WeakHashMap<UpdateListener,Object> listeners = new WeakHashMap<UpdateListener,Object>();
+        THashSet<UpdateListener> listeners = new THashSet<UpdateListener>(); // listeners == null is used as a marker that this entry is disposed
+                                                                             // should be handled only inside synchronized code
         
         ModuleSource source;
         Failable<Module> compilationResult;
@@ -80,30 +90,49 @@ public class ModuleRepository {
         }
         
         synchronized void addListener(UpdateListener listener) {
-            if(listener != null)
-                listeners.put(listener, null);
+            if(listener == null || listeners == null)
+                return;
+            listeners.add(listener);
+            listener.addObservable(this);
+        }
+
+        public synchronized void removeListener(UpdateListener listener) {
+            if (listeners == null)
+                return;
+            listeners.remove(listener);
         }
         
         @Override
         public void notifyAboutUpdate() {
-            if (listeners == null)
-                return;
+            // There is a chance that another observable calls notifyAboutUpdate() before stopListening has been completed,
+            // but notifyAboutUpdate(ArrayList<UpdateListener>) lets only one thread to do the notification of dependencies
+            // by clearing listeners field.
+            stopListening();
             ArrayList<UpdateListener> externalListeners = new ArrayList<UpdateListener>();
             notifyAboutUpdate(externalListeners);
             for(UpdateListener listener : externalListeners)
                 listener.notifyAboutUpdate();
         }
 
-        synchronized void notifyAboutUpdate(ArrayList<UpdateListener> externalListeners) {
+        void notifyAboutUpdate(ArrayList<UpdateListener> externalListeners) {
+            THashSet<UpdateListener> 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.keySet())
-                        System.out.println("    " + l);
+                    LOGGER.info("Invalidate " + moduleName);
+                    for(UpdateListener l : listenersCopy)
+                        LOGGER.info("    " + l);
                 }
-                for(UpdateListener l : listeners.keySet())
-                    if(l instanceof ModuleEntry)
+                for(UpdateListener l : listenersCopy)
+                    if(!l.stopListening())
+                        ;
+                    else if(l instanceof ModuleEntry)
                         ((ModuleEntry)l).notifyAboutUpdate(externalListeners);
                     else
                         externalListeners.add(l);
@@ -117,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);
@@ -145,7 +174,7 @@ public class ModuleRepository {
                         Collection<ImportDeclaration> dependencies = module.getDependencies();
                         THashMap<String, ModuleEntry> 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);
                         }
@@ -159,7 +188,7 @@ public class ModuleRepository {
                         if(parentModule != null)
                             parentModules.add(parentModule);
                     }*/
-                    RuntimeModule rm = new RuntimeModule(module, parentModules, source.getClassLoader());
+                    RuntimeModule rm = new RuntimeModule(module, parentModules, module.getParentClassLoader());
                     ModuleInitializer initializer = module.getModuleInitializer();
                     if(initializer != null)
                         try {
@@ -176,10 +205,9 @@ public class ModuleRepository {
             return runtimeModule;
         }
 
-        public void dispose() {
-            if (listeners != null)
-                listeners.clear();
+        public synchronized void dispose() {
             listeners = null;
+            stopListening();
             source = null;
             compilationResult = null;
             if (runtimeModule != null) {
@@ -212,6 +240,10 @@ public class ModuleRepository {
         return getModule(moduleName, null);
     }
     
+    public void update(String moduleName) {
+        getModuleEntry(moduleName, null).notifyAboutUpdate();
+    }
+    
     public Failable<RuntimeModule> getRuntimeModule(String moduleName, UpdateListener listener) {
         return getModuleEntry(moduleName, listener).getRuntimeModule();
     }
@@ -219,7 +251,7 @@ public class ModuleRepository {
     public Failable<RuntimeModule> getRuntimeModule(String moduleName) {
         return getRuntimeModule(moduleName, null);
     }
-    
+
     private ModuleEntry getModuleEntry(String moduleName, UpdateListener listener) {
         /* It is deliberate that the following code does not try to prevent
          * simultaneous compilation of the same module. This is because in
@@ -239,22 +271,35 @@ public class ModuleRepository {
     }
     
     private THashMap<String, ModuleEntry> getModuleEntries(
+            CompilationContext compilationContext,
             ImportDeclaration[] imports,
-            UpdateListener listener) throws ImportFailureException {
+            UpdateListener listener,
+            boolean robustly) throws ImportFailureException {
         THashMap<String, ModuleEntry> result = new THashMap<String, ModuleEntry>();
         Collection<ImportFailure> failures = null;
         
+        TObjectLongHashMap<String> originalImports = new TObjectLongHashMap<String>(); 
         ArrayList<ImportDeclaration> stack = new ArrayList<ImportDeclaration>(imports.length);
-        for(ImportDeclaration import_ : imports)
+        for(ImportDeclaration import_ : imports) {
             stack.add(import_);
+            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, listener);
+                boolean originalImport = originalImports.contains(import_.moduleName);
+                ModuleEntry entry = getModuleEntry(import_.moduleName, originalImport ? listener : null);
                 Failable<Module> 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)
@@ -267,7 +312,7 @@ public class ModuleRepository {
             }
         }
         
-        if(failures != null)
+        if(failures != null && !robustly)
             throw new ImportFailureException(failures);
         
         return result;
@@ -300,11 +345,32 @@ public class ModuleRepository {
     public Environment createEnvironment(
             ImportDeclaration[] imports,
             UpdateListener listener) throws ImportFailureException {
-        THashMap<String, ModuleEntry> entries = getModuleEntries(imports, listener);
+        return createEnvironment(null, imports, listener);
+    }
+    
+    public Environment createEnvironment(
+            CompilationContext compilationContext,
+            ImportDeclaration[] imports,
+            UpdateListener listener) throws ImportFailureException {
+        THashMap<String, ModuleEntry> entries = getModuleEntries(compilationContext, imports, listener, false);
         THashMap<String, Module> moduleMap = mapEntriesToModules(entries);
         return createEnvironment(moduleMap, imports);
     }
     
+    public Environment createEnvironmentRobustly(
+            CompilationContext compilationContext,
+            ImportDeclaration[] imports,
+            UpdateListener listener) {
+        try {
+            THashMap<String, ModuleEntry> entries = getModuleEntries(compilationContext, imports, listener, true);
+            THashMap<String, Module> 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 {
@@ -316,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,
@@ -330,14 +400,15 @@ public class ModuleRepository {
             ImportDeclaration[] imports,
             ClassLoader parentClassLoader,
             UpdateListener listener) throws ImportFailureException {
-        THashMap<String, ModuleEntry> entries = getModuleEntries(imports, listener);
+        THashMap<String, ModuleEntry> entries = getModuleEntries(null, imports, listener, false);
         THashMap<String, Module> moduleMap = mapEntriesToModules(entries);
         Environment environment = createEnvironment(moduleMap, imports);
         THashMap<String, RuntimeModule> runtimeModuleMap = mapEntriesToRuntimeModules(entries);
         return new RuntimeEnvironmentImpl(environment, parentClassLoader, runtimeModuleMap);
     }
     
-    private static Environment createEnvironment(THashMap<String, Module> moduleMap, 
+    private static Environment createEnvironment(
+            THashMap<String, Module> moduleMap, 
             ImportDeclaration[] imports) {
         NamespaceSpec spec = new NamespaceSpec();
         for(ImportDeclaration import_ : imports)
@@ -432,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<String, ModuleEntry>();
     }
 
+    /**
+     * 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<String, Module> getModules() {
         Map<String, Module> result = new HashMap<>(moduleCache.size()); 
         for (Map.Entry<String, ModuleEntry> entry : moduleCache.entrySet()) {
@@ -462,6 +539,6 @@ public class ModuleRepository {
     public void setAdvisor(ModuleCompilationOptionsAdvisor advisor) {
         this.advisor = advisor;
     }
-\r
+
 }
  
\ No newline at end of file