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=0f0b8542049a53c7f8eea2667ba15c40789f0f7b;hp=1e43e755be4d79f5ce5933b157765e98203035bf;hb=7444d4b3a2f3c25fac462d8a168898656dafd52e;hpb=593a8f75d9dbc363234002dc500c346afbeba040 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 1e43e755b..0f0b85420 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 @@ -1,467 +1,525 @@ -package org.simantics.scl.compiler.module.repository; - -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.elaboration.modules.SCLValue; -import org.simantics.scl.compiler.environment.ConcreteEnvironment; -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.environment.filter.NamespaceFilter; -import org.simantics.scl.compiler.environment.filter.NamespaceFilters; -import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification; -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.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.runtime.RuntimeEnvironment; -import org.simantics.scl.compiler.runtime.RuntimeEnvironmentImpl; -import org.simantics.scl.compiler.runtime.RuntimeModule; -import org.simantics.scl.compiler.runtime.RuntimeModuleMap; -import org.simantics.scl.compiler.source.ModuleSource; -import org.simantics.scl.compiler.source.repository.ModuleSourceRepository; -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 gnu.trove.map.hash.THashMap; -import gnu.trove.procedure.TObjectObjectProcedure; -import gnu.trove.set.hash.THashSet; - -/** - * Manages compilation and caching of SCL modules. - * - * @author Hannu Niemistö - */ -public class ModuleRepository { - private final ModuleRepository parentRepository; - private final ModuleSourceRepository sourceRepository; - private ConcurrentHashMap moduleCache = new ConcurrentHashMap(); - - private static final ThreadLocal> PENDING_MODULES = new ThreadLocal>(); - - private ModuleCompilationOptionsAdvisor advisor = null; - - private static void beginModuleCompilation(String moduleName) { - THashSet set = PENDING_MODULES.get(); - if(set == null) { - set = new THashSet(); - PENDING_MODULES.set(set); - } - if(!set.add(moduleName)) - throw new IllegalArgumentException("Cyclic module dependency detected at " + moduleName + "."); - } - - private static void finishModuleCompilation(String moduleName) { - PENDING_MODULES.get().remove(moduleName); - } - - private class ModuleEntry implements UpdateListener { - final String moduleName; - WeakHashMap listeners = new WeakHashMap(); - - ModuleSource source; - Failable compilationResult; - Failable runtimeModule; // created lazily - - public ModuleEntry(String moduleName) { - this.moduleName = moduleName; - } - - synchronized void addListener(UpdateListener listener) { - if(listener != null) - listeners.put(listener, null); - } - - @Override - public void notifyAboutUpdate() { - if (listeners == null) - return; - ArrayList externalListeners = new ArrayList(); - notifyAboutUpdate(externalListeners); - for(UpdateListener listener : externalListeners) - listener.notifyAboutUpdate(); - } - - synchronized void notifyAboutUpdate(ArrayList externalListeners) { - 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); - } - for(UpdateListener l : listeners.keySet()) - if(l instanceof ModuleEntry) - ((ModuleEntry)l).notifyAboutUpdate(externalListeners); - else - externalListeners.add(l); - } - } - - private ModuleEntry initModuleEntryAndAddListener(UpdateListener listener) { - source = sourceRepository.getModuleSource(moduleName, this); - - if(source == null) - compilationResult = DoesNotExist.getInstance(); - else { - if(SCLCompilerConfiguration.TRACE_MODULE_UPDATE) - System.out.println("Compile " + source); - beginModuleCompilation(moduleName); - compilationResult = source.compileModule(ModuleRepository.this, this, advisor == null ? null : advisor.getOptions(moduleName)); - finishModuleCompilation(moduleName); - } - - ModuleEntry oldEntry = moduleCache.putIfAbsent(moduleName, this); - if(oldEntry != null) { - oldEntry.addListener(listener); - return oldEntry; - } - - addListener(listener); - return this; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public synchronized Failable getRuntimeModule() { - if(runtimeModule == null) { - if(compilationResult.didSucceed()) { - Module module = compilationResult.getResult(); - RuntimeModuleMap parentModules = new RuntimeModuleMap(); - if(!moduleName.equals(Types.BUILTIN)) { - parentModules.add(ModuleRepository.this.getRuntimeModule(Types.BUILTIN) - .getResult()); - Collection dependencies = module.getDependencies(); - THashMap moduleEntries; - try { - moduleEntries = getModuleEntries(dependencies.toArray(new ImportDeclaration[dependencies.size()]), null); - } catch (ImportFailureException e) { - throw new InternalCompilerError(e); - } - for(RuntimeModule m : mapEntriesToRuntimeModules(moduleEntries).values()) - parentModules.add(m); - } - /*for(ImportDeclaration importAst : module.getDependencies()) { - RuntimeModule parentModule = - ModuleRepository.this.getRuntimeModule(importAst.moduleName) - .getResult(); - if(parentModule != null) - parentModules.add(parentModule); - }*/ - RuntimeModule rm = new RuntimeModule(module, parentModules, source.getClassLoader()); - ModuleInitializer initializer = module.getModuleInitializer(); - if(initializer != null) - try { - initializer.initializeModule(rm.getMutableClassLoader().getClassLoader()); - } catch (Exception e) { - compilationResult = new Failure(new CompilationError[] {new CompilationError("Initialization of module " + moduleName + " failed: " + e.getMessage())}); - e.printStackTrace(); - } - runtimeModule = new Success(rm); - } - else - runtimeModule = (Failable)(Failable)compilationResult; - } - return runtimeModule; - } - - public void dispose() { - if (listeners != null) - listeners.clear(); - listeners = null; - source = null; - compilationResult = null; - if (runtimeModule != null) { - if (runtimeModule.didSucceed()) - runtimeModule.getResult().dispose(); - } - runtimeModule = null; - } - - @Override - public String toString() { - return "ModuleEntry@" + moduleName + "@" + hashCode(); - } - } - - public ModuleRepository(ModuleRepository parentRepository, ModuleSourceRepository sourceRepository) { - this.parentRepository = parentRepository; - this.sourceRepository = sourceRepository; - } - - public ModuleRepository(ModuleSourceRepository sourceRepository) { - this(null, sourceRepository); - } - - public Failable getModule(String moduleName, UpdateListener listener) { - return getModuleEntry(moduleName, listener).compilationResult; - } - - public Failable getModule(String moduleName) { - return getModule(moduleName, null); - } - - public Failable getRuntimeModule(String moduleName, UpdateListener listener) { - return getModuleEntry(moduleName, listener).getRuntimeModule(); - } - - public Failable 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 - * some situations only certain thread trying compilation can succeed - * in it. - */ - ModuleEntry entry = moduleCache.get(moduleName); - if(entry == null) - entry = new ModuleEntry(moduleName).initModuleEntryAndAddListener(listener); - else - entry.addListener(listener); - - if(entry.compilationResult == DoesNotExist.INSTANCE && parentRepository != null) - return parentRepository.getModuleEntry(moduleName, listener); - else - return entry; - } - - private THashMap getModuleEntries( - ImportDeclaration[] imports, - UpdateListener listener) throws ImportFailureException { - THashMap result = new THashMap(); - Collection failures = null; - - ArrayList stack = new ArrayList(imports.length); - for(ImportDeclaration import_ : imports) - stack.add(import_); - while(!stack.isEmpty()) { - ImportDeclaration import_ = stack.remove(stack.size()-1); - if(!result.containsKey(import_.moduleName)) { - ModuleEntry entry = getModuleEntry(import_.moduleName, listener); - Failable compilationResult = entry.compilationResult; - if(compilationResult.didSucceed()) { - result.put(import_.moduleName, entry); - stack.addAll(compilationResult.getResult().getDependencies()); - } - else { - if(failures == null) - failures = new ArrayList(2); - failures.add(new ImportFailure(import_.location, import_.moduleName, - compilationResult == DoesNotExist.INSTANCE - ? ImportFailure.MODULE_DOES_NOT_EXIST_REASON - : ((Failure)compilationResult).errors)); - } - } - } - - if(failures != null) - throw new ImportFailureException(failures); - - return result; - } - - private static THashMap mapEntriesToModules(THashMap entries) { - final THashMap result = new THashMap(entries.size()); - entries.forEachEntry(new TObjectObjectProcedure() { - @Override - public boolean execute(String a, ModuleEntry b) { - result.put(a, b.compilationResult.getResult()); - return true; - } - }); - return result; - } - - private static THashMap mapEntriesToRuntimeModules(THashMap entries) { - final THashMap result = new THashMap(entries.size()); - entries.forEachEntry(new TObjectObjectProcedure() { - @Override - public boolean execute(String a, ModuleEntry b) { - result.put(a, b.getRuntimeModule().getResult()); - return true; - } - }); - return result; - } - - public Environment createEnvironment( - ImportDeclaration[] imports, - UpdateListener listener) throws ImportFailureException { - THashMap entries = getModuleEntries(imports, listener); - THashMap moduleMap = mapEntriesToModules(entries); - return createEnvironment(moduleMap, imports); - } - - public Environment createEnvironment( - EnvironmentSpecification specification, - UpdateListener listener) throws ImportFailureException { - return createEnvironment(specification.imports.toArray(new ImportDeclaration[specification.imports.size()]), listener); - } - - public RuntimeEnvironment createRuntimeEnvironment( - EnvironmentSpecification environmentSpecification, ClassLoader parentClassLoader) throws ImportFailureException { - return createRuntimeEnvironment(environmentSpecification, parentClassLoader, null); - } - - public RuntimeEnvironment createRuntimeEnvironment( - EnvironmentSpecification environmentSpecification, - ClassLoader parentClassLoader, - UpdateListener listener) throws ImportFailureException { - return createRuntimeEnvironment( - environmentSpecification.imports.toArray(new ImportDeclaration[environmentSpecification.imports.size()]), - parentClassLoader, - listener); - } - - public RuntimeEnvironment createRuntimeEnvironment( - ImportDeclaration[] imports, - ClassLoader parentClassLoader, - UpdateListener listener) throws ImportFailureException { - THashMap entries = getModuleEntries(imports, listener); - 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, - ImportDeclaration[] imports) { - NamespaceSpec spec = new NamespaceSpec(); - for(ImportDeclaration import_ : imports) - if(import_.localName != null) - addToNamespace(moduleMap, spec, import_.moduleName, import_.localName, - NamespaceFilters.createFromSpec(import_.spec)); - - return new ConcreteEnvironment(moduleMap, spec.toNamespace()); - } - - private static void addToNamespace(THashMap moduleMap, - NamespaceSpec namespace, String moduleName, String localName, - NamespaceFilter filter) { - if(localName.isEmpty()) - addToNamespace(moduleMap, namespace, moduleName, filter); - else - addToNamespace(moduleMap, namespace.getNamespace(localName), moduleName, filter); - } - - private static void addToNamespace(THashMap moduleMap, - NamespaceSpec namespace, String moduleName, NamespaceFilter filter) { - ModuleImport moduleImport = namespace.moduleMap.get(moduleName); - if(moduleImport == null) { - Module module = moduleMap.get(moduleName); - namespace.moduleMap.put(moduleName, new ModuleImport(module, filter)); - for(ImportDeclaration import_ : module.getDependencies()) - if(import_.localName != null) { - NamespaceFilter localFilter = NamespaceFilters.createFromSpec(import_.spec); - if(import_.localName.equals("")) - localFilter = NamespaceFilters.intersection(filter, localFilter); - addToNamespace(moduleMap, namespace, import_.moduleName, import_.localName, localFilter); - } - } - else if(!filter.isSubsetOf(moduleImport.filter)) { - moduleImport.filter = NamespaceFilters.union(moduleImport.filter, filter); - for(ImportDeclaration import_ : moduleImport.module.getDependencies()) - // We have to recheck only modules imported to this namespace - if("".equals(import_.localName)) { - NamespaceFilter localFilter = NamespaceFilters.createFromSpec(import_.spec); - localFilter = NamespaceFilters.intersection(filter, localFilter); - addToNamespace(moduleMap, namespace, import_.moduleName, import_.localName, localFilter); - } - } - } - - public Object getValue(String moduleName, String valueName) throws ValueNotFound { - Failable module = getRuntimeModule(moduleName); - if(module.didSucceed()) - return module.getResult().getValue(valueName); - else if(module == DoesNotExist.INSTANCE) - throw new ValueNotFound("Didn't find module " + moduleName); - else - throw new ValueNotFound(((Failure)module).toString()); - } - - public Object getValue(String fullValueName) throws ValueNotFound { - int p = fullValueName.lastIndexOf('/'); - if(p < 0) - throw new ValueNotFound(fullValueName + " is not a valid full value name."); - return getValue(fullValueName.substring(0, p), fullValueName.substring(p+1)); - } - - public SCLValue getValueRef(String moduleName, String valueName) throws ValueNotFound { - Failable module = getModule(moduleName); - if(module.didSucceed()) { - SCLValue value = module.getResult().getValue(valueName); - if(value == null) - throw new ValueNotFound("Module " + moduleName + " does not contain value " + valueName + "."); - return value; - } - else if(module == DoesNotExist.INSTANCE) - throw new ValueNotFound("Didn't find module " + moduleName); - else - throw new ValueNotFound(((Failure)module).toString()); - } - - public SCLValue getValueRef(String fullValueName) throws ValueNotFound { - int p = fullValueName.lastIndexOf('/'); - if(p < 0) - throw new ValueNotFound(fullValueName + " is not a valid full value name."); - return getValueRef(fullValueName.substring(0, p), fullValueName.substring(p+1)); - } - - public ModuleSourceRepository getSourceRepository() { - return sourceRepository; - } - - public String getDocumentation(String documentationName) { - String documentation = sourceRepository.getDocumentation(documentationName); - if(documentation == null && parentRepository != null) - return parentRepository.getDocumentation(documentationName); - return documentation; - } - - public void flush() { - if (parentRepository != null) - parentRepository.flush(); - if (moduleCache != null) { - for (ModuleEntry entry : moduleCache.values()) { - entry.dispose(); - } - moduleCache.clear(); - } - moduleCache = null; - } - - public Map getModules() { - Map result = new HashMap<>(moduleCache.size()); - for (Map.Entry entry : moduleCache.entrySet()) { - ModuleEntry moduleEntry = entry.getValue(); - if (moduleEntry.compilationResult.didSucceed()) { - result.put(entry.getKey(), moduleEntry.compilationResult.getResult()); - } - } - return result; - } - - public ModuleCompilationOptionsAdvisor getAdvisor() { - return advisor; - } - - public void setAdvisor(ModuleCompilationOptionsAdvisor advisor) { - this.advisor = advisor; - } - -} +package org.simantics.scl.compiler.module.repository; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +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; +import org.simantics.scl.compiler.environment.filter.NamespaceFilter; +import org.simantics.scl.compiler.environment.filter.NamespaceFilters; +import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification; +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; +import org.simantics.scl.compiler.runtime.RuntimeModuleMap; +import org.simantics.scl.compiler.source.ModuleSource; +import org.simantics.scl.compiler.source.repository.ModuleSourceRepository; +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 gnu.trove.map.hash.THashMap; +import gnu.trove.map.hash.TObjectLongHashMap; +import gnu.trove.procedure.TObjectObjectProcedure; +import gnu.trove.set.hash.THashSet; + +/** + * Manages compilation and caching of SCL modules. + * + * @author Hannu Niemistö + */ +public class ModuleRepository { + private final ModuleRepository parentRepository; + private final ModuleSourceRepository sourceRepository; + private ConcurrentHashMap moduleCache = new ConcurrentHashMap(); + + private static final ThreadLocal> PENDING_MODULES = new ThreadLocal>(); + + private ModuleCompilationOptionsAdvisor advisor = null; + + private static void beginModuleCompilation(String moduleName) { + THashSet set = PENDING_MODULES.get(); + if(set == null) { + set = new THashSet(); + PENDING_MODULES.set(set); + } + if(!set.add(moduleName)) + throw new IllegalArgumentException("Cyclic module dependency detected at " + moduleName + "."); + } + + 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(); + + ModuleSource source; + Failable compilationResult; + Failable runtimeModule; // created lazily + + public ModuleEntry(String moduleName) { + this.moduleName = moduleName; + } + + synchronized void addListener(UpdateListener listener) { + 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() { + ArrayList externalListeners = new ArrayList(); + notifyAboutUpdate(externalListeners); + for(UpdateListener listener : externalListeners) + listener.notifyAboutUpdate(); + } + + synchronized void notifyAboutUpdate(ArrayList externalListeners) { + stopListening(); + if (listeners == null) + return; + 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); + } + THashSet listenersCopy = listeners; + listeners = null; + for(UpdateListener l : listenersCopy) + l.stopListening(); + for(UpdateListener l : listenersCopy) + if(l instanceof ModuleEntry) + ((ModuleEntry)l).notifyAboutUpdate(externalListeners); + else { + externalListeners.add(l); + } + } + } + + private ModuleEntry initModuleEntryAndAddListener(UpdateListener listener) { + source = sourceRepository.getModuleSource(moduleName, this); + + if(source == null) + compilationResult = DoesNotExist.getInstance(); + else { + if(SCLCompilerConfiguration.TRACE_MODULE_UPDATE) + System.out.println("Compile " + source); + beginModuleCompilation(moduleName); + compilationResult = source.compileModule(ModuleRepository.this, this, advisor == null ? null : advisor.getOptions(moduleName)); + finishModuleCompilation(moduleName); + } + + ModuleEntry oldEntry = moduleCache.putIfAbsent(moduleName, this); + if(oldEntry != null) { + oldEntry.addListener(listener); + return oldEntry; + } + + addListener(listener); + return this; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public synchronized Failable getRuntimeModule() { + if(runtimeModule == null) { + if(compilationResult.didSucceed()) { + Module module = compilationResult.getResult(); + RuntimeModuleMap parentModules = new RuntimeModuleMap(); + if(!moduleName.equals(Types.BUILTIN)) { + parentModules.add(ModuleRepository.this.getRuntimeModule(Types.BUILTIN) + .getResult()); + Collection dependencies = module.getDependencies(); + THashMap moduleEntries; + try { + moduleEntries = getModuleEntries(null, dependencies.toArray(new ImportDeclaration[dependencies.size()]), null, false); + } catch (ImportFailureException e) { + throw new InternalCompilerError(e); + } + for(RuntimeModule m : mapEntriesToRuntimeModules(moduleEntries).values()) + parentModules.add(m); + } + /*for(ImportDeclaration importAst : module.getDependencies()) { + RuntimeModule parentModule = + ModuleRepository.this.getRuntimeModule(importAst.moduleName) + .getResult(); + if(parentModule != null) + parentModules.add(parentModule); + }*/ + RuntimeModule rm = new RuntimeModule(module, parentModules, module.getParentClassLoader()); + ModuleInitializer initializer = module.getModuleInitializer(); + if(initializer != null) + try { + initializer.initializeModule(rm.getMutableClassLoader().getClassLoader()); + } catch (Exception e) { + compilationResult = new Failure(new CompilationError[] {new CompilationError("Initialization of module " + moduleName + " failed: " + e.getMessage())}); + e.printStackTrace(); + } + runtimeModule = new Success(rm); + } + else + runtimeModule = (Failable)(Failable)compilationResult; + } + return runtimeModule; + } + + public synchronized void dispose() { + if (listeners != null) + listeners.clear(); + listeners = null; + stopListening(); + source = null; + compilationResult = null; + if (runtimeModule != null) { + if (runtimeModule.didSucceed()) + runtimeModule.getResult().dispose(); + } + runtimeModule = null; + } + + @Override + public String toString() { + return "ModuleEntry@" + moduleName + "@" + hashCode(); + } + } + + public ModuleRepository(ModuleRepository parentRepository, ModuleSourceRepository sourceRepository) { + this.parentRepository = parentRepository; + this.sourceRepository = sourceRepository; + } + + public ModuleRepository(ModuleSourceRepository sourceRepository) { + this(null, sourceRepository); + } + + public Failable getModule(String moduleName, UpdateListener listener) { + return getModuleEntry(moduleName, listener).compilationResult; + } + + public Failable getModule(String moduleName) { + return getModule(moduleName, null); + } + + public Failable getRuntimeModule(String moduleName, UpdateListener listener) { + return getModuleEntry(moduleName, listener).getRuntimeModule(); + } + + public Failable 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 + * some situations only certain thread trying compilation can succeed + * in it. + */ + ModuleEntry entry = moduleCache.get(moduleName); + if(entry == null) + entry = new ModuleEntry(moduleName).initModuleEntryAndAddListener(listener); + else + entry.addListener(listener); + + if(entry.compilationResult == DoesNotExist.INSTANCE && parentRepository != null) + return parentRepository.getModuleEntry(moduleName, listener); + else + return entry; + } + + private THashMap getModuleEntries( + CompilationContext compilationContext, + ImportDeclaration[] imports, + UpdateListener listener, + boolean robustly) throws ImportFailureException { + THashMap result = new THashMap(); + Collection failures = null; + + TObjectLongHashMap originalImports = new TObjectLongHashMap(); + ArrayList stack = new ArrayList(imports.length); + 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)) { + 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) + failures = new ArrayList(2); + failures.add(new ImportFailure(import_.location, import_.moduleName, + compilationResult == DoesNotExist.INSTANCE + ? ImportFailure.MODULE_DOES_NOT_EXIST_REASON + : ((Failure)compilationResult).errors)); + } + } + } + + if(failures != null && !robustly) + throw new ImportFailureException(failures); + + return result; + } + + private static THashMap mapEntriesToModules(THashMap entries) { + final THashMap result = new THashMap(entries.size()); + entries.forEachEntry(new TObjectObjectProcedure() { + @Override + public boolean execute(String a, ModuleEntry b) { + result.put(a, b.compilationResult.getResult()); + return true; + } + }); + return result; + } + + private static THashMap mapEntriesToRuntimeModules(THashMap entries) { + final THashMap result = new THashMap(entries.size()); + entries.forEachEntry(new TObjectObjectProcedure() { + @Override + public boolean execute(String a, ModuleEntry b) { + result.put(a, b.getRuntimeModule().getResult()); + return true; + } + }); + return result; + } + + public Environment createEnvironment( + ImportDeclaration[] imports, + UpdateListener listener) throws ImportFailureException { + 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 { + return createEnvironment(specification.imports.toArray(new ImportDeclaration[specification.imports.size()]), listener); + } + + public RuntimeEnvironment createRuntimeEnvironment( + EnvironmentSpecification environmentSpecification, ClassLoader parentClassLoader) 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, + UpdateListener listener) throws ImportFailureException { + return createRuntimeEnvironment( + environmentSpecification.imports.toArray(new ImportDeclaration[environmentSpecification.imports.size()]), + parentClassLoader, + listener); + } + + public RuntimeEnvironment createRuntimeEnvironment( + ImportDeclaration[] imports, + ClassLoader parentClassLoader, + UpdateListener listener) throws ImportFailureException { + 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, + ImportDeclaration[] imports) { + NamespaceSpec spec = new NamespaceSpec(); + for(ImportDeclaration import_ : imports) + if(import_.localName != null) + addToNamespace(moduleMap, spec, import_.moduleName, import_.localName, + NamespaceFilters.createFromSpec(import_.spec)); + + return new ConcreteEnvironment(moduleMap, spec.toNamespace()); + } + + private static void addToNamespace(THashMap moduleMap, + NamespaceSpec namespace, String moduleName, String localName, + NamespaceFilter filter) { + if(localName.isEmpty()) + addToNamespace(moduleMap, namespace, moduleName, filter); + else + addToNamespace(moduleMap, namespace.getNamespace(localName), moduleName, filter); + } + + private static void addToNamespace(THashMap moduleMap, + NamespaceSpec namespace, String moduleName, NamespaceFilter filter) { + ModuleImport moduleImport = namespace.moduleMap.get(moduleName); + if(moduleImport == null) { + Module module = moduleMap.get(moduleName); + namespace.moduleMap.put(moduleName, new ModuleImport(module, filter)); + for(ImportDeclaration import_ : module.getDependencies()) + if(import_.localName != null) { + NamespaceFilter localFilter = NamespaceFilters.createFromSpec(import_.spec); + if(import_.localName.equals("")) + localFilter = NamespaceFilters.intersection(filter, localFilter); + addToNamespace(moduleMap, namespace, import_.moduleName, import_.localName, localFilter); + } + } + else if(!filter.isSubsetOf(moduleImport.filter)) { + moduleImport.filter = NamespaceFilters.union(moduleImport.filter, filter); + for(ImportDeclaration import_ : moduleImport.module.getDependencies()) + // We have to recheck only modules imported to this namespace + if("".equals(import_.localName)) { + NamespaceFilter localFilter = NamespaceFilters.createFromSpec(import_.spec); + localFilter = NamespaceFilters.intersection(filter, localFilter); + addToNamespace(moduleMap, namespace, import_.moduleName, import_.localName, localFilter); + } + } + } + + public Object getValue(String moduleName, String valueName) throws ValueNotFound { + Failable module = getRuntimeModule(moduleName); + if(module.didSucceed()) + return module.getResult().getValue(valueName); + else if(module == DoesNotExist.INSTANCE) + throw new ValueNotFound("Didn't find module " + moduleName); + else + throw new ValueNotFound(((Failure)module).toString()); + } + + public Object getValue(String fullValueName) throws ValueNotFound { + int p = fullValueName.lastIndexOf('/'); + if(p < 0) + throw new ValueNotFound(fullValueName + " is not a valid full value name."); + return getValue(fullValueName.substring(0, p), fullValueName.substring(p+1)); + } + + public SCLValue getValueRef(String moduleName, String valueName) throws ValueNotFound { + Failable module = getModule(moduleName); + if(module.didSucceed()) { + SCLValue value = module.getResult().getValue(valueName); + if(value == null) + throw new ValueNotFound("Module " + moduleName + " does not contain value " + valueName + "."); + return value; + } + else if(module == DoesNotExist.INSTANCE) + throw new ValueNotFound("Didn't find module " + moduleName); + else + throw new ValueNotFound(((Failure)module).toString()); + } + + public SCLValue getValueRef(String fullValueName) throws ValueNotFound { + int p = fullValueName.lastIndexOf('/'); + if(p < 0) + throw new ValueNotFound(fullValueName + " is not a valid full value name."); + return getValueRef(fullValueName.substring(0, p), fullValueName.substring(p+1)); + } + + public ModuleSourceRepository getSourceRepository() { + return sourceRepository; + } + + public String getDocumentation(String documentationName) { + String documentation = sourceRepository.getDocumentation(documentationName); + if(documentation == null && parentRepository != null) + return parentRepository.getDocumentation(documentationName); + return documentation; + } + + public void flush() { + if (parentRepository != null) + parentRepository.flush(); + if (moduleCache != null) { + for (ModuleEntry entry : moduleCache.values()) { + entry.dispose(); + } + moduleCache.clear(); + } + moduleCache = null; + } + + public Map getModules() { + Map result = new HashMap<>(moduleCache.size()); + for (Map.Entry entry : moduleCache.entrySet()) { + ModuleEntry moduleEntry = entry.getValue(); + if (moduleEntry.compilationResult.didSucceed()) { + result.put(entry.getKey(), moduleEntry.compilationResult.getResult()); + } + } + return result; + } + + public ModuleCompilationOptionsAdvisor getAdvisor() { + return advisor; + } + + public void setAdvisor(ModuleCompilationOptionsAdvisor advisor) { + this.advisor = advisor; + } + +} \ No newline at end of file