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;
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;
import org.simantics.scl.compiler.types.Types;
import gnu.trove.map.hash.THashMap;
+import gnu.trove.map.hash.TObjectLongHashMap;
import gnu.trove.procedure.TObjectObjectProcedure;
import gnu.trove.set.hash.THashSet;
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;
}
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())
+ for(UpdateListener l : listenersCopy)
System.out.println(" " + 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);
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);
}
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 {
return runtimeModule;
}
- public void dispose() {
- listeners.clear();
+ public synchronized void dispose() {
listeners = null;
+ stopListening();
source = null;
compilationResult = null;
- runtimeModule.getResult().dispose();
+ if (runtimeModule != null) {
+ if (runtimeModule.didSucceed())
+ runtimeModule.getResult().dispose();
+ }
runtimeModule = null;
}
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
}
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)
}
}
- if(failures != null)
+ if(failures != null && !robustly)
throw new ImportFailureException(failures);
return result;
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 {
return createRuntimeEnvironment(environmentSpecification, parentClassLoader, null);
}
+ public RuntimeEnvironment createRuntimeEnvironment(EnvironmentSpecification environmentSpecification) throws ImportFailureException {
+ return createRuntimeEnvironment(environmentSpecification, getClass().getClassLoader());
+ }
+
public RuntimeEnvironment createRuntimeEnvironment(
EnvironmentSpecification environmentSpecification,
ClassLoader parentClassLoader,
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)
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()) {
public void setAdvisor(ModuleCompilationOptionsAdvisor advisor) {
this.advisor = advisor;
}
-\r
+
}
\ No newline at end of file