Skip to content

Commit

Permalink
Version 7.3.5: Added support for script object identity comparison (G…
Browse files Browse the repository at this point in the history
…itHub Issue #422); overhauled scriptable enumerators to fix GitHub Issue #423; eliminated default V8 platform to fix process exit deadlock on Windows 7 (GitHub Issue #424); addressed performance regression reported in GitHub Issue #433; added a pair of ToRestrictedHostObject overloads (GitHub Issue #437); fixed a specific property accessor scriptability scenario (GitHub Issue #439); updated API documentation. Tested with V8 10.7.193.22.
  • Loading branch information
ClearScriptLib committed Nov 12, 2022
1 parent 02b7203 commit 5fe5403
Show file tree
Hide file tree
Showing 699 changed files with 3,174 additions and 972 deletions.
9 changes: 8 additions & 1 deletion ClearScript/CustomAttributes.NetFramework.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.ClearScript.Util;

namespace Microsoft.ClearScript
{
internal static partial class CustomAttributes
{
private static ConditionalWeakTable<ICustomAttributeProvider, CacheEntry> cache = new ConditionalWeakTable<ICustomAttributeProvider, CacheEntry>();

public static void ClearCache()
{
keyCache.Values.ForEach(key => attributeCache.Remove(key));
lock (cacheLock)
{
cache = new ConditionalWeakTable<ICustomAttributeProvider, CacheEntry>();
}
}
}
}
12 changes: 10 additions & 2 deletions ClearScript/CustomAttributes.NetStandard.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Reflection;
using System.Runtime.CompilerServices;

namespace Microsoft.ClearScript
{
internal static partial class CustomAttributes
{
private static readonly ConditionalWeakTable<ICustomAttributeProvider, CacheEntry> cache = new ConditionalWeakTable<ICustomAttributeProvider, CacheEntry>();

public static void ClearCache()
{
attributeCache.Clear();
lock (cacheLock)
{
cache.Clear();
}
}
}
}
138 changes: 130 additions & 8 deletions ClearScript/CustomAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,153 @@
// Licensed under the MIT license.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.ClearScript.Util;

namespace Microsoft.ClearScript
{
internal static partial class CustomAttributes
{
private static readonly ConcurrentDictionary<(ICustomAttributeProvider, Type), object> keyCache = new ConcurrentDictionary<(ICustomAttributeProvider, Type), object>();
private static readonly ConditionalWeakTable<object, object> attributeCache = new ConditionalWeakTable<object, object>();
private static readonly object cacheLock = new object();

public static T[] GetOrLoad<T>(ICustomAttributeProvider resource, bool inherit) where T : Attribute
{
return (T[])attributeCache.GetValue(GetKey<T>(resource), _ => HostSettings.CustomAttributeLoader.LoadCustomAttributes<T>(resource, inherit) ?? ArrayHelpers.GetEmptyArray<T>());
lock (cacheLock)
{
return GetOrLoad<T>(cache.GetOrCreateValue(resource), resource, inherit);
}
}

public static bool Has<T>(ICustomAttributeProvider resource, bool inherit) where T : Attribute
{
return GetOrLoad<T>(resource, inherit).Length > 0;
lock (cacheLock)
{
return Has<T>(cache.GetOrCreateValue(resource), resource, inherit);
}
}

private static object GetKey<T>(ICustomAttributeProvider resource) where T : Attribute
private static T[] GetOrLoad<T>(CacheEntry entry, ICustomAttributeProvider resource, bool inherit) where T : Attribute
{
return keyCache.GetOrAdd((resource, typeof(T)), _ => new object());
if (entry.TryGet<T>(out var attrs))
{
return attrs;
}

attrs = GetOrLoad<T>(GetIsBypass(entry, resource), resource, inherit);
entry.Add(attrs);

return attrs;
}

private static bool Has<T>(CacheEntry entry, ICustomAttributeProvider resource, bool inherit) where T : Attribute
{
return GetOrLoad<T>(entry, resource, inherit).Length > 0;
}

private static T[] GetOrLoad<T>(bool isBypass, ICustomAttributeProvider resource, bool inherit) where T : Attribute
{
var loader = isBypass ? HostSettings.DefaultCustomAttributeLoader : HostSettings.CustomAttributeLoader;
return loader.LoadCustomAttributes<T>(resource, inherit) ?? ArrayHelpers.GetEmptyArray<T>();
}

private static bool Has<T>(bool isBypass, ICustomAttributeProvider resource, bool inherit) where T : Attribute
{
return GetOrLoad<T>(isBypass, resource, inherit).Length > 0;
}

private static bool GetIsBypass(ICustomAttributeProvider resource)
{
// ReSharper disable once InconsistentlySynchronizedField
return GetIsBypass(cache.GetOrCreateValue(resource), resource);
}

private static bool GetIsBypass(CacheEntry entry, ICustomAttributeProvider resource)
{
if (!entry.IsBypass.HasValue)
{
entry.IsBypass = GetIsBypassInternal(resource);
}

return entry.IsBypass.Value;
}

private static bool GetIsBypassInternal(ICustomAttributeProvider resource)
{
if (Has<BypassCustomAttributeLoaderAttribute>(true, resource, false))
{
return true;
}

var parent = GetParent(resource);
if (parent != null)
{
return GetIsBypass(parent);
}

return false;
}

private static ICustomAttributeProvider GetParent(ICustomAttributeProvider resource)
{
if (resource is ParameterInfo parameter)
{
return parameter.Member;
}

if (resource is Type type)
{
return (type.DeclaringType as ICustomAttributeProvider) ?? type.Module;
}

if (resource is MemberInfo member)
{
return member.DeclaringType;
}

if (resource is Module module)
{
return module.Assembly;
}

return null;
}

#region Nested type: CacheEntry

// ReSharper disable ClassNeverInstantiated.Local

private sealed class CacheEntry
{
private readonly Dictionary<Type, object> map = new Dictionary<Type, object>();

public bool? IsBypass { get; set; }

public void Add<T>(T[] attrs)
{
map.Add(typeof(T), attrs);
}

public bool TryGet<T>(out T[] attrs)
{
if (map.TryGetValue(typeof(T), out var attrsObject))
{
attrs = attrsObject as T[];
return true;
}

attrs = null;
return false;
}
}

// ReSharper restore ClassNeverInstantiated.Local

#endregion
}

[AttributeUsage(AttributeTargets.All, Inherited = false)]
internal sealed class BypassCustomAttributeLoaderAttribute : Attribute
{
}
}
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.3.4"
#define CLEARSCRIPT_VERSION_COMMA_SEPARATED 7,3,4
#define CLEARSCRIPT_VERSION_STRING_INFORMATIONAL "7.3.4"
#define CLEARSCRIPT_VERSION_STRING "7.3.5"
#define CLEARSCRIPT_VERSION_COMMA_SEPARATED 7,3,5
#define CLEARSCRIPT_VERSION_STRING_INFORMATIONAL "7.3.5"
#define CLEARSCRIPT_FILE_FLAGS 0L
41 changes: 41 additions & 0 deletions ClearScript/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,46 @@ public static object ToRestrictedHostObject<T>(this T target, ScriptEngine engin
MiscHelpers.VerifyNonNullArgument(engine, nameof(engine));
return HostItem.Wrap(engine, target, typeof(T));
}

/// <summary>
/// Converts an object to a host object with a type restriction specified as a
/// <see cref="Type"/> instance, for use with script code currently running on the calling
/// thread.
/// </summary>
/// <param name="target">The object to convert to a host object for use with script code.</param>
/// <param name="type">The type whose members are to be made accessible from script code.</param>
/// <returns>A host object with the specified type restriction.</returns>
public static object ToRestrictedHostObject(this object target, Type type)
{
return target.ToRestrictedHostObject(type, ScriptEngine.Current);
}

/// <summary>
/// Converts an object to a host object with a type restriction specified as a
/// <see cref="Type"/> instance, for use with script code running in the specified script
/// engine.
/// </summary>
/// <param name="target">The object to convert to a host object for use with script code.</param>
/// <param name="type">The type whose members are to be made accessible from script code.</param>
/// <param name="engine">The script engine in which the host object will be used.</param>
/// <returns>A host object with the specified type restriction.</returns>
public static object ToRestrictedHostObject(this object target, Type type, ScriptEngine engine)
{
MiscHelpers.VerifyNonNullArgument(target, nameof(target));
MiscHelpers.VerifyNonNullArgument(type, nameof(type));
MiscHelpers.VerifyNonNullArgument(engine, nameof(engine));

if (!MiscHelpers.Try(out var holder, () => typeof(Holder<>).MakeGenericType(type).CreateInstance()))
{
throw new ArgumentException("The specified type is invalid", nameof(type));
}

if (!MiscHelpers.Try(() => ((IHolder)holder).Value = target))
{
throw new ArgumentException("The target object is incompatible with the specified type", nameof(target));
}

return HostItem.Wrap(engine, target, type);
}
}
}
10 changes: 5 additions & 5 deletions ClearScript/HostItem.NetFramework.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private static HostItem Create(ScriptEngine engine, HostTarget target, HostItemF

private object CreateAsyncEnumerator<T>(IEnumerable<T> enumerable)
{
return HostObject.Wrap(enumerable.GetEnumerator().ToAsyncEnumerator(Engine), typeof(IAsyncEnumeratorPromise<T>));
return HostObject.Wrap(enumerable.GetEnumerator().ToScriptableAsyncEnumerator(Engine), typeof(IScriptableAsyncEnumerator<T>));
}

private object CreateAsyncEnumerator()
Expand All @@ -35,16 +35,16 @@ private object CreateAsyncEnumerator()
{
if ((Target.InvokeTarget != null) && Target.Type.IsAssignableToGenericType(typeof(IEnumerable<>), out var typeArgs))
{
var enumerableHelpersHostItem = Wrap(Engine, typeof(EnumerableHelpers<>).MakeGenericType(typeArgs).InvokeMember("HostType", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField, null, null, null), HostItemFlags.PrivateAccess);
if (MiscHelpers.Try(out var enumerator, () => ((IDynamic)enumerableHelpersHostItem).InvokeMethod("GetAsyncEnumerator", this, Engine)))
var helpersHostItem = Wrap(Engine, typeof(ScriptableEnumerableHelpers<>).MakeGenericType(typeArgs).InvokeMember("HostType", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField, null, null, null), HostItemFlags.PrivateAccess);
if (MiscHelpers.Try(out var enumerator, () => ((IDynamic)helpersHostItem).InvokeMethod("GetScriptableAsyncEnumerator", this, Engine)))
{
return enumerator;
}
}
else if (BindSpecialTarget(out IEnumerable _))
{
var enumerableHelpersHostItem = Wrap(Engine, EnumerableHelpers.HostType, HostItemFlags.PrivateAccess);
if (MiscHelpers.Try(out var enumerator, () => ((IDynamic)enumerableHelpersHostItem).InvokeMethod("GetAsyncEnumerator", this, Engine)))
var helpersHostItem = Wrap(Engine, ScriptableEnumerableHelpers.HostType, HostItemFlags.PrivateAccess);
if (MiscHelpers.Try(out var enumerator, () => ((IDynamic)helpersHostItem).InvokeMethod("GetScriptableAsyncEnumerator", this, Engine)))
{
return enumerator;
}
Expand Down
14 changes: 7 additions & 7 deletions ClearScript/HostItem.NetStandard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private static HostItem Create(ScriptEngine engine, HostTarget target, HostItemF

private object CreateAsyncEnumerator<T>(IEnumerable<T> enumerable)
{
return HostObject.Wrap(enumerable.GetEnumerator().ToAsyncEnumerator(Engine), typeof(IAsyncEnumeratorPromise<T>));
return HostObject.Wrap(enumerable.GetEnumerator().ToScriptableAsyncEnumerator(Engine), typeof(IScriptableAsyncEnumerator<T>));
}

private object CreateAsyncEnumerator()
Expand All @@ -45,24 +45,24 @@ private object CreateAsyncEnumerator()
{
if ((Target.InvokeTarget != null) && Target.Type.IsAssignableToGenericType(typeof(IAsyncEnumerable<>), out var typeArgs))
{
var enumerableHelpersHostItem = Wrap(Engine, typeof(EnumerableHelpers<>).MakeGenericType(typeArgs).InvokeMember("HostType", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField, null, null, null), HostItemFlags.PrivateAccess);
if (MiscHelpers.Try(out var enumerator, () => ((IDynamic)enumerableHelpersHostItem).InvokeMethod("GetAsyncEnumerator", this, Engine)))
var helpersHostItem = Wrap(Engine, typeof(ScriptableEnumerableHelpers<>).MakeGenericType(typeArgs).InvokeMember("HostType", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField, null, null, null), HostItemFlags.PrivateAccess);
if (MiscHelpers.Try(out var enumerator, () => ((IDynamic)helpersHostItem).InvokeMethod("GetScriptableAsyncEnumerator", this, Engine)))
{
return enumerator;
}
}
else if ((Target.InvokeTarget != null) && Target.Type.IsAssignableToGenericType(typeof(IEnumerable<>), out typeArgs))
{
var enumerableHelpersHostItem = Wrap(Engine, typeof(EnumerableHelpers<>).MakeGenericType(typeArgs).InvokeMember("HostType", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField, null, null, null), HostItemFlags.PrivateAccess);
if (MiscHelpers.Try(out var enumerator, () => ((IDynamic)enumerableHelpersHostItem).InvokeMethod("GetAsyncEnumerator", this, Engine)))
var helpersHostItem = Wrap(Engine, typeof(ScriptableEnumerableHelpers<>).MakeGenericType(typeArgs).InvokeMember("HostType", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField, null, null, null), HostItemFlags.PrivateAccess);
if (MiscHelpers.Try(out var enumerator, () => ((IDynamic)helpersHostItem).InvokeMethod("GetScriptableAsyncEnumerator", this, Engine)))
{
return enumerator;
}
}
else if (BindSpecialTarget(out IEnumerable _))
{
var enumerableHelpersHostItem = Wrap(Engine, EnumerableHelpers.HostType, HostItemFlags.PrivateAccess);
if (MiscHelpers.Try(out var enumerator, () => ((IDynamic)enumerableHelpersHostItem).InvokeMethod("GetAsyncEnumerator", this, Engine)))
var helpersHostItem = Wrap(Engine, ScriptableEnumerableHelpers.HostType, HostItemFlags.PrivateAccess);
if (MiscHelpers.Try(out var enumerator, () => ((IDynamic)helpersHostItem).InvokeMethod("GetScriptableAsyncEnumerator", this, Engine)))
{
return enumerator;
}
Expand Down
Loading

0 comments on commit 5fe5403

Please sign in to comment.