- Updated Log.cs to handle nullable object types - Modified LogEvent class to accept nullable object types - Adjusted methods in log class to use nullable object types where necessary - Initialized s_thread variable with Thread.CurrentThread - Updated MemorySourceText and MemoryRefResolver classes in Script.cs - Implemented overrides for SourceText properties and methods in MemorySourceText class - Refactored MemoryRefResolver class to handle nullability correctly
408 lines
9.9 KiB
C#
408 lines
9.9 KiB
C#
|
|
|
|
#nullable enable
|
|
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.Serialization;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using lib;
|
|
|
|
namespace imm;
|
|
|
|
/*
|
|
T O D O :
|
|
T O D O :
|
|
T O D O :
|
|
x) Add unit tests for all this. This will definitely benefit from them
|
|
*/
|
|
|
|
static public class Util
|
|
{
|
|
//This can handle both Timed and Recorded
|
|
static public T Process<T>( ref T obj, Func<T, T> fn, string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0,
|
|
[CallerArgumentExpression("fn")]
|
|
string dbgExp = "" )
|
|
where T : Recorded<T>
|
|
{
|
|
obj = obj.Process( fn, reason, dbgName, dbgPath, dbgLine, dbgExp );
|
|
return obj;
|
|
}
|
|
|
|
static public T LightProcess<T>( ref T obj, Func<T, T> fn,
|
|
string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0,
|
|
[CallerArgumentExpression("fn")]
|
|
string dbgExp = ""
|
|
)
|
|
where T : Versioned<T>
|
|
{
|
|
obj = obj.Process( fn, reason );
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
public interface Meta
|
|
{
|
|
public uint Version => 0;
|
|
public string Reason => "";
|
|
public string Expression => "";
|
|
public string MemberName => "";
|
|
public string FilePath => "";
|
|
public int LineNumber => -1;
|
|
public DateTime CreatedAt => DateTime.MinValue;
|
|
public DateTime TouchedAt => DateTime.MinValue;
|
|
|
|
}
|
|
|
|
|
|
public interface Imm
|
|
{
|
|
public Meta Meta { get; }
|
|
public object Record( string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0
|
|
);
|
|
|
|
public Imm Process( Imm next,
|
|
string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0,
|
|
[CallerArgumentExpression("next")]
|
|
string dbgExp = ""
|
|
)
|
|
{
|
|
return next;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//[lib.Ser( Types = lib.Types.None )]
|
|
public record class Versioned<T> : Imm
|
|
where T : Versioned<T>
|
|
{
|
|
|
|
public delegate void ChangeDelegate( T ZZOld, T next );
|
|
|
|
public record class MetaData : Meta
|
|
{
|
|
public uint Version { get; internal set; } = 0;
|
|
public string Reason { get; internal set; } = "";
|
|
|
|
public MetaData() { }
|
|
}
|
|
|
|
protected Versioned( )
|
|
: this( new MetaData { Version = 1, Reason = $"Versioned.cons" } )
|
|
{
|
|
}
|
|
|
|
internal Versioned( MetaData meta )
|
|
{
|
|
MetaStorage = meta;
|
|
}
|
|
|
|
virtual public T Record(
|
|
string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0
|
|
)
|
|
{
|
|
return Process( t => t, reason );
|
|
}
|
|
|
|
|
|
|
|
protected MetaData MetaStorage = new();
|
|
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
public MetaData Meta => MetaStorage;
|
|
|
|
Meta Imm.Meta => MetaStorage;
|
|
|
|
[lib.Dont]
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
public ChangeDelegate OnChange = (old, cur) => {};
|
|
|
|
/*
|
|
public void AddOnChange( ChangeDelegate fn,
|
|
string reason,
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0`
|
|
)
|
|
{
|
|
log.debug( $"ADD {log.whatFile(dbgPath)}({dbgLine}): {dbgName} added OnChange bcs {reason}" );
|
|
OnChange += fn;
|
|
}
|
|
|
|
public void RemOnChange( ChangeDelegate fn,
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0
|
|
)
|
|
{
|
|
log.debug( $"REM {log.whatFile(dbgPath)}({dbgLine}): {dbgName} removing OnChange" );
|
|
OnChange -= fn;
|
|
}
|
|
*/
|
|
|
|
|
|
public T Process( Func<T, T> fn, string reason = "" )
|
|
{
|
|
var newT = fn( ( T )this );
|
|
|
|
return newT with
|
|
{
|
|
MetaStorage = Meta with
|
|
{
|
|
Version = newT.Meta.Version + 1,
|
|
Reason = reason,
|
|
}
|
|
};
|
|
}
|
|
|
|
object Imm.Record( string reason, string dbgName, string dbgPath, int dbgLine ) => Record( reason, dbgName, dbgPath, dbgLine );
|
|
|
|
//public object Record( string reason = "", [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = 0 ) => Recorded( );
|
|
}
|
|
|
|
//[lib.Ser( Types = lib.Types.None )]
|
|
public record class Recorded<T> : Versioned<T>, imm.Imm
|
|
where T : Recorded<T>
|
|
{
|
|
|
|
new public record class MetaData : Versioned<T>.MetaData
|
|
{
|
|
[lib.Dont]
|
|
public T? ZZOld { get; internal set; }
|
|
public T? Old => ZZOld;
|
|
public string DbgName { get; internal set; } = "";
|
|
public string DbgPath { get; internal set; } = "";
|
|
public int DbgLine { get; internal set; } = -1;
|
|
public string DbgExp { get; internal set; } = "";
|
|
|
|
public MetaData() { }
|
|
}
|
|
|
|
public Recorded() : this( new MetaData() )
|
|
{
|
|
}
|
|
|
|
public Recorded(MetaData meta) : base( meta )
|
|
{
|
|
}
|
|
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
new public MetaData Meta => MetaStorage as MetaData ?? new MetaData();
|
|
|
|
|
|
override public T Record(
|
|
string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0
|
|
)
|
|
{
|
|
return Process( t => t, reason, dbgName, dbgPath, dbgLine );
|
|
}
|
|
|
|
virtual public T Process( T next,
|
|
string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0,
|
|
[CallerArgumentExpression("next")]
|
|
string dbgExp = ""
|
|
)
|
|
{
|
|
return ProcessWork( ( old ) => next, reason, dbgName, dbgPath, dbgLine, dbgExp );
|
|
}
|
|
|
|
|
|
virtual public T Process( Func<T, T> fn,
|
|
string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0,
|
|
[CallerArgumentExpression("fn")]
|
|
string dbgExp = ""
|
|
)
|
|
{
|
|
return ProcessWork( fn, reason, dbgName, dbgPath, dbgLine, dbgExp );
|
|
}
|
|
|
|
virtual public T ProcessWork( Func<T, T> fn,
|
|
string reason,
|
|
string dbgName,
|
|
string dbgPath,
|
|
int dbgLine,
|
|
string dbgExp
|
|
)
|
|
{
|
|
var orig = ( T )this;
|
|
|
|
var next = fn( orig );
|
|
|
|
if( object.ReferenceEquals( orig, next) )
|
|
return next;
|
|
|
|
var ret = next with
|
|
{
|
|
//Do the Versioned code here
|
|
MetaStorage = Meta with
|
|
{
|
|
Version = orig.Meta.Version + 1,
|
|
Reason = !string.IsNullOrWhiteSpace( reason ) ? reason : next.Meta.Reason,
|
|
|
|
ZZOld = orig,
|
|
DbgName = dbgName,
|
|
DbgPath = dbgPath,
|
|
DbgLine = dbgLine,
|
|
DbgExp = dbgExp
|
|
}
|
|
};
|
|
|
|
OnChange( orig, ret );
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
public record class Timed<T> : Recorded<T>, imm.Imm
|
|
where T : Timed<T>
|
|
{
|
|
|
|
new public record class MetaData : Recorded<T>.MetaData
|
|
{
|
|
public readonly DateTime CreatedAt = DateTime.Now;
|
|
public DateTime TouchedAt { get; internal set; } = DateTime.Now;
|
|
}
|
|
|
|
public Timed() : this( new MetaData() )
|
|
{
|
|
}
|
|
|
|
public Timed( MetaData meta ) : base( meta )
|
|
{
|
|
}
|
|
|
|
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
|
new public MetaData Meta => MetaStorage as MetaData ?? new MetaData();
|
|
|
|
public TimeSpan Since => Meta.TouchedAt - Meta.Old?.Meta.TouchedAt ?? TimeSpan.MaxValue;
|
|
|
|
public void CallOnChange()
|
|
{
|
|
OnChange( Meta.Old, (T)this );
|
|
}
|
|
|
|
override public T Record(
|
|
string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0
|
|
)
|
|
{
|
|
return Process( t => t with { MetaStorage = t.Meta with { Reason = $"Record {reason}" }}, reason, dbgName, dbgPath, dbgLine );
|
|
}
|
|
|
|
override public T Process( T next,
|
|
string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0,
|
|
[CallerArgumentExpression("next")]
|
|
string dbgExp = ""
|
|
)
|
|
{
|
|
return ProcessWork( ( old ) => next, reason, dbgName, dbgPath, dbgLine, dbgExp );
|
|
}
|
|
|
|
public U ProcessFn<U>( Func<U, U> fn,
|
|
string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0,
|
|
[CallerArgumentExpression("next")]
|
|
string dbgExp = ""
|
|
)
|
|
where U : T
|
|
{
|
|
return (U)ProcessWork( fn as Func<T, T>, reason, dbgName, dbgPath, dbgLine, dbgExp );
|
|
}
|
|
|
|
override public T Process( Func<T, T> fn,
|
|
string reason = "",
|
|
[CallerMemberName] string dbgName = "",
|
|
[CallerFilePath] string dbgPath = "",
|
|
[CallerLineNumber] int dbgLine = 0,
|
|
[CallerArgumentExpression("fn")]
|
|
string dbgExp = ""
|
|
)
|
|
=> ProcessWork( fn, reason, dbgName, dbgPath, dbgLine, dbgExp );
|
|
|
|
virtual public T ProcessWork( Func<T, T> fn,
|
|
string reason,
|
|
string dbgName,
|
|
string dbgPath,
|
|
int dbgLine,
|
|
string dbgExp
|
|
)
|
|
{
|
|
var orig = ( T )this;
|
|
|
|
var next = fn( orig );
|
|
|
|
if( object.ReferenceEquals( orig, next) )
|
|
return next;
|
|
|
|
var ret = next with
|
|
{
|
|
MetaStorage = Meta with
|
|
{
|
|
//Versioned
|
|
Version = orig.Meta.Version + 1,
|
|
Reason = !string.IsNullOrWhiteSpace( reason ) ? reason : next.Meta.Reason,
|
|
|
|
//Recorded
|
|
DbgName = dbgName,
|
|
DbgPath = dbgPath,
|
|
DbgLine = dbgLine,
|
|
DbgExp = dbgExp,
|
|
ZZOld = orig,
|
|
|
|
//Timed
|
|
TouchedAt = DateTime.Now,
|
|
}
|
|
|
|
};
|
|
|
|
if( OnChange != null)
|
|
OnChange( orig, ret );
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
}
|