Skip to content

Commit d3522eb

Browse files
committed
In configuration settings of the ChakraCore JS engine was added one new property - AllowReflection (default false)
1 parent 7613af8 commit d3522eb

File tree

7 files changed

+287
-10
lines changed

7 files changed

+287
-10
lines changed

src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreJsEngine.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public sealed class ChakraCoreJsEngine : JsEngineBase
7373
/// <summary>
7474
/// Type mapper
7575
/// </summary>
76-
private TypeMapper _typeMapper = new TypeMapper();
76+
private TypeMapper _typeMapper;
7777

7878
/// <summary>
7979
/// Callback for continuation of promise
@@ -148,6 +148,7 @@ public ChakraCoreJsEngine(ChakraCoreSettings settings)
148148
attributes |= JsRuntimeAttributes.EnableExperimentalFeatures;
149149
}
150150

151+
_typeMapper = new TypeMapper(chakraCoreSettings.AllowReflection);
151152
#if NETSTANDARD1_3
152153
_dispatcher = new ScriptDispatcher();
153154
#else

src/JavaScriptEngineSwitcher.ChakraCore/ChakraCoreSettings.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ public sealed class ChakraCoreSettings
2828
private int _maxStackSize;
2929

3030
#endif
31+
/// <summary>
32+
/// Gets or sets a flag for whether to allow the usage of reflection API in the script code
33+
/// </summary>
34+
/// <remarks>
35+
/// This affects <see cref="Object.GetType"/>, <c>Exception.GetType</c>,
36+
/// <c>Exception.TargetSite</c> and <c>Delegate.Method</c>.
37+
/// </remarks>
38+
public bool AllowReflection
39+
{
40+
get;
41+
set;
42+
}
43+
3144
/// <summary>
3245
/// Gets or sets a flag for whether to disable any background work (such as garbage collection)
3346
/// on background threads
@@ -136,6 +149,7 @@ public ChakraCoreSettings()
136149
{
137150
bool is64BitProcess = Utils.Is64BitProcess();
138151

152+
AllowReflection = false;
139153
DisableBackgroundWork = false;
140154
DisableEval = false;
141155
DisableExecutablePageAllocation = false;

src/JavaScriptEngineSwitcher.ChakraCore/Helpers/ReflectionHelpers.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ namespace JavaScriptEngineSwitcher.ChakraCore.Helpers
1212
/// </summary>
1313
internal static class ReflectionHelpers
1414
{
15+
private static readonly PropertyInfo[] _disallowedProperties =
16+
{
17+
typeof(Delegate).GetProperty("Method"),
18+
typeof(Exception).GetProperty("TargetSite")
19+
};
20+
21+
private static readonly MethodInfo[] _disallowedMethods =
22+
{
23+
typeof(object).GetMethod("GetType"),
24+
typeof(Exception).GetMethod("GetType")
25+
};
26+
27+
1528
public static BindingFlags GetDefaultBindingFlags(bool instance)
1629
{
1730
BindingFlags bindingFlags = BindingFlags.Public;
@@ -27,6 +40,20 @@ public static BindingFlags GetDefaultBindingFlags(bool instance)
2740
return bindingFlags;
2841
}
2942

43+
public static bool IsAllowedProperty(PropertyInfo property)
44+
{
45+
bool isAllowed = !_disallowedProperties.Contains(property, MemberComparer<PropertyInfo>.Instance);
46+
47+
return isAllowed;
48+
}
49+
50+
public static bool IsAllowedMethod(MethodInfo method)
51+
{
52+
bool isAllowed = !_disallowedMethods.Contains(method, MemberComparer<MethodInfo>.Instance);
53+
54+
return isAllowed;
55+
}
56+
3057
public static bool IsFullyFledgedMethod(MethodInfo method)
3158
{
3259
if (!method.Attributes.HasFlag(MethodAttributes.SpecialName))
@@ -221,6 +248,38 @@ private static bool CompareParameterTypes(object[] argValues, Type[] argTypes, T
221248
}
222249

223250

251+
private sealed class MemberComparer<T> : EqualityComparer<T>
252+
where T : MemberInfo
253+
{
254+
public static MemberComparer<T> Instance { get; } = new MemberComparer<T>();
255+
256+
257+
private MemberComparer()
258+
{ }
259+
260+
261+
#region MemberComparer overrides
262+
263+
public override bool Equals(T x, T y)
264+
{
265+
return x.Module == y.Module
266+
#if !NETSTANDARD1_3
267+
&& x.MetadataToken == y.MetadataToken
268+
#else
269+
&& x.DeclaringType == y.DeclaringType
270+
&& x.Name == y.Name
271+
#endif
272+
;
273+
}
274+
275+
public override int GetHashCode(T obj)
276+
{
277+
return obj != null ? obj.GetHashCode() : 0;
278+
}
279+
280+
#endregion
281+
}
282+
224283
private sealed class MethodWithMetadata
225284
{
226285
public MethodBase Method

src/JavaScriptEngineSwitcher.ChakraCore/JavaScriptEngineSwitcher.ChakraCore.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ This package does not contain the native implementations of ChakraCore. Therefor
3131
* JavaScriptEngineSwitcher.ChakraCore.Native.osx-x64</Description>
3232
<PackageTags>$(PackageCommonTags);ChakraCore</PackageTags>
3333
<PackageIconFullPath>../../Icons/JavaScriptEngineSwitcher_ChakraCore_Logo128x128.png</PackageIconFullPath>
34-
<PackageReleaseNotes>ChakraCore was updated to version of November 9, 2022.</PackageReleaseNotes>
34+
<PackageReleaseNotes>In configuration settings of the ChakraCore JS engine was added one new property - `AllowReflection` (default `false`).</PackageReleaseNotes>
3535
</PropertyGroup>
3636

3737
<ItemGroup>

src/JavaScriptEngineSwitcher.ChakraCore/JsRt/TypeMapper.cs

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ internal sealed class TypeMapper : IDisposable
3131
/// </summary>
3232
private const string ExternalObjectPropertyName = "_JavaScriptEngineSwitcher_externalObject";
3333

34+
/// <summary>
35+
/// Flag for whether to allow the usage of reflection API in the script code
36+
/// </summary>
37+
private readonly bool _allowReflection;
38+
3439
/// <summary>
3540
/// Storage for lazy-initialized embedded objects
3641
/// </summary>
@@ -80,8 +85,11 @@ internal sealed class TypeMapper : IDisposable
8085
/// <summary>
8186
/// Constructs an instance of type mapper
8287
/// </summary>
83-
public TypeMapper()
84-
{ }
88+
/// <param name="allowReflection">Flag for whether to allow the usage of reflection API in the script code</param>
89+
public TypeMapper(bool allowReflection)
90+
{
91+
_allowReflection = allowReflection;
92+
}
8593

8694

8795
/// <summary>
@@ -604,6 +612,11 @@ private void ProjectProperties(EmbeddedItem externalItem)
604612

605613
foreach (PropertyInfo property in properties)
606614
{
615+
if (!IsAvailableProperty(property))
616+
{
617+
continue;
618+
}
619+
607620
string propertyName = property.Name;
608621

609622
JsValue descriptorValue = JsValue.CreateObject();
@@ -732,14 +745,13 @@ private void ProjectMethods(EmbeddedItem externalItem)
732745

733746
string typeName = type.FullName;
734747
BindingFlags defaultBindingFlags = ReflectionHelpers.GetDefaultBindingFlags(instance);
735-
IEnumerable<MethodInfo> methods = type.GetMethods(defaultBindingFlags)
736-
.Where(ReflectionHelpers.IsFullyFledgedMethod);
737-
IEnumerable<IGrouping<string, MethodInfo>> methodGroups = methods.GroupBy(m => m.Name);
748+
MethodInfo[] methods = type.GetMethods(defaultBindingFlags);
749+
Dictionary<string, List<MethodInfo>> availableMethodGroups = GetAvailableMethodGroups(methods);
738750

739-
foreach (IGrouping<string, MethodInfo> methodGroup in methodGroups)
751+
foreach (KeyValuePair<string, List<MethodInfo>> methodGroup in availableMethodGroups)
740752
{
741753
string methodName = methodGroup.Key;
742-
MethodInfo[] methodCandidates = methodGroup.ToArray();
754+
MethodInfo[] methodCandidates = methodGroup.Value.ToArray();
743755

744756
JsNativeFunction nativeFunction = (callee, isConstructCall, args, argCount, callbackData) =>
745757
{
@@ -808,6 +820,55 @@ private void ProjectMethods(EmbeddedItem externalItem)
808820
}
809821
}
810822

823+
[MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)]
824+
private bool IsAvailableProperty(PropertyInfo property)
825+
{
826+
if (_allowReflection)
827+
{
828+
return true;
829+
}
830+
831+
bool isAvailable = ReflectionHelpers.IsAllowedProperty(property);
832+
833+
return isAvailable;
834+
}
835+
836+
[MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)]
837+
private Dictionary<string, List<MethodInfo>> GetAvailableMethodGroups(MethodInfo[] methods)
838+
{
839+
int methodCount = methods.Length;
840+
if (methodCount == 0)
841+
{
842+
return new Dictionary<string, List<MethodInfo>>();
843+
}
844+
845+
var availableMethodGroups = new Dictionary<string, List<MethodInfo>>(methodCount);
846+
847+
foreach (MethodInfo method in methods)
848+
{
849+
if (!ReflectionHelpers.IsFullyFledgedMethod(method)
850+
|| (!_allowReflection && !ReflectionHelpers.IsAllowedMethod(method)))
851+
{
852+
continue;
853+
}
854+
855+
string methodName = method.Name;
856+
List<MethodInfo> methodGroup;
857+
858+
if (availableMethodGroups.TryGetValue(methodName, out methodGroup))
859+
{
860+
methodGroup.Add(method);
861+
}
862+
else
863+
{
864+
methodGroup = new List<MethodInfo> { method };
865+
availableMethodGroups.Add(methodName, methodGroup);
866+
}
867+
}
868+
869+
return availableMethodGroups;
870+
}
871+
811872
private object[] GetHostItemMemberArguments(JsValue[] args, int maxArgCount = -1)
812873
{
813874
if (args == null)

src/JavaScriptEngineSwitcher.ChakraCore/readme.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
=============
3232
RELEASE NOTES
3333
=============
34-
ChakraCore was updated to version of November 9, 2022.
34+
In configuration settings of the ChakraCore JS engine was added one new
35+
property - `AllowReflection` (default `false`).
3536

3637
=============
3738
DOCUMENTATION

0 commit comments

Comments
 (0)