Skip to content

Commit

Permalink
Implement WeakRef (#1242)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed Jul 25, 2022
1 parent 97e481f commit 723723c
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 4 deletions.
3 changes: 1 addition & 2 deletions Jint.Tests.Test262/Test262Harness.settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
"tail-call-optimization",
"top-level-await",
"Temporal",
"u180e",
"WeakRef"
"u180e"
],
"ExcludedFlags": [
"async"
Expand Down
7 changes: 7 additions & 0 deletions Jint.Tests.Test262/Test262Test.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ private Engine BuildTestExecutor(Test262File file)
return JsValue.Undefined;
}), true, true, true));

o.FastSetProperty("gc", new PropertyDescriptor(new ClrFunctionInstance(engine, "gc",
(_, _) =>
{
GC.Collect();
return JsValue.Undefined;
}), true, true, true));

engine.SetValue("$262", o);

foreach (var include in file.Includes)
Expand Down
21 changes: 21 additions & 0 deletions Jint/Agent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Jint.Native;

namespace Jint;

/// <summary>
/// https://tc39.es/ecma262/#sec-agents , still a work in progress, mostly placeholder
/// </summary>
internal sealed class Agent
{
private List<JsValue> _keptAlive = new();

public void AddToKeptObjects(JsValue target)
{
_keptAlive.Add(target);
}

public void ClearKeptObjects()
{
_keptAlive.Clear();
}
}
8 changes: 8 additions & 0 deletions Jint/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public sealed partial class Engine : IDisposable

private readonly EventLoop _eventLoop = new();

private readonly Agent _agent = new Agent();

// lazy properties
private DebugHandler? _debugHandler;

Expand Down Expand Up @@ -356,6 +358,11 @@ internal void AddToEventLoop(Action continuation)
_eventLoop.Events.Enqueue(continuation);
}

internal void AddToKeptObjects(JsValue target)
{
_agent.AddToKeptObjects(target);
}

internal void RunAvailableContinuations()
{
var queue = _eventLoop.Events;
Expand Down Expand Up @@ -660,6 +667,7 @@ private T ExecuteWithConstraints<T>(bool strict, Func<T> callback)
}
_isStrict = oldStrict;
ResetConstraints();
_agent.ClearKeptObjects();
}
}

Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Global/GlobalObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ protected override void Initialize()
["Uint8Array"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.Uint8Array, propertyFlags),
["Uint8ClampedArray"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.Uint8ClampedArray, propertyFlags),
["WeakMap"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.WeakMap, propertyFlags),
["WeakRef"] = new LazyPropertyDescriptor(this, static state => Undefined, propertyFlags),
["WeakRef"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.WeakRef, propertyFlags),
["WeakSet"] = new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.WeakSet, propertyFlags),


Expand Down
60 changes: 60 additions & 0 deletions Jint/Native/WeakRef/WeakRefConstructor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using Jint.Native.Function;
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Descriptors;

namespace Jint.Native.WeakRef;

/// <summary>
/// https://tc39.es/ecma262/#sec-weak-ref-constructor
/// </summary>
internal sealed class WeakRefConstructor : FunctionInstance, IConstructor
{
private static readonly JsString _functionName = new("WeakRef");

internal WeakRefConstructor(
Engine engine,
Realm realm,
FunctionPrototype functionPrototype,
ObjectPrototype objectPrototype)
: base(engine, realm, _functionName)
{
_prototype = functionPrototype;
PrototypeObject = new WeakRefPrototype(engine, realm, this, objectPrototype);
_length = new PropertyDescriptor(1, PropertyFlag.Configurable);
_prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
}

private WeakRefPrototype PrototypeObject { get; }

protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
{
ExceptionHelper.ThrowTypeError(_realm, "Constructor WeakRef requires 'new'");
return null;
}

ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget)
{
if (newTarget.IsUndefined())
{
ExceptionHelper.ThrowTypeError(_realm);
}

var target = arguments.At(0);

if (target is not ObjectInstance)
{
ExceptionHelper.ThrowTypeError(_realm, "WeakRef: target must be an object");
}

var weakRef = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.WeakRef.PrototypeObject,
static (Engine engine, Realm _, object? t) => new WeakRefInstance(engine, (ObjectInstance) t!),
target);

_engine.AddToKeptObjects(target);

return weakRef;
}
}
27 changes: 27 additions & 0 deletions Jint/Native/WeakRef/WeakRefInstance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Jint.Native.Object;

namespace Jint.Native.WeakRef;

/// <summary>
/// https://tc39.es/ecma262/#sec-properties-of-weak-ref-instances
/// </summary>
internal sealed class WeakRefInstance : ObjectInstance
{
private readonly WeakReference<ObjectInstance> _weakRefTarget;

public WeakRefInstance(Engine engine, ObjectInstance target) : base(engine)
{
_weakRefTarget = new WeakReference<ObjectInstance>(target);
}

public JsValue WeakRefDeref()
{
if (_weakRefTarget.TryGetTarget(out var target))
{
_engine.AddToKeptObjects(target);
return target;
}

return Undefined;
}
}
54 changes: 54 additions & 0 deletions Jint/Native/WeakRef/WeakRefPrototype.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
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.WeakRef;

/// <summary>
/// https://tc39.es/ecma262/#sec-properties-of-the-weak-ref-prototype-object
/// </summary>
internal sealed class WeakRefPrototype : Prototype
{
private readonly WeakRefConstructor _constructor;

internal WeakRefPrototype(
Engine engine,
Realm realm,
WeakRefConstructor constructor,
ObjectPrototype prototype) : base(engine, realm)
{
_prototype = prototype;
_constructor = constructor;
}

protected override void Initialize()
{
const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
var properties = new PropertyDictionary(5, checkExistingKeys: false)
{
["constructor"] = new(_constructor, PropertyFlag.NonEnumerable),
["deref"] = new(new ClrFunctionInstance(Engine, "deref", Deref, 0, PropertyFlag.Configurable), propertyFlags)
};
SetProperties(properties);

var symbols = new SymbolDictionary(1)
{
[GlobalSymbolRegistry.ToStringTag] = new("WeakRef", false, false, true)
};
SetSymbols(symbols);
}

private JsValue Deref(JsValue thisObj, JsValue[] arguments)
{
var weakRef = thisObj as WeakRefInstance;
if (weakRef is null)
{
ExceptionHelper.ThrowTypeError(_realm, "object must be a WeakRef");
}

return weakRef.WeakRefDeref();
}
}
5 changes: 5 additions & 0 deletions Jint/Runtime/Intrinsics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using Jint.Native.Symbol;
using Jint.Native.TypedArray;
using Jint.Native.WeakMap;
using Jint.Native.WeakRef;
using Jint.Native.WeakSet;

namespace Jint.Runtime
Expand Down Expand Up @@ -53,6 +54,7 @@ public sealed class Intrinsics
private ErrorConstructor? _uriError;
private WeakMapConstructor? _weakMap;
private WeakSetConstructor? _weakSet;
private WeakRefConstructor? _weakRef;
private PromiseConstructor? _promise;
private ProxyConstructor? _proxy;
private ReflectInstance? _reflect;
Expand Down Expand Up @@ -181,6 +183,9 @@ internal Intrinsics(Engine engine, Realm realm)
public WeakSetConstructor WeakSet =>
_weakSet ??= new WeakSetConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);

internal WeakRefConstructor WeakRef =>
_weakRef ??= new WeakRefConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);

public PromiseConstructor Promise =>
_promise ??= new PromiseConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ The entire execution engine was rebuild with performance in mind, in many cases
-`AggregateError`
-`Promise.any`
-`String.prototype.replaceAll`
-`WeakRef` and `FinalizationRegistry`
-`WeakRef`
-`FinalizationRegistry`

#### ECMAScript 2022

Expand Down

0 comments on commit 723723c

Please sign in to comment.