Skip to content

Commit

Permalink
Version 7.2.5: Added V8Runtime.HeapSizeViolationPolicy and V8ScriptEn…
Browse files Browse the repository at this point in the history
…gine.RuntimeHeapSizeViolationPolicy (GitHub Issue #369); added ScriptEngine.VoidResultValue and made VoidResult.Value public (GitHub Issue #372); fixed array buffer memory leak (GitHub Issue #370); fixed access to events with inaccessible handler types (GitHub Issue #365); updated API documentation. Tested with V8 10.1.124.11.
  • Loading branch information
ClearScriptLib committed May 1, 2022
1 parent 9000508 commit 70a9c6f
Show file tree
Hide file tree
Showing 697 changed files with 2,699 additions and 1,151 deletions.
6 changes: 3 additions & 3 deletions ClearScript/Exports/VersionSymbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#pragma once

#define CLEARSCRIPT_VERSION_STRING "7.2.4"
#define CLEARSCRIPT_VERSION_COMMA_SEPARATED 7,2,4
#define CLEARSCRIPT_VERSION_STRING_INFORMATIONAL "7.2.4"
#define CLEARSCRIPT_VERSION_STRING "7.2.5"
#define CLEARSCRIPT_VERSION_COMMA_SEPARATED 7,2,5
#define CLEARSCRIPT_VERSION_STRING_INFORMATIONAL "7.2.5"
#define CLEARSCRIPT_FILE_FLAGS 0L
112 changes: 79 additions & 33 deletions ClearScript/HostEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,24 @@
namespace Microsoft.ClearScript
{
/// <summary>
/// Represents a host event source.
/// Provides the base implementation for a host event source.
/// </summary>
/// <typeparam name="T">The event handler delegate type.</typeparam>
public class EventSource<T>
public abstract class EventSource
{
private readonly ScriptEngine engine;

internal EventSource(ScriptEngine engine, object source, EventInfo eventInfo)
{
MiscHelpers.VerifyNonNullArgument(engine, nameof(engine));
MiscHelpers.VerifyNonNullArgument(eventInfo, nameof(eventInfo));

if (eventInfo.EventHandlerType != typeof(T))
{
throw new ArgumentException("Invalid event type", nameof(eventInfo));
}

this.engine = engine;
Engine = engine;
Source = source;
EventInfo = eventInfo;
}

internal ScriptEngine Engine { get; }

internal abstract Type HandlerType { get; }

internal object Source { get; }

internal EventInfo EventInfo { get; }
Expand All @@ -42,33 +38,68 @@ internal EventSource(ScriptEngine engine, object source, EventInfo eventInfo)
/// Connects the host event source to the specified script handler function.
/// </summary>
/// <param name="scriptFunc">The script function that will handle the event.</param>
/// <returns>An <see cref="EventConnection{T}"/> that represents the connection.</returns>
public EventConnection<T> connect(object scriptFunc)
/// <returns>An <see cref="EventConnection"/> that represents the connection.</returns>
public EventConnection connect(object scriptFunc)
{
MiscHelpers.VerifyNonNullArgument(scriptFunc, nameof(scriptFunc));
return engine.CreateEventConnection<T>(Source, EventInfo, DelegateFactory.CreateDelegate(engine, scriptFunc, typeof(T)));
return Engine.CreateEventConnection(HandlerType, Source, EventInfo, DelegateFactory.CreateDelegate(Engine, scriptFunc, HandlerType));
}

// ReSharper restore InconsistentNaming

#endregion
}

internal interface IEventConnection
/// <summary>
/// Represents a host event source.
/// </summary>
/// <typeparam name="T">The event handler delegate type.</typeparam>
public sealed class EventSource<T> : EventSource
{
void Break();
internal EventSource(ScriptEngine engine, object source, EventInfo eventInfo)
: base(engine, source, eventInfo)
{
if (eventInfo.EventHandlerType != typeof(T))
{
throw new ArgumentException("Invalid event type (handler type mismatch)", nameof(eventInfo));
}
}

#region EventSource overrides

internal override Type HandlerType => typeof(T);

#endregion

#region script-callable interface

// ReSharper disable InconsistentNaming

/// <summary>
/// Connects the host event source to the specified script handler function.
/// </summary>
/// <param name="scriptFunc">The script function that will handle the event.</param>
/// <returns>An <see cref="EventConnection{T}"/> that represents the connection.</returns>
public new EventConnection<T> connect(object scriptFunc)
{
MiscHelpers.VerifyNonNullArgument(scriptFunc, nameof(scriptFunc));
return Engine.CreateEventConnection<T>(Source, EventInfo, DelegateFactory.CreateDelegate(Engine, scriptFunc, typeof(T)));
}

// ReSharper restore InconsistentNaming

#endregion
}

/// <summary>
/// Represents a connection between a host event source and a script handler function.
/// Provides the base implementation for a connection between a host event source and a script handler function.
/// </summary>
/// <typeparam name="T">The event handler delegate type.</typeparam>
public class EventConnection<T> : IEventConnection
public abstract class EventConnection
{
private readonly ScriptEngine engine;
private readonly object source;
private readonly EventInfo eventInfo;
private readonly Delegate handler;
private readonly MethodInfo removeMethod;
private readonly object[] parameters;
private readonly InterlockedOneWayFlag brokenFlag = new InterlockedOneWayFlag();

internal EventConnection(ScriptEngine engine, object source, EventInfo eventInfo, Delegate handler)
Expand All @@ -77,17 +108,28 @@ internal EventConnection(ScriptEngine engine, object source, EventInfo eventInfo
MiscHelpers.VerifyNonNullArgument(handler, nameof(handler));
MiscHelpers.VerifyNonNullArgument(eventInfo, nameof(eventInfo));

if (eventInfo.EventHandlerType != typeof(T))
if (!MiscHelpers.Try(out var addMethod, () => eventInfo.GetAddMethod(true)) || (addMethod == null))
{
throw new ArgumentException("Invalid event type", nameof(eventInfo));
throw new ArgumentException("Invalid event type (no accessible add method)", nameof(eventInfo));
}

if (!MiscHelpers.Try(out removeMethod, () => eventInfo.GetRemoveMethod(true)) || (removeMethod == null))
{
throw new ArgumentException("Invalid event type (no accessible remove method)", nameof(eventInfo));
}

this.engine = engine;
this.source = source;
this.eventInfo = eventInfo;
this.handler = handler;

eventInfo.AddEventHandler(source, handler);
addMethod.Invoke(source, parameters = new object[] { handler });
}

internal void Break()
{
if (brokenFlag.Set())
{
removeMethod.Invoke(source, parameters);
}
}

#region script-callable interface
Expand All @@ -105,17 +147,21 @@ public void disconnect()
// ReSharper restore InconsistentNaming

#endregion
}

#region IEventConnection implementation

void IEventConnection.Break()
/// <summary>
/// Represents a connection between a host event source and a script handler function.
/// </summary>
/// <typeparam name="T">The event handler delegate type.</typeparam>
public sealed class EventConnection<T> : EventConnection
{
internal EventConnection(ScriptEngine engine, object source, EventInfo eventInfo, Delegate handler)
: base(engine, source, eventInfo, handler)
{
if (brokenFlag.Set())
if (eventInfo.EventHandlerType != typeof(T))
{
eventInfo.RemoveEventHandler(source, handler);
throw new ArgumentException("Invalid event type (handler type mismatch)", nameof(eventInfo));
}
}

#endregion
}
}
16 changes: 10 additions & 6 deletions ClearScript/HostItem.InvokeMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ private MethodBindResult BindMethod(string name, Type[] typeArgs, object[] args,
}
else
{
result = BindMethodInternal(AccessContext, bindFlags, Target, name, typeArgs, args, bindArgs);
result = BindMethodInternal(signature, AccessContext, bindFlags, Target, name, typeArgs, args, bindArgs);
if (!result.IsPreferredMethod(this, name))
{
if (result is MethodBindSuccess)
Expand All @@ -127,7 +127,7 @@ private MethodBindResult BindMethod(string name, Type[] typeArgs, object[] args,

foreach (var altName in GetAltMethodNames(name, bindFlags))
{
var altResult = BindMethodInternal(AccessContext, bindFlags, Target, altName, typeArgs, args, bindArgs);
var altResult = BindMethodInternal(null, AccessContext, bindFlags, Target, altName, typeArgs, args, bindArgs);
if (altResult.IsUnblockedMethod(this))
{
result = altResult;
Expand All @@ -151,12 +151,16 @@ private MethodBindResult BindMethod(string name, Type[] typeArgs, object[] args,
return result;
}

private static MethodBindResult BindMethodInternal(Type bindContext, BindingFlags bindFlags, HostTarget target, string name, Type[] typeArgs, object[] args, object[] bindArgs)
private static MethodBindResult BindMethodInternal(BindSignature signature, Type bindContext, BindingFlags bindFlags, HostTarget target, string name, Type[] typeArgs, object[] args, object[] bindArgs)
{
// WARNING: BindSignature holds on to the specified typeArgs; subsequent modification
// will result in bugs that are difficult to diagnose. Create a copy if necessary.
if (signature == null)
{
// WARNING: BindSignature holds on to the specified typeArgs; subsequent modification
// will result in bugs that are difficult to diagnose. Create a copy if necessary.

signature = new BindSignature(bindContext, bindFlags, target, name, typeArgs, bindArgs);
}

var signature = new BindSignature(bindContext, bindFlags, target, name, typeArgs, bindArgs);
MethodBindResult result;

if (coreBindCache.TryGetValue(signature, out var rawResult))
Expand Down
3 changes: 1 addition & 2 deletions ClearScript/HostItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1479,9 +1479,8 @@ private object GetHostProperty(string name, BindingFlags invokeFlags, object[] a
var eventInfo = Target.Type.GetScriptableEvent(name, invokeFlags, AccessContext, DefaultAccess);
if (eventInfo != null)
{
var type = typeof(EventSource<>).MakeSpecificType(eventInfo.EventHandlerType);
isCacheable = (TargetDynamicMetaObject == null);
return type.CreateInstance(BindingFlags.NonPublic, Engine, Target.InvokeTarget, eventInfo);
return typeof(EventSource<>).MakeSpecificType(eventInfo.EventHandlerType).CreateInstance(BindingFlags.NonPublic, Engine, Target.InvokeTarget, eventInfo);
}

var field = Target.Type.GetScriptableField(name, invokeFlags, AccessContext, DefaultAccess);
Expand Down
10 changes: 5 additions & 5 deletions ClearScript/Properties/AssemblyInfo.Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
[assembly: InternalsVisibleTo("ClearScriptTest")]

[assembly: ComVisible(false)]
[assembly: AssemblyVersion("7.2.4")]
[assembly: AssemblyFileVersion("7.2.4")]
[assembly: AssemblyInformationalVersion("7.2.4")]
[assembly: AssemblyVersion("7.2.5")]
[assembly: AssemblyFileVersion("7.2.5")]
[assembly: AssemblyInformationalVersion("7.2.5")]

namespace Microsoft.ClearScript.Properties
{
internal static class ClearScriptVersion
{
public const string Triad = "7.2.4";
public const string Informational = "7.2.4";
public const string Triad = "7.2.5";
public const string Informational = "7.2.5";
}
}
6 changes: 3 additions & 3 deletions ClearScript/Properties/AssemblyInfo.V8.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
[assembly: InternalsVisibleTo("ClearScriptTest")]

[assembly: ComVisible(false)]
[assembly: AssemblyVersion("7.2.4")]
[assembly: AssemblyFileVersion("7.2.4")]
[assembly: AssemblyInformationalVersion("7.2.4")]
[assembly: AssemblyVersion("7.2.5")]
[assembly: AssemblyFileVersion("7.2.5")]
[assembly: AssemblyInformationalVersion("7.2.5")]
6 changes: 3 additions & 3 deletions ClearScript/Properties/AssemblyInfo.Windows.Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
[assembly: InternalsVisibleTo("ClearScriptTest")]

[assembly: ComVisible(false)]
[assembly: AssemblyVersion("7.2.4")]
[assembly: AssemblyFileVersion("7.2.4")]
[assembly: AssemblyInformationalVersion("7.2.4")]
[assembly: AssemblyVersion("7.2.5")]
[assembly: AssemblyFileVersion("7.2.5")]
[assembly: AssemblyInformationalVersion("7.2.5")]
6 changes: 3 additions & 3 deletions ClearScript/Properties/AssemblyInfo.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
[assembly: InternalsVisibleTo("ClearScriptTest")]

[assembly: ComVisible(false)]
[assembly: AssemblyVersion("7.2.4")]
[assembly: AssemblyFileVersion("7.2.4")]
[assembly: AssemblyInformationalVersion("7.2.4")]
[assembly: AssemblyVersion("7.2.5")]
[assembly: AssemblyFileVersion("7.2.5")]
[assembly: AssemblyInformationalVersion("7.2.5")]
40 changes: 34 additions & 6 deletions ClearScript/ScriptEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,19 @@ public bool DisableExtensionMethods
/// </remarks>
public object UndefinedImportValue { get; set; } = Undefined.Value;

/// <summary>
/// Gets or sets the engine's void result export value.
/// </summary>
/// <remarks>
/// Some script languages expect every subroutine call to return a value. When script code
/// written in such a language invokes a host method that explicitly returns no value (such
/// as a C#
/// <see href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/void">void</see>
/// method), the script engine returns the value of this property as a dummy result. The
/// default value is <see cref="VoidResult.Value"/>.
/// </remarks>
public object VoidResultValue { get; set; } = VoidResult.Value;

/// <summary>
/// Gets or sets a callback that can be used to halt script execution.
/// </summary>
Expand Down Expand Up @@ -1947,12 +1960,17 @@ internal HostTargetMemberData GetSharedHostObjectMemberData(HostObject target, T

private readonly EventConnectionMap eventConnectionMap = new EventConnectionMap();

internal EventConnection CreateEventConnection(Type handlerType, object source, EventInfo eventInfo, Delegate handler)
{
return eventConnectionMap.Create(this, handlerType, source, eventInfo, handler);
}

internal EventConnection<T> CreateEventConnection<T>(object source, EventInfo eventInfo, Delegate handler)
{
return eventConnectionMap.Create<T>(this, source, eventInfo, handler);
}

internal void BreakEventConnection(IEventConnection connection)
internal void BreakEventConnection(EventConnection connection)
{
eventConnectionMap.Break(connection);
}
Expand Down Expand Up @@ -2035,25 +2053,35 @@ internal sealed class ScriptFrame

private sealed class EventConnectionMap : IDisposable
{
private readonly HashSet<IEventConnection> map = new HashSet<IEventConnection>();
private readonly HashSet<EventConnection> map = new HashSet<EventConnection>();
private readonly InterlockedOneWayFlag disposedFlag = new InterlockedOneWayFlag();

internal EventConnection Create(ScriptEngine engine, Type handlerType, object source, EventInfo eventInfo, Delegate handler)
{
var connection = (EventConnection)typeof(EventConnection<>).MakeGenericType(handlerType).CreateInstance(BindingFlags.NonPublic, engine, source, eventInfo, handler);
Add(connection);
return connection;
}

internal EventConnection<T> Create<T>(ScriptEngine engine, object source, EventInfo eventInfo, Delegate handler)
{
var connection = new EventConnection<T>(engine, source, eventInfo, handler);
Add(connection);
return connection;
}

private void Add(EventConnection connection)
{
if (!disposedFlag.IsSet)
{
lock (map)
{
map.Add(connection);
}
}

return connection;
}

internal void Break(IEventConnection connection)
internal void Break(EventConnection connection)
{
var mustBreak = true;

Expand All @@ -2075,7 +2103,7 @@ public void Dispose()
{
if (disposedFlag.Set())
{
var connections = new List<IEventConnection>();
var connections = new List<EventConnection>();

lock (map)
{
Expand Down
3 changes: 2 additions & 1 deletion ClearScript/Undefined.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ namespace Microsoft.ClearScript
/// <remarks>
/// Some script languages support one or more special non-<c>null</c> values that represent
/// nonexistent, missing, unknown, or undefined data. The ClearScript library maps such values
/// to instances of this class.
/// to an instance of this class.
/// </remarks>
/// <seealso cref="ScriptEngine.UndefinedImportValue"/>
public class Undefined
{
/// <summary>
Expand Down
Loading

0 comments on commit 70a9c6f

Please sign in to comment.