public static IStorageSchema AddStorageExtension(string tslSrcDir, string rootNamespace) { // assemblyName serves as a global-unique identifier for a storage extension. var assemblyName = Guid.NewGuid().ToString("N"); var projDir = GetTempDirectory(); var buildDir = GetTempDirectory(); var outDir = GetTempDirectory(); var ext = new StorageExtensionRecord(s_currentCellTypeOffset, rootNamespace, assemblyName); IStorageSchema schema; CreateCSProj(projDir, assemblyName); CodeGen(tslSrcDir, rootNamespace, ext.CellTypeOffset, projDir); Build(projDir, outDir, assemblyName); schema = Load(ext, true); SaveMetadata(); AggregatedStorageSchema.s_singleton.Update(); return(schema); }
private static IStorageSchema Load(StorageExtensionRecord ext, bool primary) { // capture states var ctoffset = ext.CellTypeOffset; var ctmap = new Dictionary <string, int>(s_CellTypeIDs); var gcops = new List <IGenericCellOperations>(s_GenericCellOperations); var schemas = new List <IStorageSchema>(s_StorageSchemas); var iditv = new List <int>(s_IDIntervals); var pshadow = ShadowCopy(Path.Combine(PathHelper.StorageSlot(primary), ext.AssemblyName)); var apath = Path.Combine(pshadow, ext.AssemblyName + ".dll"); var asm = Assembly.LoadFrom(apath); Log.WriteLine(LogLevel.Debug, $"{nameof(CompositeStorage)}: assembly from {apath} is loaded."); new[] { "Current Storage Info:", $"#Loaded extensions: {s_Extensions.Count}", $"#IDIntervals: : {iditv.Count}", $"#CellTypeIDs:{ctmap.Count}", $"#StorageSchema:{schemas.Count}", $"#GenericCellOperations:{gcops.Count}" } .Each(_ => Log.WriteLine(LogLevel.Debug, $"{nameof(CompositeStorage)}: {{0}}", _)); var schema = AssemblyUtility.GetAllClassInstances <IStorageSchema>(assembly: asm).FirstOrDefault(); var cellOps = AssemblyUtility.GetAllClassInstances <IGenericCellOperations>(assembly: asm).FirstOrDefault(); if (schema == null || cellOps == null) { throw new AsmLoadException("Not a TSL extension."); } var cellDescs = schema.CellDescriptors.ToList(); schemas.Add(schema); gcops.Add(cellOps); int maxoffset = ctoffset; foreach (var cellDesc in cellDescs) { if (ctmap.TryGetValue(cellDesc.TypeName, out var existing_tid)) { Log.WriteLine(LogLevel.Info, $"{nameof(CompositeStorage)}: overriding type {cellDesc.TypeName}: TypeId {existing_tid}->{cellDesc.CellType}"); } ctmap[cellDesc.TypeName] = cellDesc.CellType; if (cellDesc.CellType < ctoffset) { throw new AsmLoadException("New cell type id conflicts existing type space"); } maxoffset = Math.Max(maxoffset, cellDesc.CellType); } ctoffset += cellDescs.Count + 1; // extra typeid for undefined/reserved cell type if (ctoffset != maxoffset + 1) { throw new AsmLoadException("The whole type id space is not compact"); } iditv.Add(ctoffset); if (!iditv.OrderBy(_ => _).SequenceEqual(iditv)) { throw new AsmLoadException("intervals do not grow monotonically"); } // commit states lock (s_lock) { s_currentCellTypeOffset = ctoffset; s_CellTypeIDs = ctmap; s_GenericCellOperations = gcops; s_StorageSchemas = schemas; s_IDIntervals = iditv; s_Extensions.Add(ext); Log.WriteLine(LogLevel.Info, $"{nameof(CompositeStorage)}: storage extension '{ext.RootNamespace}' loaded"); return(schema); } }