-
-
Notifications
You must be signed in to change notification settings - Fork 547
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
268 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
Jint/Native/FinalizationRegistry/FinalizationRegistryConstructor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using Jint.Native.Function; | ||
using Jint.Native.Object; | ||
using Jint.Runtime; | ||
using Jint.Runtime.Descriptors; | ||
|
||
namespace Jint.Native.FinalizationRegistry; | ||
|
||
/// <summary> | ||
/// https://tc39.es/ecma262/#sec-finalization-registry-constructor | ||
/// </summary> | ||
internal sealed class FinalizationRegistryConstructor : FunctionInstance, IConstructor | ||
{ | ||
private static readonly JsString _functionName = new("FinalizationRegistry"); | ||
|
||
public FinalizationRegistryConstructor( | ||
Engine engine, | ||
Realm realm, | ||
FunctionConstructor functionConstructor, | ||
ObjectPrototype objectPrototype) : base(engine, realm, _functionName) | ||
{ | ||
PrototypeObject = new FinalizationRegistryPrototype(engine, realm, this, objectPrototype); | ||
_prototype = functionConstructor.PrototypeObject; | ||
_prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden); | ||
_length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable); | ||
} | ||
|
||
public FinalizationRegistryPrototype PrototypeObject { get; } | ||
|
||
protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments) | ||
{ | ||
return Construct(arguments, thisObject); | ||
} | ||
|
||
ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget) => Construct(arguments, newTarget); | ||
|
||
private ObjectInstance Construct(JsValue[] arguments, JsValue newTarget) | ||
{ | ||
if (newTarget.IsUndefined()) | ||
{ | ||
ExceptionHelper.ThrowTypeError(_realm); | ||
} | ||
|
||
var cleanupCallback = arguments.At(0); | ||
if (cleanupCallback is not ICallable callable) | ||
{ | ||
ExceptionHelper.ThrowTypeError(_realm, "cleanup must be callable"); | ||
return null; | ||
} | ||
|
||
var finalizationRegistry = OrdinaryCreateFromConstructor( | ||
newTarget, | ||
static intrinsics => intrinsics.FinalizationRegistry.PrototypeObject, | ||
(engine, realm, state) => new FinalizationRegistryInstance(engine, realm, state!), | ||
callable | ||
); | ||
|
||
return finalizationRegistry; | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
Jint/Native/FinalizationRegistry/FinalizationRegistryInstance.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
using System.Runtime.CompilerServices; | ||
using Jint.Native.Object; | ||
using Jint.Runtime; | ||
|
||
namespace Jint.Native.FinalizationRegistry; | ||
|
||
internal sealed record Cell(JsValue WeakRefTarget, JsValue HeldValue, ObjectInstance? UnregisterToken); | ||
|
||
internal sealed class FinalizationRegistryInstance : ObjectInstance | ||
{ | ||
private readonly Realm _realm; | ||
private readonly JobCallback _callable; | ||
private readonly ConditionalWeakTable<JsValue, List<Observer>> _cells = new(); | ||
private readonly Dictionary<JsValue, List<Observer>> _byToken = new(); | ||
|
||
public FinalizationRegistryInstance(Engine engine, Realm realm, ICallable cleanupCallback) : base(engine) | ||
{ | ||
_realm = realm; | ||
_callable = engine._host.MakeJobCallBack(cleanupCallback); | ||
} | ||
|
||
public void CleanupFinalizationRegistry(ICallable? callback) | ||
{ | ||
} | ||
|
||
public void AddCell(Cell cell) | ||
{ | ||
var observer = new Observer(_callable); | ||
var observerList = _cells.GetOrCreateValue(cell.WeakRefTarget); | ||
observerList.Add(observer); | ||
|
||
if (cell.UnregisterToken is not null) | ||
{ | ||
if (!_byToken.TryGetValue(cell.UnregisterToken, out var list)) | ||
{ | ||
_byToken[cell.UnregisterToken] = list = new List<Observer>(); | ||
} | ||
list.Add(observer); | ||
} | ||
} | ||
|
||
public JsValue Remove(JsValue unregisterToken) | ||
{ | ||
if (_byToken.TryGetValue(unregisterToken, out var list)) | ||
{ | ||
var any = list.Count > 0; | ||
list.Clear(); | ||
return any; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private sealed class Observer | ||
{ | ||
private readonly JobCallback _callable; | ||
|
||
public Observer(JobCallback callable) | ||
{ | ||
_callable = callable; | ||
} | ||
|
||
~Observer() | ||
{ | ||
_callable.Callback.Call(Undefined); | ||
} | ||
} | ||
} |
120 changes: 120 additions & 0 deletions
120
Jint/Native/FinalizationRegistry/FinalizationRegistryPrototype.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
using Jint.Collections; | ||
using Jint.Native.Object; | ||
using Jint.Native.Symbol; | ||
using Jint.Runtime; | ||
using Jint.Runtime.Descriptors; | ||
using Jint.Runtime.Interop; | ||
|
||
namespace Jint.Native.FinalizationRegistry; | ||
|
||
/// <summary> | ||
/// https://tc39.es/ecma262/#sec-properties-of-the-finalization-registry-prototype-object | ||
/// </summary> | ||
internal sealed class FinalizationRegistryPrototype : Prototype | ||
{ | ||
private readonly FinalizationRegistryConstructor _constructor; | ||
|
||
public FinalizationRegistryPrototype( | ||
Engine engine, | ||
Realm realm, | ||
FinalizationRegistryConstructor constructor, | ||
ObjectPrototype objectPrototype) : base(engine, realm) | ||
{ | ||
_constructor = constructor; | ||
_prototype = objectPrototype; | ||
} | ||
|
||
protected override void Initialize() | ||
{ | ||
const PropertyFlag PropertyFlags = PropertyFlag.NonEnumerable; | ||
var properties = new PropertyDictionary(4, checkExistingKeys: false) | ||
{ | ||
[KnownKeys.Constructor] = new(_constructor, PropertyFlag.NonEnumerable), | ||
["register"] = new(new ClrFunctionInstance(Engine, "register", Register, 2, PropertyFlag.Configurable), PropertyFlags), | ||
["unregister"] = new(new ClrFunctionInstance(Engine, "unregister", Unregister, 1, PropertyFlag.Configurable), PropertyFlags), | ||
["cleanupSome"] = new(new ClrFunctionInstance(Engine, "cleanupSome", CleanupSome, 0, PropertyFlag.Configurable), PropertyFlags), | ||
}; | ||
SetProperties(properties); | ||
|
||
var symbols = new SymbolDictionary(1) { [GlobalSymbolRegistry.ToStringTag] = new("FinalizationRegistry", PropertyFlag.Configurable) }; | ||
SetSymbols(symbols); | ||
} | ||
|
||
/// <summary> | ||
/// https://tc39.es/ecma262/#sec-finalization-registry.prototype.register | ||
/// </summary> | ||
private JsValue Register(JsValue thisObj, JsValue[] arguments) | ||
{ | ||
var finalizationRegistry = AssertFinalizationRegistryInstance(thisObj); | ||
|
||
var target = arguments.At(0); | ||
var heldValue = arguments.At(1); | ||
var unregisterToken = arguments.At(2); | ||
|
||
if (target is not ObjectInstance) | ||
{ | ||
ExceptionHelper.ThrowTypeError(_realm, "target must be an object"); | ||
} | ||
|
||
if (SameValue(target, heldValue)) | ||
{ | ||
ExceptionHelper.ThrowTypeError(_realm, "target and holdings must not be same"); | ||
} | ||
|
||
if (unregisterToken is not ObjectInstance oi) | ||
{ | ||
if (!unregisterToken.IsUndefined()) | ||
{ | ||
ExceptionHelper.ThrowTypeError(_realm, unregisterToken + " must be an object"); | ||
} | ||
|
||
} | ||
var cell = new Cell(target, heldValue, unregisterToken as ObjectInstance); | ||
finalizationRegistry.AddCell(cell); | ||
return Undefined; | ||
} | ||
|
||
/// <summary> | ||
/// https://tc39.es/ecma262/#sec-finalization-registry.prototype.unregister | ||
/// </summary> | ||
private JsValue Unregister(JsValue thisObj, JsValue[] arguments) | ||
{ | ||
var finalizationRegistry = AssertFinalizationRegistryInstance(thisObj); | ||
|
||
var unregisterToken = arguments.At(0); | ||
|
||
if (unregisterToken is not ObjectInstance oi) | ||
{ | ||
ExceptionHelper.ThrowTypeError(_realm, unregisterToken + " must be an object"); | ||
} | ||
|
||
return finalizationRegistry.Remove(unregisterToken); | ||
} | ||
|
||
private JsValue CleanupSome(JsValue thisObj, JsValue[] arguments) | ||
{ | ||
var finalizationRegistry = AssertFinalizationRegistryInstance(thisObj); | ||
var callback = arguments.At(0); | ||
|
||
if (!callback.IsUndefined() && callback is not ICallable) | ||
{ | ||
ExceptionHelper.ThrowTypeError(_realm, callback + " must be callable"); | ||
} | ||
|
||
finalizationRegistry.CleanupFinalizationRegistry(callback as ICallable); | ||
|
||
return Undefined; | ||
} | ||
|
||
private FinalizationRegistryInstance AssertFinalizationRegistryInstance(JsValue thisObj) | ||
{ | ||
if (thisObj is not FinalizationRegistryInstance finalizationRegistryInstance) | ||
{ | ||
ExceptionHelper.ThrowTypeError(_realm, "object must be a FinalizationRegistry"); | ||
return null; | ||
} | ||
|
||
return finalizationRegistryInstance; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters