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