IObservable <CoalesceEntry> ReifyProject(IObservable <ProjectBytecode> bytecode, IObservable <BytecodeUpdated> bytecodeUpdated, CoalesceEntryCache cache, IObservable <IEnumerable <AbsoluteFilePath> > assets) { int idx = 0; var bytecodeUpdates = bytecodeUpdated.Select(m => m.ToCoalesceEntry(BytecodeUpdated.MessageType + (++idx))) .Publish() .RefCount(); var clearOldUpdates = bytecodeUpdates .Buffer(bytecode) .Select( oldUpdates => { var cachedUpdates = new List <CoalesceEntry>(); foreach (var oldUpdate in oldUpdates) { cachedUpdates.Add(new CoalesceEntry() { BlobData = Optional.None(), CoalesceKey = oldUpdate.CoalesceKey }); } return(cachedUpdates); }) .SelectMany(t => t); var reify = bytecode.WithLatestFromBuffered(assets, (bc, ass) => { var waitForDependencies = Task.WaitAll(new [] { bc.Dependencies.Select(d => cache.HasEntry(d.ToString())) .ToObservableEnumerable() .FirstAsync() .ToTask(), ass.Select(d => cache.HasEntry(d.ToString())) .ToObservableEnumerable() .FirstAsync() .ToTask() }, TimeSpan.FromSeconds(60)); if (waitForDependencies == false) { throw new TimeoutException("Failed to load all assets dependencies."); } try { return(new BytecodeGenerated( new ProjectBytecode( reify: new Lambda( Signature.Action(Variable.This), Enumerable.Empty <BindVariable>(), new[] { ExpressionConverter.BytecodeFromSimpleLambda(() => ObjectTagRegistry.Clear()), new CallLambda(bc.Reify, new ReadVariable(Variable.This)), }), metadata: bc.Metadata, dependencies: bc.Dependencies)) .ToCoalesceEntry(BytecodeGenerated.MessageType)); } catch (Exception) { return(new BytecodeUpdated( new Lambda( Signature.Action(), Enumerable.Empty <BindVariable>(), Enumerable.Empty <Statement>())) .ToCoalesceEntry("invalid-byte-code")); } }) .CatchAndRetry(TimeSpan.FromSeconds(15), e => { _logMessages.OnNext("Failed to refresh because: " + e.Message); }); return(Observable.Merge(clearOldUpdates, reify, bytecodeUpdates)); }
public ProjectPreview( IProjectLike project, IFileSystem shell, BuildOutputDirGenerator buildOutputDirGenerator, ProxyServer proxy) { Name = project.Name; var simulatorHost = ProjectProcess.SpawnAsync().Replay(1); var simulatorMessages = simulatorHost.Switch(p => p.Messages.RefCount()).Publish().RefCount(); var assemblyBuilt = simulatorMessages.TryParse(AssemblyBuilt.MessageType, AssemblyBuilt.ReadDataFrom); var bytecodeGenerated = simulatorMessages.TryParse(BytecodeGenerated.MessageType, BytecodeGenerated.ReadDataFrom); var bytecodeUpdated = simulatorMessages.TryParse(BytecodeUpdated.MessageType, BytecodeUpdated.ReadDataFrom); _project = project; _buildOutputDirGenerator = buildOutputDirGenerator; _proxy = proxy; _simulatorHost = simulatorHost; _assetsWatcher = new AssetsWatcher(shell, project.RootDirectory, Scheduler.Default); var bytecode = bytecodeGenerated.Select(msg => msg.Bytecode); _coalesceEntryCache = new CoalesceEntryCache(); var assets = project.BundleFiles.CombineLatest(project.FuseJsFiles, (a, b) => a.Concat(b)); var reifyMessages = ReifyProject(bytecode, bytecodeUpdated, _coalesceEntryCache, assets); var dependencyMessages = _assetsWatcher.UpdateChangedDependencies(bytecode.Select(bc => bc.Dependencies.ToImmutableHashSet())); var bundleFileMessages = _assetsWatcher.UpdateChangedBundleFiles(project.BundleFiles); var fuseJsFileMessages = _assetsWatcher.UpdateChangedFuseJsFiles(project.FuseJsFiles); _dispose = Observable.Merge( bundleFileMessages, dependencyMessages, fuseJsFileMessages, reifyMessages) .Subscribe(e => _coalesceEntryCache.Add(e)); var incommingMessages = new Subject <IBinaryMessage>(); var clientAdded = new Subject <string>(); var clientRemoved = new Subject <string>(); var socketServer = SocketServer.Start( port: 0, clientRun: (clientStream, endPoint) => { bool isDisconnected = false; var writeMessages = _coalesceEntryCache .ReplayFrom(-1) .ObserveOn(TaskPoolScheduler.Default) .Subscribe(cacheEntry => { if (isDisconnected) { return; } try { using (var memoryStream = new MemoryStream()) { using (var memoryStreamWriter = new BinaryWriter(memoryStream)) { // ReSharper disable once AccessToDisposedClosure cacheEntry.Entry.BlobData.Do(message => message.WriteTo(memoryStreamWriter)); clientStream.Write(memoryStream.ToArray(), 0, (int)memoryStream.Length); } } } catch (Exception) { isDisconnected = true; } }); var clientInfo = Optional.None <RegisterName>(); try { using (var binaryStream = new BinaryReader(clientStream)) { while (true) { var msg = BinaryMessage.ReadFrom(binaryStream); if (!clientInfo.HasValue) { clientInfo = BinaryMessage.TryParse(msg, RegisterName.MessageType, RegisterName.ReadDataFrom); if (clientInfo.HasValue) { clientAdded.OnNext(clientInfo.Value.DeviceId); } } incommingMessages.OnNext(msg); } } } finally { if (clientInfo.HasValue) { clientRemoved.OnNext(clientInfo.Value.DeviceId); } writeMessages.Dispose(); } }); Assembly = assemblyBuilt; Port = socketServer.LocalEndPoint.Port; Bytecode = bytecodeGenerated; PackageReferences = project.PackageReferences; ProjectReferences = project.ProjectReferences; ClientAdded = clientAdded; ClientRemoved = clientRemoved; Messages = Observable.Merge( incommingMessages, simulatorMessages); AccessCode = CodeGenerator.CreateRandomCode(5); }