Selaa lähdekoodia

Implement WeakRef (#1242)

Marko Lahma 3 vuotta sitten
vanhempi
commit
723723c84e

+ 1 - 2
Jint.Tests.Test262/Test262Harness.settings.json

@@ -29,8 +29,7 @@
     "tail-call-optimization",
     "top-level-await",
     "Temporal",
-    "u180e",
-    "WeakRef"
+    "u180e"
   ],
   "ExcludedFlags": [
     "async"

+ 7 - 0
Jint.Tests.Test262/Test262Test.cs

@@ -61,6 +61,13 @@ public abstract partial class Test262Test
                 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)

+ 21 - 0
Jint/Agent.cs

@@ -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 - 0
Jint/Engine.cs

@@ -35,6 +35,8 @@ namespace Jint
 
         private readonly EventLoop _eventLoop = new();
 
+        private readonly Agent _agent = new Agent();
+
         // lazy properties
         private DebugHandler? _debugHandler;
 
@@ -356,6 +358,11 @@ namespace Jint
             _eventLoop.Events.Enqueue(continuation);
         }
 
+        internal void AddToKeptObjects(JsValue target)
+        {
+            _agent.AddToKeptObjects(target);
+        }
+
         internal void RunAvailableContinuations()
         {
             var queue = _eventLoop.Events;
@@ -660,6 +667,7 @@ namespace Jint
                 }
                 _isStrict = oldStrict;
                 ResetConstraints();
+                _agent.ClearKeptObjects();
             }
         }
 

+ 1 - 1
Jint/Native/Global/GlobalObject.cs

@@ -76,7 +76,7 @@ namespace Jint.Native.Global
                 ["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),
 
 

+ 60 - 0
Jint/Native/WeakRef/WeakRefConstructor.cs

@@ -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 - 0
Jint/Native/WeakRef/WeakRefInstance.cs

@@ -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 - 0
Jint/Native/WeakRef/WeakRefPrototype.cs

@@ -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 - 0
Jint/Runtime/Intrinsics.cs

@@ -24,6 +24,7 @@ using Jint.Native.String;
 using Jint.Native.Symbol;
 using Jint.Native.TypedArray;
 using Jint.Native.WeakMap;
+using Jint.Native.WeakRef;
 using Jint.Native.WeakSet;
 
 namespace Jint.Runtime
@@ -53,6 +54,7 @@ namespace Jint.Runtime
         private ErrorConstructor? _uriError;
         private WeakMapConstructor? _weakMap;
         private WeakSetConstructor? _weakSet;
+        private WeakRefConstructor? _weakRef;
         private PromiseConstructor? _promise;
         private ProxyConstructor? _proxy;
         private ReflectInstance? _reflect;
@@ -181,6 +183,9 @@ namespace Jint.Runtime
         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);
 

+ 2 - 1
README.md

@@ -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