diff --git a/ClearScript/V8/FastProxy/V8FastArg.cs b/ClearScript/V8/FastProxy/V8FastArg.cs index 985e087a..2e1f4992 100644 --- a/ClearScript/V8/FastProxy/V8FastArg.cs +++ b/ClearScript/V8/FastProxy/V8FastArg.cs @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using Microsoft.ClearScript.Util; +using Microsoft.ClearScript.V8.SplitProxy; using System; +using System.Collections.Concurrent; using System.Numerics; -using Microsoft.ClearScript.V8.SplitProxy; namespace Microsoft.ClearScript.V8.FastProxy { @@ -12,155 +14,170 @@ namespace Microsoft.ClearScript.V8.FastProxy /// public readonly ref struct V8FastArg { + private readonly V8ScriptEngine engine; private readonly V8Value.FastArg.Ptr pArg; private readonly V8FastArgKind kind; - private readonly object obj; + private readonly Holder holder; + private static readonly ConcurrentBag> holderPool = new(); + internal static readonly object NotInitialized = new(); internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKind kind) { + this.engine = engine; this.pArg = pArg; this.kind = kind; - obj = V8FastArgImpl.InitializeObject(engine, pArg.AsRef()); + + if (!holderPool.TryTake(out holder)) + { + holder = new Holder(); + holder.Value = NotInitialized; + } + } + + internal void Dispose() + { + holder.Value = NotInitialized; + holderPool.Add(holder); } /// /// Determines whether the argument is falsy. /// - public bool IsFalsy => V8FastArgImpl.IsFalsy(pArg.AsRef(), obj); + public bool IsFalsy => V8FastArgImpl.IsFalsy(pArg.AsRef(), GetObject()); /// /// Determines whether the argument is truthy. /// - public bool IsTruthy => V8FastArgImpl.IsTruthy(pArg.AsRef(), obj); + public bool IsTruthy => V8FastArgImpl.IsTruthy(pArg.AsRef(), GetObject()); /// /// Determines whether the argument is undefined. /// - public bool IsUndefined => V8FastArgImpl.IsUndefined(pArg.AsRef(), obj); + public bool IsUndefined => V8FastArgImpl.IsUndefined(pArg.AsRef(), GetObject()); /// /// Determines whether the argument is null. /// - public bool IsNull => V8FastArgImpl.IsNull(pArg.AsRef(), obj); + public bool IsNull => V8FastArgImpl.IsNull(pArg.AsRef(), GetObject()); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out bool value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out bool value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out char value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out char value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out sbyte value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out sbyte value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out byte value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out byte value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out short value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out short value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out ushort value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out ushort value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out int value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out int value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out uint value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out uint value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out long value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out long value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out ulong value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out ulong value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out float value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out float value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out double value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out double value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out decimal value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out decimal value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the string value of the argument if possible. /// /// On return, the string value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out string value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out string value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the string value of the argument as a ReadOnlySpan<char> if possible. /// /// On return, the string value of the argument as a ReadOnlySpan<char> if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out ReadOnlySpan value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out ReadOnlySpan value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out DateTime value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out DateTime value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument if possible. /// /// On return, the value of the argument if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out BigInteger value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out BigInteger value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets a nullable value of the given underlying type from the argument if possible. @@ -168,7 +185,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// The underlying type of the nullable value to get. /// On return, a nullable value of underlying type if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out T? value) where T : struct => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out T? value) where T : struct => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets a value of the given type from the argument if possible. @@ -176,7 +193,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// The type of value to get. /// On return, a value of type if the operation succeeded. /// True if the operation succeeded, false otherwise. - public bool TryGet(out T value) => V8FastArgImpl.TryGet(pArg.AsRef(), obj, out value); + public bool TryGet(out T value) => V8FastArgImpl.TryGet(pArg.AsRef(), GetObject(), out value); /// /// Gets the value of the argument. @@ -186,7 +203,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public bool GetBoolean(string name = null) => V8FastArgImpl.GetBoolean(pArg.AsRef(), obj, kind, name); + public bool GetBoolean(string name = null) => V8FastArgImpl.GetBoolean(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -196,7 +213,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public char GetChar(string name = null) => V8FastArgImpl.GetChar(pArg.AsRef(), obj, kind, name); + public char GetChar(string name = null) => V8FastArgImpl.GetChar(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -206,7 +223,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public sbyte GetSByte(string name = null) => V8FastArgImpl.GetSByte(pArg.AsRef(), obj, kind, name); + public sbyte GetSByte(string name = null) => V8FastArgImpl.GetSByte(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -216,7 +233,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public byte GetByte(string name = null) => V8FastArgImpl.GetByte(pArg.AsRef(), obj, kind, name); + public byte GetByte(string name = null) => V8FastArgImpl.GetByte(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -226,7 +243,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public short GetInt16(string name = null) => V8FastArgImpl.GetInt16(pArg.AsRef(), obj, kind, name); + public short GetInt16(string name = null) => V8FastArgImpl.GetInt16(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -236,7 +253,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public ushort GetUInt16(string name = null) => V8FastArgImpl.GetUInt16(pArg.AsRef(), obj, kind, name); + public ushort GetUInt16(string name = null) => V8FastArgImpl.GetUInt16(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -246,7 +263,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public int GetInt32(string name = null) => V8FastArgImpl.GetInt32(pArg.AsRef(), obj, kind, name); + public int GetInt32(string name = null) => V8FastArgImpl.GetInt32(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -256,7 +273,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public uint GetUInt32(string name = null) => V8FastArgImpl.GetUInt32(pArg.AsRef(), obj, kind, name); + public uint GetUInt32(string name = null) => V8FastArgImpl.GetUInt32(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -266,7 +283,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public long GetInt64(string name = null) => V8FastArgImpl.GetInt64(pArg.AsRef(), obj, kind, name); + public long GetInt64(string name = null) => V8FastArgImpl.GetInt64(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -276,7 +293,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public ulong GetUInt64(string name = null) => V8FastArgImpl.GetUInt64(pArg.AsRef(), obj, kind, name); + public ulong GetUInt64(string name = null) => V8FastArgImpl.GetUInt64(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -286,7 +303,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public float GetSingle(string name = null) => V8FastArgImpl.GetSingle(pArg.AsRef(), obj, kind, name); + public float GetSingle(string name = null) => V8FastArgImpl.GetSingle(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -296,7 +313,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public double GetDouble(string name = null) => V8FastArgImpl.GetDouble(pArg.AsRef(), obj, kind, name); + public double GetDouble(string name = null) => V8FastArgImpl.GetDouble(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -306,7 +323,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public decimal GetDecimal(string name = null) => V8FastArgImpl.GetDecimal(pArg.AsRef(), obj, kind, name); + public decimal GetDecimal(string name = null) => V8FastArgImpl.GetDecimal(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the string value of the argument. @@ -316,7 +333,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public string GetString(string name = null) => V8FastArgImpl.GetString(pArg.AsRef(), obj, kind, name); + public string GetString(string name = null) => V8FastArgImpl.GetString(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the string value of the argument as a ReadOnlySpan<char>. @@ -326,7 +343,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public ReadOnlySpan GetCharSpan(string name = null) => V8FastArgImpl.GetCharSpan(pArg.AsRef(), obj, kind, name); + public ReadOnlySpan GetCharSpan(string name = null) => V8FastArgImpl.GetCharSpan(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -336,7 +353,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public DateTime GetDateTime(string name = null) => V8FastArgImpl.GetDateTime(pArg.AsRef(), obj, kind, name); + public DateTime GetDateTime(string name = null) => V8FastArgImpl.GetDateTime(pArg.AsRef(), GetObject(), kind, name); /// /// Gets the value of the argument. @@ -346,7 +363,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public BigInteger GetBigInteger(string name = null) => V8FastArgImpl.GetBigInteger(pArg.AsRef(), obj, kind, name); + public BigInteger GetBigInteger(string name = null) => V8FastArgImpl.GetBigInteger(pArg.AsRef(), GetObject(), kind, name); /// /// Gets a nullable value of the given underlying type from the argument. @@ -357,7 +374,7 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public T? GetNullable(string name = null) where T : struct => V8FastArgImpl.GetNullable(pArg.AsRef(), obj, kind, name); + public T? GetNullable(string name = null) where T : struct => V8FastArgImpl.GetNullable(pArg.AsRef(), GetObject(), kind, name); /// /// Gets a value of the given type from the argument. @@ -368,6 +385,16 @@ internal V8FastArg(V8ScriptEngine engine, V8Value.FastArg.Ptr pArg, V8FastArgKin /// /// This method throws an exception if the operation fails. /// - public T Get(string name = null) => V8FastArgImpl.Get(pArg.AsRef(), obj, kind, name); + public T Get(string name = null) => V8FastArgImpl.Get(pArg.AsRef(), GetObject(), kind, name); + + private object GetObject() + { + if (holder.Value == NotInitialized) + { + holder.Value = V8FastArgImpl.InitializeObject(engine, pArg.AsRef()); + } + + return holder.Value; + } } } diff --git a/ClearScript/V8/FastProxy/V8FastArgs.cs b/ClearScript/V8/FastProxy/V8FastArgs.cs index 0f500439..39ee6e8e 100644 --- a/ClearScript/V8/FastProxy/V8FastArgs.cs +++ b/ClearScript/V8/FastProxy/V8FastArgs.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using Microsoft.ClearScript.V8.SplitProxy; using System; +using System.Buffers; using System.Numerics; -using Microsoft.ClearScript.Util; -using Microsoft.ClearScript.V8.SplitProxy; namespace Microsoft.ClearScript.V8.FastProxy { @@ -13,27 +13,29 @@ namespace Microsoft.ClearScript.V8.FastProxy /// public readonly ref struct V8FastArgs { + private readonly V8ScriptEngine engine; private readonly ReadOnlySpan args; private readonly V8FastArgKind argKind; private readonly object[] objects; internal V8FastArgs(V8ScriptEngine engine, in ReadOnlySpan args, V8FastArgKind argKind) { + this.engine = engine; this.args = args; this.argKind = argKind; - objects = null; + objects = ArrayPool.Shared.Rent(args.Length); for (var index = 0; index < args.Length; index++) { - var tempObject = V8FastArgImpl.InitializeObject(engine, args[index]); - if (tempObject is not Nonexistent) - { - EnsureObjects(ref objects, args.Length); - objects[index] = tempObject; - } + objects[index] = V8FastArg.NotInitialized; } } + internal void Dispose() + { + ArrayPool.Shared.Return(objects, true); + } + /// /// Gets the number of arguments in the list. /// @@ -432,18 +434,16 @@ internal V8FastArgs(V8ScriptEngine engine, in ReadOnlySpan args /// public T Get(int index, string name = null) => V8FastArgImpl.Get(args[index], GetObject(index), argKind, name); - private static void EnsureObjects(ref object[] objects, int count) + private object GetObject(int index) { - if (objects is null) + ref object obj = ref objects[index]; + + if (obj == V8FastArg.NotInitialized) { - objects = new object[count]; - for (var index = 0; index < count; index++) - { - objects[index] = Nonexistent.Value; - } + obj = V8FastArgImpl.InitializeObject(engine, args[index]); } - } - private object GetObject(int index) => (objects is not null) ? objects[index] : Nonexistent.Value; + return obj; + } } } diff --git a/ClearScript/V8/FastProxy/V8FastHostItem.cs b/ClearScript/V8/FastProxy/V8FastHostItem.cs index 07d32cce..9fc06a1b 100644 --- a/ClearScript/V8/FastProxy/V8FastHostItem.cs +++ b/ClearScript/V8/FastProxy/V8FastHostItem.cs @@ -66,12 +66,13 @@ public void SetProperty(StdString.Ptr pName, V8Value.FastArg.Ptr pValue) Engine.HostInvoke( static ctx => { - var value = new V8FastArg(ctx.self.Engine, ctx.pValue, V8FastArgKind.PropertyValue); - var target = ctx.self.Target; if (target.Operations is {} operations) { - operations.SetProperty(target, ctx.name, value); + using (var value = new V8FastArg(ctx.self.Engine, ctx.pValue, V8FastArgKind.PropertyValue)) + { + operations.SetProperty(target, ctx.name, value); + } } else { @@ -158,12 +159,13 @@ public void SetProperty(int index, V8Value.FastArg.Ptr pValue) Engine.HostInvoke( static ctx => { - var value = new V8FastArg(ctx.self.Engine, ctx.pValue, V8FastArgKind.PropertyValue); - var target = ctx.self.Target; if (target.Operations is {} operations) { - operations.SetProperty(target, ctx.index, value); + using (var value = new V8FastArg(ctx.self.Engine, ctx.pValue, V8FastArgKind.PropertyValue)) + { + operations.SetProperty(target, ctx.index, value); + } } else { @@ -239,9 +241,11 @@ public void Invoke(bool asConstructor, int argCount, V8Value.FastArg.Ptr pArgs, throw new NotSupportedException("The object does not support constructor invocation"); } - var args = new V8FastArgs(ctx.self.Engine, ctx.pArgs.ToSpan(ctx.argCount), V8FastArgKind.MethodArg); + using (var args = new V8FastArgs(ctx.self.Engine, ctx.pArgs.ToSpan(ctx.argCount), V8FastArgKind.MethodArg)) + { + methodOperations.Invoke(args, result); + } - methodOperations.Invoke(args, result); if (!result.IsSet) { result.SetUndefined(); @@ -249,9 +253,11 @@ public void Invoke(bool asConstructor, int argCount, V8Value.FastArg.Ptr pArgs, } else if (target.Operations is IV8FastHostFunctionOperations functionOperations) { - var args = new V8FastArgs(ctx.self.Engine, ctx.pArgs.ToSpan(ctx.argCount), V8FastArgKind.FunctionArg); + using (var args = new V8FastArgs(ctx.self.Engine, ctx.pArgs.ToSpan(ctx.argCount), V8FastArgKind.FunctionArg)) + { + functionOperations.Invoke(ctx.asConstructor, args, result); + } - functionOperations.Invoke(ctx.asConstructor, args, result); if (!result.IsSet) { result.SetUndefined();