Skip to content

Commit

Permalink
Version 7.2.4: Added V8ScriptEngineFlags.UseCaseInsensitiveMemberBind…
Browse files Browse the repository at this point in the history
…ing (GitHub Issue #363); restored compatibility with older Linux systems going back to glibc-2.23 (GitHub Issue #362); overhauled attribute access and added custom attribute loaders; added case-insensitivity support to PropertyBag and DynamicHostObject; added .NET 6 targets to test projects; updated API documentation. Tested with V8 10.0.139.8.
  • Loading branch information
ClearScriptLib committed Mar 29, 2022
1 parent 3e13f32 commit d6cc677
Show file tree
Hide file tree
Showing 672 changed files with 2,241 additions and 981 deletions.
2 changes: 2 additions & 0 deletions ClearScript.NoV8.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=E_00F3in/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=FILEEXISTS/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=fine/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=foobarbaz/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=FUNCDESC/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=FUNCFLAGS/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=guids/@EntryIndexedValue">True</s:Boolean>
Expand All @@ -92,6 +93,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=MEMBERNOTFOUND/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=memid/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=MSXML/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=NETFRAMEWORK/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=NOINTERFACE/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Numerics/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=oleaut/@EntryIndexedValue">True</s:Boolean>
Expand Down
1 change: 1 addition & 0 deletions ClearScript.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=MEMBERNOTFOUND/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=memid/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=MSXML/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=NETFRAMEWORK/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=NOINTERFACE/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Numerics/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=oleaut/@EntryIndexedValue">True</s:Boolean>
Expand Down
2 changes: 1 addition & 1 deletion ClearScript/CanonicalRefTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ private static ICanonicalRefMap GetMap(object obj)
type == typeof(DateTime) ||
type == typeof(DateTimeOffset) ||
type == typeof(TimeSpan) ||
type.GetCustomAttributes(typeof(ImmutableValueAttribute), false).Any())
type.GetOrLoadCustomAttributes<ImmutableValueAttribute>(false).Any())
{
map = (ICanonicalRefMap)typeof(CanonicalRefMap<>).MakeGenericType(type).CreateInstance();
}
Expand Down
65 changes: 65 additions & 0 deletions ClearScript/CustomAttributeLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Linq;
using System.Reflection;

namespace Microsoft.ClearScript
{
/// <summary>
/// Represents a custom attribute loader.
/// </summary>
public class CustomAttributeLoader
{
// ReSharper disable EmptyConstructor

/// <summary>
/// Initializes a new <see cref="CustomAttributeLoader"/> instance.
/// </summary>
public CustomAttributeLoader()
{
// the help file builder (SHFB) insists on an empty constructor here
}

// ReSharper restore EmptyConstructor


/// <summary>
/// Loads custom attributes of the specified type for the given resource.
/// </summary>
/// <typeparam name="T">The type, or a base type, of the custom attributes to load.</typeparam>
/// <param name="resource">The resource for which to load custom attributes of type <typeparamref name="T"/>.</param>
/// <param name="inherit"><c>True</c> to include custom attributes of type <typeparamref name="T"/> defined for ancestors of <paramref name="resource"/>, <c>false</c> otherwise.</param>
/// <returns>An array of custom attributes of type <typeparamref name="T"/>.</returns>.
/// <remarks>
/// This method is performance-critical. Overrides must not invoke script engine methods or
/// other ClearScript functionality. The base implementation loads custom attributes via
/// reflection.
/// </remarks>
public virtual T[] LoadCustomAttributes<T>(ICustomAttributeProvider resource, bool inherit) where T : Attribute
{
if (resource is MemberInfo member)
{
return Attribute.GetCustomAttributes(member, typeof(T), inherit).OfType<T>().ToArray();
}

if (resource is ParameterInfo parameter)
{
return Attribute.GetCustomAttributes(parameter, typeof(T), inherit).OfType<T>().ToArray();
}

if (resource is Assembly assembly)
{
return Attribute.GetCustomAttributes(assembly, typeof(T), inherit).OfType<T>().ToArray();
}

if (resource is Module module)
{
return Attribute.GetCustomAttributes(module, typeof(T), inherit).OfType<T>().ToArray();
}

return resource.GetCustomAttributes(typeof(T), inherit).OfType<T>().ToArray();
}
}
}
15 changes: 15 additions & 0 deletions ClearScript/CustomAttributes.NetFramework.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using Microsoft.ClearScript.Util;

namespace Microsoft.ClearScript
{
internal static partial class CustomAttributes
{
public static void ClearCache()
{
keyCache.Values.ForEach(key => attributeCache.Remove(key));
}
}
}
13 changes: 13 additions & 0 deletions ClearScript/CustomAttributes.NetStandard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

namespace Microsoft.ClearScript
{
internal static partial class CustomAttributes
{
public static void ClearCache()
{
attributeCache.Clear();
}
}
}
32 changes: 32 additions & 0 deletions ClearScript/CustomAttributes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections.Concurrent;
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>();

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>());
}

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

private static object GetKey<T>(ICustomAttributeProvider resource) where T : Attribute
{
return keyCache.GetOrAdd((resource, typeof(T)), _ => new object());
}
}
}
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.3"
#define CLEARSCRIPT_VERSION_COMMA_SEPARATED 7,2,3
#define CLEARSCRIPT_VERSION_STRING_INFORMATIONAL "7.2.3"
#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_FILE_FLAGS 0L
2 changes: 1 addition & 1 deletion ClearScript/ExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void RebuildSummary()

private static bool IsScriptableExtensionMethod(MethodInfo method, Type accessContext, ScriptAccess defaultAccess)
{
return method.IsScriptable(accessContext, defaultAccess) && method.IsDefined(typeof(ExtensionAttribute), false);
return method.IsScriptable(accessContext, defaultAccess) && method.HasCustomAttributes<ExtensionAttribute>(false);
}
}

Expand Down
3 changes: 1 addition & 2 deletions ClearScript/HostFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.ClearScript.Util;
using Microsoft.ClearScript.Util.COM;
Expand Down Expand Up @@ -1665,7 +1664,7 @@ public HostTypeCollection typeLibEnums<T>(T obj, HostTypeCollection collection =
}
}
}
else if (type.IsImport && (type.Assembly.GetCustomAttribute(typeof(ImportedFromTypeLibAttribute)) != null))
else if (type.IsImport && (type.Assembly.GetOrLoadCustomAttribute<ImportedFromTypeLibAttribute>(false) != null))
{
type.Assembly.GetReferencedEnums().ForEach(collection.AddType);
return collection;
Expand Down
4 changes: 2 additions & 2 deletions ClearScript/HostIndexedProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ public override bool TryInvokeAuxMember(IHostInvokeContext context, string membe
{
if (invokeFlags.HasFlag(BindingFlags.InvokeMethod))
{
if (memberName == "get")
if (string.Equals(memberName, "get", invokeFlags.GetMemberNameComparison()))
{
result = target.InvokeMember(name, BindingFlags.GetProperty, args, bindArgs, null, true);
return true;
}

if (memberName == "set")
if (string.Equals(memberName, "set", invokeFlags.GetMemberNameComparison()))
{
result = target.InvokeMember(name, BindingFlags.SetProperty, args, bindArgs, null, true);
return true;
Expand Down
36 changes: 28 additions & 8 deletions ClearScript/HostItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,12 @@ protected virtual void RemoveExpandoMemberName(string name)

#region member invocation

private bool UseCaseInsensitiveMemberBinding => Engine.UseCaseInsensitiveMemberBinding;

private StringComparison MemberNameComparison => UseCaseInsensitiveMemberBinding ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;

private StringComparer MemberNameComparer => UseCaseInsensitiveMemberBinding ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;

private T HostInvoke<T>(Func<T> func)
{
BindTargetMemberData();
Expand All @@ -824,6 +830,11 @@ private BindingFlags GetCommonBindFlags()
bindFlags |= BindingFlags.Instance;
}

if (UseCaseInsensitiveMemberBinding)
{
bindFlags |= BindingFlags.IgnoreCase;
}

return bindFlags;
}

Expand Down Expand Up @@ -874,6 +885,15 @@ private void AdjustInvokeFlags(ref BindingFlags invokeFlags)
invokeFlags &= ~BindingFlags.Instance;
}

if (UseCaseInsensitiveMemberBinding)
{
invokeFlags |= BindingFlags.IgnoreCase;
}
else
{
invokeFlags &= ~BindingFlags.IgnoreCase;
}

if (invokeFlags.HasFlag(BindingFlags.GetProperty))
{
invokeFlags |= BindingFlags.GetField;
Expand Down Expand Up @@ -1293,7 +1313,7 @@ private object InvokeHostMember(string name, BindingFlags invokeFlags, object[]
}
}

if (ThisReflect.GetMethods(GetMethodBindFlags()).Any(method => method.Name == name))
if (ThisReflect.GetMethods(GetMethodBindFlags()).Any(method => string.Equals(method.Name, name, MemberNameComparison)))
{
// The target appears to have a method with the right name, but it could be an
// extension method that fails to bind. If that happens, we should attempt the
Expand Down Expand Up @@ -1416,7 +1436,7 @@ private object GetHostProperty(string name, BindingFlags invokeFlags, object[] a
{
if (HostMethodMap == null)
{
HostMethodMap = new Dictionary<string, HostMethod>();
HostMethodMap = new Dictionary<string, HostMethod>(MemberNameComparer);
}

if (!HostMethodMap.TryGetValue(name, out var hostMethod))
Expand Down Expand Up @@ -1490,12 +1510,12 @@ private object GetHostProperty(string name, BindingFlags invokeFlags, object[] a
return hostIndexedProperty;
}

var method = ThisReflect.GetMethods(GetMethodBindFlags()).FirstOrDefault(testMethod => testMethod.Name == name);
var method = ThisReflect.GetMethods(GetMethodBindFlags()).FirstOrDefault(testMethod => string.Equals(testMethod.Name, name, MemberNameComparison));
if (method != null)
{
if (HostMethodMap == null)
{
HostMethodMap = new Dictionary<string, HostMethod>();
HostMethodMap = new Dictionary<string, HostMethod>(MemberNameComparer);
}

if (!HostMethodMap.TryGetValue(name, out var hostMethod))
Expand Down Expand Up @@ -1819,7 +1839,7 @@ public override bool TryConvert(ConvertBinder binder, out object result)

FieldInfo IReflect.GetField(string name, BindingFlags bindFlags)
{
var fields = ThisReflect.GetFields(bindFlags).Where(field => field.Name == name).ToArray();
var fields = ThisReflect.GetFields(bindFlags).Where(field => string.Equals(field.Name, name, bindFlags.GetMemberNameComparison())).ToArray();
if (fields.Length < 1)
{
return null;
Expand Down Expand Up @@ -1849,7 +1869,7 @@ FieldInfo[] IReflect.GetFields(BindingFlags bindFlags)

MemberInfo[] IReflect.GetMember(string name, BindingFlags bindFlags)
{
return ThisReflect.GetMembers(bindFlags).Where(member => member.Name == name).ToArray();
return ThisReflect.GetMembers(bindFlags).Where(member => string.Equals(member.Name, name, bindFlags.GetMemberNameComparison())).ToArray();
}

MemberInfo[] IReflect.GetMembers(BindingFlags bindFlags)
Expand All @@ -1859,7 +1879,7 @@ MemberInfo[] IReflect.GetMembers(BindingFlags bindFlags)

MethodInfo IReflect.GetMethod(string name, BindingFlags bindFlags)
{
var methods = ThisReflect.GetMethods(bindFlags).Where(method => method.Name == name).ToArray();
var methods = ThisReflect.GetMethods(bindFlags).Where(method => string.Equals(method.Name, name, bindFlags.GetMemberNameComparison())).ToArray();
if (methods.Length < 1)
{
return null;
Expand Down Expand Up @@ -1913,7 +1933,7 @@ PropertyInfo IReflect.GetProperty(string name, BindingFlags bindFlags, Binder bi

PropertyInfo IReflect.GetProperty(string name, BindingFlags bindFlags)
{
var properties = ThisReflect.GetProperties(bindFlags).Where(property => property.Name == name).ToArray();
var properties = ThisReflect.GetProperties(bindFlags).Where(property => string.Equals(property.Name, name, bindFlags.GetMemberNameComparison())).ToArray();
if (properties.Length < 1)
{
return null;
Expand Down
17 changes: 17 additions & 0 deletions ClearScript/HostSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ namespace Microsoft.ClearScript
/// </summary>
public static class HostSettings
{
private static CustomAttributeLoader customAttributeLoader;
private static readonly CustomAttributeLoader defaultCustomAttributeLoader = new CustomAttributeLoader();

/// <summary>
/// Enables or disables assembly table usage.
/// </summary>
Expand All @@ -34,5 +37,19 @@ public static class HostSettings
/// resources such as native assemblies and related data files.
/// </remarks>
public static string AuxiliarySearchPath { get; set; }

/// <summary>
/// Gets or sets the custom attribute loader for ClearScript.
/// </summary>
public static CustomAttributeLoader CustomAttributeLoader
{
get => customAttributeLoader ?? defaultCustomAttributeLoader;

set
{
customAttributeLoader = value;
CustomAttributes.ClearCache();
}
}
}
}
2 changes: 1 addition & 1 deletion ClearScript/HostType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public override bool TryInvokeAuxMember(IHostInvokeContext context, string name,
var type = GetSpecificTypeNoThrow();
if (type != null)
{
var nestedTypes = type.GetScriptableNestedTypes(invokeFlags, context.AccessContext, context.DefaultAccess).Where(testType => testType.GetRootName() == name).ToIList();
var nestedTypes = type.GetScriptableNestedTypes(invokeFlags, context.AccessContext, context.DefaultAccess).Where(testType => string.Equals(testType.GetRootName(), name, invokeFlags.GetMemberNameComparison())).ToIList();
if (nestedTypes.Count > 0)
{
var tempResult = Wrap(nestedTypes.Select(testType => testType.ApplyTypeArguments(type.GetGenericArguments())).ToArray());
Expand Down
6 changes: 3 additions & 3 deletions ClearScript/HostVariable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,23 +92,23 @@ public override bool TryInvokeAuxMember(IHostInvokeContext context, string name,
BindingFlags.PutDispProperty |
BindingFlags.PutRefDispProperty;

if (name == "out")
if (string.Equals(name, "out", invokeFlags.GetMemberNameComparison()))
{
if ((invokeFlags & getPropertyFlags) != 0)
{
result = new OutArg<T>(this);
return true;
}
}
else if (name == "ref")
else if (string.Equals(name, "ref", invokeFlags.GetMemberNameComparison()))
{
if ((invokeFlags & getPropertyFlags) != 0)
{
result = new RefArg<T>(this);
return true;
}
}
else if (name == "value")
else if (string.Equals(name, "value", invokeFlags.GetMemberNameComparison()))
{
if (invokeFlags.HasFlag(BindingFlags.InvokeMethod))
{
Expand Down
Loading

0 comments on commit d6cc677

Please sign in to comment.