Skip to content

Commit 60ab70d

Browse files
ScriptEngine.AllowReflection, HostTypeCollection filtering, minor updates.
1 parent 93fc343 commit 60ab70d

35 files changed

+488
-103
lines changed

ClearScript/HostFunctions.cs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@
6060
//
6161

6262
using System;
63+
using System.Diagnostics.CodeAnalysis;
64+
using System.Globalization;
65+
using System.Linq;
6366
using Microsoft.ClearScript.Util;
6467

6568
namespace Microsoft.ClearScript
@@ -224,7 +227,7 @@ public Array newArr<T>(params int[] lengths)
224227
/// var dict = host.newObj(StringDictT);
225228
/// dict.Add("foo", "bar");
226229
/// dict.Add("baz", "qux");
227-
/// // look up a dictionary entry */
230+
/// // look up a dictionary entry
228231
/// var result = host.newVar(StringT);
229232
/// var found = dict.TryGetValue("baz", result.out);
230233
/// </code>
@@ -360,6 +363,10 @@ public object func<T>(int argCount, object scriptFunc)
360363
/// <c><see href="http://msdn.microsoft.com/en-us/library/58918ffs(VS.71).aspx">typeof</see></c>
361364
/// operator. It is overloaded with <see cref="typeOf(object)"/> and selected at runtime if
362365
/// <typeparamref name="T"/> can be used as a type argument.
366+
/// <para>
367+
/// This function throws an exception if the script engine's
368+
/// <see cref="ScriptEngine.AllowReflection"/> property is set to <c>false</c>.
369+
/// </para>
363370
/// </remarks>
364371
/// <example>
365372
/// The following code retrieves the assembly-qualified name of a host type.
@@ -374,6 +381,7 @@ public object func<T>(int argCount, object scriptFunc)
374381
/// <seealso cref="ExtendedHostFunctions.type(string, object[])"/>
375382
public Type typeOf<T>()
376383
{
384+
GetEngine().CheckReflection();
377385
return typeof(T);
378386
}
379387

@@ -389,6 +397,10 @@ public Type typeOf<T>()
389397
/// operator. It is overloaded with <see cref="typeOf{T}"/> and selected at runtime if
390398
/// <paramref name="value"/> cannot be used as a type argument. Note that this applies to
391399
/// some host types; examples are static types and overloaded generic type groups.
400+
/// <para>
401+
/// This function throws an exception if the script engine's
402+
/// <see cref="ScriptEngine.AllowReflection"/> property is set to <c>false</c>.
403+
/// </para>
392404
/// </remarks>
393405
/// <example>
394406
/// The following code retrieves the assembly-qualified name of a host type.
@@ -403,6 +415,8 @@ public Type typeOf<T>()
403415
/// <seealso cref="ExtendedHostFunctions.type(string, object[])"/>
404416
public Type typeOf(object value)
405417
{
418+
GetEngine().CheckReflection();
419+
406420
var hostType = value as HostType;
407421
if (hostType == null)
408422
{
@@ -544,6 +558,51 @@ public bool isTypeObj<T>()
544558

545559
// ReSharper restore UnusedTypeParameter
546560

561+
/// <summary>
562+
/// Creates a strongly typed flag set.
563+
/// </summary>
564+
/// <typeparam name="T">The type of flag set to create.</typeparam>
565+
/// <param name="args">The flags to include in the flag set.</param>
566+
/// <returns>A strongly typed flag set containing the specified flags.</returns>
567+
/// <remarks>
568+
/// This function throws an exception if <typeparamref name="T"/> is not a flag set type.
569+
/// </remarks>
570+
/// <example>
571+
/// The following code demonstrates using a strongly typed flag set.
572+
/// It assumes that an instance of <see cref="ExtendedHostFunctions"/> is exposed under
573+
/// the name "host"
574+
/// (see <see cref="ScriptEngine.AddHostObject(string, object)">AddHostObject</see>).
575+
/// <code lang="JavaScript">
576+
/// // import URI types
577+
/// var UriT = host.type("System.Uri", "System");
578+
/// var UriFormatT = host.type("System.UriFormat", "System");
579+
/// var UriComponentsT = host.type("System.UriComponents", "System");
580+
/// // create a URI
581+
/// var uri = host.newObj(UriT, "http://www.example.com:8080/path/to/file/sample.htm?x=1&amp;y=2");
582+
/// // extract URI components
583+
/// var components = host.flags(UriComponentsT.Scheme, UriComponentsT.Host, UriComponentsT.Path);
584+
/// var result = uri.GetComponents(components, UriFormatT.Unescaped);
585+
/// </code>
586+
/// </example>
587+
/// <seealso cref="ExtendedHostFunctions.type(string, string, object[])"/>
588+
public T flags<T>(params T[] args)
589+
{
590+
var type = typeof(T);
591+
if (!type.IsFlagsEnum())
592+
{
593+
throw new InvalidOperationException(MiscHelpers.FormatInvariant("{0} is not a flag set type", type.GetFullFriendlyName()));
594+
}
595+
596+
try
597+
{
598+
return args.Aggregate(0UL, (flags, arg) => flags | ((IConvertible)arg).ToUInt64(CultureInfo.InvariantCulture)).DynamicCast<T>();
599+
}
600+
catch (OverflowException)
601+
{
602+
return args.Aggregate(0L, (flags, arg) => flags | ((IConvertible)arg).ToInt64(CultureInfo.InvariantCulture)).DynamicCast<T>();
603+
}
604+
}
605+
547606
// ReSharper restore InconsistentNaming
548607

549608
#endregion
@@ -562,6 +621,7 @@ internal ScriptEngine GetEngine()
562621

563622
// ReSharper disable ParameterHidesMember
564623

624+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member is not expected to be re-implemented in derived classes.")]
565625
void IScriptableObject.OnExposedToScriptCode(ScriptEngine engine)
566626
{
567627
MiscHelpers.VerifyNonNullArgument(engine, "engine");

ClearScript/HostItem.Invoke.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ private object InvokeMethod(string name, Type[] typeArgs, object[] args)
100100
var extensionBindResult = extensionHostItem.BindMethod(name, typeArgs, extensionArgs);
101101
if (extensionBindResult is MethodBindSuccess)
102102
{
103-
return extensionBindResult.Invoke();
103+
return extensionBindResult.Invoke(engine);
104104
}
105105
}
106106
}
107107

108-
return bindResult.Invoke();
108+
return bindResult.Invoke(engine);
109109
}
110110

111111
private static IEnumerable<Type> GetTypeArgs(object[] args)
@@ -265,7 +265,7 @@ public static MethodBindResult Create(string name, object rawResult, HostTarget
265265
return new MethodBindFailure((rawResult as Exception) ?? new NotSupportedException(MiscHelpers.FormatInvariant("Invocation of method '{0}' failed (unrecognized binding)", name)));
266266
}
267267

268-
public abstract object Invoke();
268+
public abstract object Invoke(ScriptEngine engine);
269269
}
270270

271271
#endregion
@@ -274,6 +274,8 @@ public static MethodBindResult Create(string name, object rawResult, HostTarget
274274

275275
private class MethodBindSuccess : MethodBindResult
276276
{
277+
private static readonly MethodInfo getTypeMethod = typeof(object).GetMethod("GetType");
278+
277279
private readonly HostTarget hostTarget;
278280
private readonly MethodInfo method;
279281
private readonly object[] args;
@@ -287,8 +289,13 @@ public MethodBindSuccess(HostTarget hostTarget, MethodInfo method, object[] args
287289

288290
#region MethodBindResult overrides
289291

290-
public override object Invoke()
292+
public override object Invoke(ScriptEngine engine)
291293
{
294+
if (method == getTypeMethod)
295+
{
296+
engine.CheckReflection();
297+
}
298+
292299
return InvokeHelpers.InvokeMethod(hostTarget.InvokeTarget, method, args);
293300
}
294301

@@ -310,7 +317,7 @@ public MethodBindFailure(Exception exception)
310317

311318
#region MethodBindResult overrides
312319

313-
public override object Invoke()
320+
public override object Invoke(ScriptEngine engine)
314321
{
315322
throw exception;
316323
}

ClearScript/HostTypeCollection.cs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ namespace Microsoft.ClearScript
8282
/// </remarks>
8383
public class HostTypeCollection : PropertyBag
8484
{
85+
private static readonly Predicate<Type> defaultFilter = type => true;
86+
8587
/// <summary>
8688
/// Initializes a new host type collection.
8789
/// </summary>
@@ -102,8 +104,8 @@ public HostTypeCollection(params Assembly[] assemblies)
102104
}
103105

104106
/// <summary>
105-
/// Initializes a new host type collection with types from one or more assemblies. The assemblies
106-
/// are specified by name.
107+
/// Initializes a new host type collection with types from one or more assemblies. The
108+
/// assemblies are specified by name.
107109
/// </summary>
108110
/// <param name="assemblyNames">The names of the assemblies that contain the types with which to initialize the collection.</param>
109111
public HostTypeCollection(params string[] assemblyNames)
@@ -113,6 +115,29 @@ public HostTypeCollection(params string[] assemblyNames)
113115
Array.ForEach(assemblyNames, AddAssembly);
114116
}
115117

118+
/// <summary>
119+
/// Initializes a new host type collection with selected types from one or more assemblies.
120+
/// </summary>
121+
/// <param name="filter">A filter for selecting the types to add.</param>
122+
/// <param name="assemblies">The assemblies that contain the types with which to initialize the collection.</param>
123+
public HostTypeCollection(Predicate<Type> filter, params Assembly[] assemblies)
124+
{
125+
MiscHelpers.VerifyNonNullArgument(assemblies, "assemblies");
126+
Array.ForEach(assemblies, assembly => AddAssembly(assembly, filter));
127+
}
128+
129+
/// <summary>
130+
/// Initializes a new host type collection with selected types from one or more assemblies.
131+
/// The assemblies are specified by name.
132+
/// </summary>
133+
/// <param name="filter">A filter for selecting the types to add.</param>
134+
/// <param name="assemblyNames">The names of the assemblies that contain the types with which to initialize the collection.</param>
135+
public HostTypeCollection(Predicate<Type> filter, params string[] assemblyNames)
136+
{
137+
MiscHelpers.VerifyNonNullArgument(assemblyNames, "assemblyNames");
138+
Array.ForEach(assemblyNames, assemblyName => AddAssembly(assemblyName, filter));
139+
}
140+
116141
/// <summary>
117142
/// Adds types from an assembly to a host type collection.
118143
/// </summary>
@@ -132,12 +157,36 @@ public void AddAssembly(Assembly assembly)
132157
/// <param name="assemblyName">The name of the assembly that contains the types to add.</param>
133158
public void AddAssembly(string assemblyName)
134159
{
135-
if (string.IsNullOrWhiteSpace(assemblyName))
160+
MiscHelpers.VerifyNonBlankArgument(assemblyName, "assemblyName", "Invalid assembly name");
161+
AddAssembly(Assembly.Load(AssemblyHelpers.GetFullAssemblyName(assemblyName)));
162+
}
163+
164+
/// <summary>
165+
/// Adds selected types from an assembly to a host type collection.
166+
/// </summary>
167+
/// <param name="assembly">The assembly that contains the types to add.</param>
168+
/// <param name="filter">A filter for selecting the types to add.</param>
169+
public void AddAssembly(Assembly assembly, Predicate<Type> filter)
170+
{
171+
MiscHelpers.VerifyNonNullArgument(assembly, "assembly");
172+
173+
var activeFilter = filter ?? defaultFilter;
174+
foreach (var type in assembly.GetTypes().Where(type => type.IsImportable() && activeFilter(type)))
136175
{
137-
throw new ArgumentException("Invalid assembly name", "assemblyName");
176+
AddType(type);
138177
}
178+
}
139179

140-
AddAssembly(Assembly.Load(AssemblyHelpers.GetFullAssemblyName(assemblyName)));
180+
/// <summary>
181+
/// Adds selected types from an assembly to a host type collection. The assembly is
182+
/// specified by name.
183+
/// </summary>
184+
/// <param name="assemblyName">The name of the assembly that contains the types to add.</param>
185+
/// <param name="filter">A filter for selecting the types to add.</param>
186+
public void AddAssembly(string assemblyName, Predicate<Type> filter)
187+
{
188+
MiscHelpers.VerifyNonBlankArgument(assemblyName, "assemblyName", "Invalid assembly name");
189+
AddAssembly(Assembly.Load(AssemblyHelpers.GetFullAssemblyName(assemblyName)), filter);
141190
}
142191

143192
/// <summary>

ClearScript/PropertyBag.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
using System.Collections;
6464
using System.Collections.Generic;
6565
using System.ComponentModel;
66+
using System.Diagnostics.CodeAnalysis;
6667
using System.Linq;
6768
using Microsoft.ClearScript.Util;
6869

@@ -169,11 +170,13 @@ private void InvokePropertyChanged(string name)
169170

170171
#region IEnumerable<KeyValuePair<string, object>> implementation
171172

173+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member requires explicit implementation to resolve ambiguity.")]
172174
IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
173175
{
174176
return dictionary.GetEnumerator();
175177
}
176178

179+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member requires explicit implementation to resolve ambiguity.")]
177180
IEnumerator IEnumerable.GetEnumerator()
178181
{
179182
return dictionary.GetEnumerator();
@@ -183,30 +186,35 @@ IEnumerator IEnumerable.GetEnumerator()
183186

184187
#region ICollection<KeyValuePair<string, object>> implementation
185188

189+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member is not expected to be re-implemented in derived classes.")]
186190
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
187191
{
188192
CheckReadOnly();
189193
SetPropertyNoCheck(item.Key, item.Value);
190194
InvokePropertyChanged(item.Key);
191195
}
192196

197+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member is not expected to be re-implemented in derived classes.")]
193198
void ICollection<KeyValuePair<string, object>>.Clear()
194199
{
195200
CheckReadOnly();
196201
collection.Clear();
197202
InvokePropertyChanged(null);
198203
}
199204

205+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member is not expected to be re-implemented in derived classes.")]
200206
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
201207
{
202208
return collection.Contains(item);
203209
}
204210

211+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member is not expected to be re-implemented in derived classes.")]
205212
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
206213
{
207214
collection.CopyTo(array, arrayIndex);
208215
}
209216

217+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member is not expected to be re-implemented in derived classes.")]
210218
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
211219
{
212220
CheckReadOnly();
@@ -219,11 +227,13 @@ bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, objec
219227
return false;
220228
}
221229

230+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member is not expected to be re-implemented in derived classes.")]
222231
int ICollection<KeyValuePair<string, object>>.Count
223232
{
224233
get { return collection.Count; }
225234
}
226235

236+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member is not expected to be re-implemented in derived classes.")]
227237
bool ICollection<KeyValuePair<string, object>>.IsReadOnly
228238
{
229239
get { return isReadOnly; }
@@ -329,6 +339,7 @@ public ICollection<object> Values
329339

330340
#region IScriptableObject implementation
331341

342+
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "This member is not expected to be re-implemented in derived classes.")]
332343
void IScriptableObject.OnExposedToScriptCode(ScriptEngine engine)
333344
{
334345
if ((engine != null) && engineSet.TryAdd(engine))

ClearScript/ScriptEngine.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,18 @@ protected ScriptEngine()
107107
/// </remarks>
108108
public Type AccessContext { get; set; }
109109

110+
/// <summary>
111+
/// Gets or sets a value that controls whether script code is permitted to use reflection.
112+
/// </summary>
113+
/// <remarks>
114+
/// When this property is set to <c>true</c>, script code running in the current script
115+
/// engine is permitted to use reflection. This affects
116+
/// <see cref="System.Object.GetType"/>, <see cref="HostFunctions.typeOf(object)"/> and
117+
/// <see cref="HostFunctions.typeOf{T}"/>. By default, any attempt to invoke these methods
118+
/// from script code results in an exception.
119+
/// </remarks>
120+
public bool AllowReflection { get; set; }
121+
110122
/// <summary>
111123
/// Gets or sets a callback that can be used to halt script execution.
112124
/// </summary>
@@ -589,6 +601,14 @@ internal void RequestInterrupt()
589601
}
590602
}
591603

604+
internal void CheckReflection()
605+
{
606+
if (!AllowReflection)
607+
{
608+
throw new UnauthorizedAccessException("Use of reflection is prohibited in this script engine");
609+
}
610+
}
611+
592612
#endregion
593613

594614
#region host-side invocation

ClearScript/Util/MiscHelpers.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ public static void VerifyNonNullArgument(object value, string name)
8181
}
8282
}
8383

84+
public static void VerifyNonBlankArgument(string value, string name, string message)
85+
{
86+
if (string.IsNullOrWhiteSpace(value))
87+
{
88+
throw new ArgumentException(message, name);
89+
}
90+
}
91+
8492
public static string EnsureNonBlank(string input, string alternate)
8593
{
8694
Debug.Assert(!string.IsNullOrWhiteSpace(alternate));

0 commit comments

Comments
 (0)