using XFESystem.Collections;
namespace XFESystem.Browser;
public sealed record BrowserWeakCollectionsRegressionResult(bool Passed,string Actual,string Expected,
WeakCollectionLifetime BeforeRelease,WeakCollectionLifetime AfterRelease,
bool NativeHandleCountChecked,ulong NativeHandlesBefore,ulong NativeHandlesAfter,
bool EngineCachesExercised,bool ConstructorFailureReleased,bool ConstructorExceptionPreserved,
WeakCollectionLifetime ConstructorBeforeRelease,WeakCollectionLifetime ConstructorAfterRelease);
/// <summary>Runs the actual patched interpreter intrinsics, not a JS polyfill.
/// The owner releases every slot before returning, including evaluation errors.</summary>
public static class BrowserWeakCollectionsRegression
{
public static BrowserWeakCollectionsRegressionResult Run(Func<ulong>? dependentHandleCount=null)
{
const string expected="true,true,true,true,true,3,true,true,24384,8128,true";
ulong nativeBefore=dependentHandleCount?.Invoke()??0;
var runtime=new JavaScriptRuntime();
JavaScriptResult result;
try
{
result=runtime.Evaluate("""
const k={},v={},m=new WeakMap([[k,v]]),s=new WeakSet([k]);
let bad=0;try{new WeakMap()}catch(e){bad+=100}try{WeakMap()}catch(e){bad+=e instanceof TypeError?1:100}
try{m.set(1,2)}catch(e){bad+=e instanceof TypeError?1:100}try{s.add(1)}catch(e){bad+=e instanceof TypeError?1:100}
const same=m.set(k,v)===m&&m.get(k)===v&&m.has(k)&&s.add(k)===s&&s.has(k);
const primitive=m.get(4)===undefined&&!m.has(4)&&!m.delete(4)&&!s.has(4)&&!s.delete(4);
const desc=!Object.getOwnPropertyDescriptor(WeakMap.prototype,'set').enumerable&&Object.getPrototypeOf(m)===WeakMap.prototype;
m.delete(k);const inserted=m.getOrInsert(k,21)===21&&m.getOrInsert(k,99)===21;
m.delete(k);const computed=m.getOrInsertComputed(k,()=>{m.set(k,77);return 42})===42&&m.get(k)===42;
class Secret {
#value; #read(){return this.#value} get #doubled(){return this.#value*2}
constructor(value){this.#value=value} read(){return this.#read()+this.#doubled}
}
class ShapeType {constructor(value){this.own=value}}
globalThis.kept=[];globalThis.shapes=[];globalThis.typedShapes=[];let secretSum=0,shapeSum=0,identity=true;
for(let i=0;i<128;i++){
const secret=new Secret(i);kept.push(secret);secretSum+=secret.read();
const typed=new ShapeType(i);typedShapes.push(typed);
const proto={index:i},shape=Object.create(proto);shape.own=i;shapes.push(shape);
shapeSum+=shape.own;identity=identity&&Object.getPrototypeOf(shape)===proto&&shape.index===i&&typed.own===i;
}
[same,primitive,desc,inserted,computed,bad,s.delete(k),!s.has(k),secretSum,shapeSum,identity].join(',')
""");
}
catch(Exception error) when(error is not OutOfMemoryException and not StackOverflowException)
{
result=new(false,string.Empty,string.Empty,BoundedText.Safe(error.Message,512));
}
var before=runtime.WeakCollectionLifetime;
runtime.Dispose();
var after=runtime.WeakCollectionLifetime;
WeakCollectionScope? constructorScope=null;
var empty=new WeakCollectionLifetime(0,0,0,0,0,0);
var constructorBefore=empty;bool constructorExceptionPreserved=false;
var knownError=new InvalidOperationException("XFESystem owned Engine constructor regression");
try
{
_=new Jint.Engine((engine,options)=>
{
constructorScope=((IWeakCollectionOwner)(object)engine).WeakCollections;
var table=new OwnedWeakTable<object,object>(constructorScope);
table.Add(new object(),new object());constructorBefore=constructorScope.Lifetime;
throw knownError;
});
}
catch(Exception error) when(error is not OutOfMemoryException and not StackOverflowException)
{constructorExceptionPreserved=ReferenceEquals(error,knownError);}
var constructorAfter=constructorScope?.Lifetime??empty;
bool constructorReleased=constructorScope is not null&&constructorBefore.Created>0&&constructorAfter.Tables==0&&
constructorAfter.Entries==0&&constructorAfter.Created==constructorAfter.Disposed&&
constructorAfter.TableHandlesCreated==constructorAfter.TableHandlesDisposed;
ulong nativeAfter=dependentHandleCount?.Invoke()??0;
bool engineCaches=result.Success&&result.Text==expected&&before.Tables>=5&&before.Created>=130;
bool passed=result.Success&&result.Text==expected&&before.Created>0&&after.Tables==0&&after.Entries==0&&
after.Created==after.Disposed&&after.TableHandlesCreated==after.TableHandlesDisposed&&
engineCaches&&constructorReleased&&constructorExceptionPreserved&&
(dependentHandleCount is null||nativeBefore==nativeAfter);
return new(passed,result.Success?result.Text:result.Error,expected,before,after,
dependentHandleCount is not null,nativeBefore,nativeAfter,engineCaches,constructorReleased,constructorExceptionPreserved,
constructorBefore,constructorAfter);
}
}
using XFESystem.Collections;
namespace XFESystem.Browser;
public sealed record BrowserWeakCollectionsRegressionResult(bool Passed,string Actual,string Expected,
WeakCollectionLifetime BeforeRelease,WeakCollectionLifetime AfterRelease,
bool NativeHandleCountChecked,ulong NativeHandlesBefore,ulong NativeHandlesAfter,
bool EngineCachesExercised,bool ConstructorFailureReleased,bool ConstructorExceptionPreserved,
WeakCollectionLifetime ConstructorBeforeRelease,WeakCollectionLifetime ConstructorAfterRelease);
/// <summary>Runs the actual patched interpreter intrinsics, not a JS polyfill.
/// The owner releases every slot before returning, including evaluation errors.</summary>
public static class BrowserWeakCollectionsRegression
{
public static BrowserWeakCollectionsRegressionResult Run(Func<ulong>? dependentHandleCount=null)
{
const string expected="true,true,true,true,true,3,true,true,24384,8128,true";
ulong nativeBefore=dependentHandleCount?.Invoke()??0;
var runtime=new JavaScriptRuntime();
JavaScriptResult result;
try
{
result=runtime.Evaluate("""
const k={},v={},m=new WeakMap([[k,v]]),s=new WeakSet([k]);
let bad=0;try{new WeakMap()}catch(e){bad+=100}try{WeakMap()}catch(e){bad+=e instanceof TypeError?1:100}
try{m.set(1,2)}catch(e){bad+=e instanceof TypeError?1:100}try{s.add(1)}catch(e){bad+=e instanceof TypeError?1:100}
const same=m.set(k,v)===m&&m.get(k)===v&&m.has(k)&&s.add(k)===s&&s.has(k);
const primitive=m.get(4)===undefined&&!m.has(4)&&!m.delete(4)&&!s.has(4)&&!s.delete(4);
const desc=!Object.getOwnPropertyDescriptor(WeakMap.prototype,'set').enumerable&&Object.getPrototypeOf(m)===WeakMap.prototype;
m.delete(k);const inserted=m.getOrInsert(k,21)===21&&m.getOrInsert(k,99)===21;
m.delete(k);const computed=m.getOrInsertComputed(k,()=>{m.set(k,77);return 42})===42&&m.get(k)===42;
class Secret {
#value; #read(){return this.#value} get #doubled(){return this.#value*2}
constructor(value){this.#value=value} read(){return this.#read()+this.#doubled}
}
class ShapeType {constructor(value){this.own=value}}
globalThis.kept=[];globalThis.shapes=[];globalThis.typedShapes=[];let secretSum=0,shapeSum=0,identity=true;
for(let i=0;i<128;i++){
const secret=new Secret(i);kept.push(secret);secretSum+=secret.read();
const typed=new ShapeType(i);typedShapes.push(typed);
const proto={index:i},shape=Object.create(proto);shape.own=i;shapes.push(shape);
shapeSum+=shape.own;identity=identity&&Object.getPrototypeOf(shape)===proto&&shape.index===i&&typed.own===i;
}
[same,primitive,desc,inserted,computed,bad,s.delete(k),!s.has(k),secretSum,shapeSum,identity].join(',')
""");
}
catch(Exception error) when(error is not OutOfMemoryException and not StackOverflowException)
{
result=new(false,string.Empty,string.Empty,BoundedText.Safe(error.Message,512));
}
var before=runtime.WeakCollectionLifetime;
runtime.Dispose();
var after=runtime.WeakCollectionLifetime;
WeakCollectionScope? constructorScope=null;
var empty=new WeakCollectionLifetime(0,0,0,0,0,0);
var constructorBefore=empty;bool constructorExceptionPreserved=false;
var knownError=new InvalidOperationException("XFESystem owned Engine constructor regression");
try
{
_=new Jint.Engine((engine,options)=>
{
constructorScope=((IWeakCollectionOwner)(object)engine).WeakCollections;
var table=new OwnedWeakTable<object,object>(constructorScope);
table.Add(new object(),new object());constructorBefore=constructorScope.Lifetime;
throw knownError;
});
}
catch(Exception error) when(error is not OutOfMemoryException and not StackOverflowException)
{constructorExceptionPreserved=ReferenceEquals(error,knownError);}
var constructorAfter=constructorScope?.Lifetime??empty;
bool constructorReleased=constructorScope is not null&&constructorBefore.Created>0&&constructorAfter.Tables==0&&
constructorAfter.Entries==0&&constructorAfter.Created==constructorAfter.Disposed&&
constructorAfter.TableHandlesCreated==constructorAfter.TableHandlesDisposed;
ulong nativeAfter=dependentHandleCount?.Invoke()??0;
bool engineCaches=result.Success&&result.Text==expected&&before.Tables>=5&&before.Created>=130;
bool passed=result.Success&&result.Text==expected&&before.Created>0&&after.Tables==0&&after.Entries==0&&
after.Created==after.Disposed&&after.TableHandlesCreated==after.TableHandlesDisposed&&
engineCaches&&constructorReleased&&constructorExceptionPreserved&&
(dependentHandleCount is null||nativeBefore==nativeAfter);
return new(passed,result.Success?result.Text:result.Error,expected,before,after,
dependentHandleCount is not null,nativeBefore,nativeAfter,engineCaches,constructorReleased,constructorExceptionPreserved,
constructorBefore,constructorAfter);
}
}