Skip to content

Commit bb0c181

Browse files
ClearScript 5.1: ScriptEngine.AddRestrictedHostObject(), ScriptMemberAttribute, NoScriptAccessAttribute, improved method bind plumbing and V8 GlobalMembers support, lots of new tests.
1 parent a0f5728 commit bb0c181

64 files changed

Lines changed: 2188 additions & 268 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ClearScript/ByRefArg.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,14 @@ public override string[] GetAuxPropertyNames(BindingFlags bindFlags)
132132
return target.GetAuxPropertyNames(bindFlags);
133133
}
134134

135-
public override bool TryInvokeAuxMember(string name, BindingFlags invokeFlags, object[] args, out object result)
135+
public override bool TryInvokeAuxMember(string name, BindingFlags invokeFlags, object[] args, object[] bindArgs, out object result)
136136
{
137-
return target.TryInvokeAuxMember(name, invokeFlags, args, out result);
137+
return target.TryInvokeAuxMember(name, invokeFlags, args, bindArgs, out result);
138138
}
139139

140-
public override bool TryInvoke(BindingFlags invokeFlags, object[] args, out object result)
140+
public override bool TryInvoke(BindingFlags invokeFlags, object[] args, object[] bindArgs, out object result)
141141
{
142-
return target.TryInvoke(invokeFlags, args, out result);
142+
return target.TryInvoke(invokeFlags, args, bindArgs, out result);
143143
}
144144

145145
#endregion

ClearScript/ClearScript.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,18 @@
6060
<Compile Include="HostTargetFlags.cs" />
6161
<Compile Include="IScriptableObject.cs" />
6262
<Compile Include="BindSignature.cs" />
63+
<Compile Include="NoScriptAccessAttribute.cs" />
6364
<Compile Include="Properties\AssemblyInfo.cs">
6465
<AutoGen>True</AutoGen>
6566
<DesignTime>True</DesignTime>
6667
<DependentUpon>AssemblyInfo.tt</DependentUpon>
6768
</Compile>
69+
<Compile Include="ScriptAccess.cs" />
6870
<Compile Include="ScriptMethod.cs" />
71+
<Compile Include="ScriptMemberAttribute.cs" />
72+
<Compile Include="ScriptUsageAttribute.cs" />
6973
<Compile Include="Util\IScriptMarshalWrapper.cs" />
74+
<Compile Include="Util\MemberHelpers.cs" />
7075
<Compile Include="Windows\WindowsScriptEngineFlags.cs" />
7176
<Compile Include="Util\IDynamic.cs" />
7277
<Compile Include="Util\SpecialMemberNames.cs" />

ClearScript/ContinuationCallback.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
namespace Microsoft.ClearScript
6363
{
6464
/// <summary>
65-
/// Represents the method that indicates to a script engine whether script execution should continue.
65+
/// Represents the method that specifies to a script engine whether script execution should continue.
6666
/// </summary>
6767
/// <returns><c>True</c> to continue script execution, <c>false</c> to interrupt it.</returns>
6868
/// <seealso cref="ScriptEngine.ContinuationCallback"/>

ClearScript/ExtensionMethods.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,17 @@ public bool ProcessType(Type type)
8585
if (!table.ContainsKey(type) && type.HasExtensionMethods())
8686
{
8787
const BindingFlags bindFlags = BindingFlags.Public | BindingFlags.Static;
88-
table[type] = type.GetMethods(bindFlags).Where(IsExtensionMethod).ToArray();
88+
table[type] = type.GetMethods(bindFlags).Where(IsScriptableExtensionMethod).ToArray();
8989
summary = new ExtensionMethodSummary(table);
9090
return true;
9191
}
9292

9393
return false;
9494
}
9595

96-
private static bool IsExtensionMethod(MethodInfo method)
96+
private static bool IsScriptableExtensionMethod(MethodInfo method)
9797
{
98-
return !method.IsSpecialName && method.IsDefined(typeof(ExtensionAttribute), false);
98+
return method.IsScriptable() && method.IsDefined(typeof(ExtensionAttribute), false);
9999
}
100100
}
101101

@@ -112,7 +112,7 @@ public ExtensionMethodSummary(Dictionary<Type, MethodInfo[]> table)
112112
{
113113
Types = table.Keys.ToArray();
114114
Methods = table.SelectMany(pair => pair.Value).ToArray();
115-
MethodNames = Methods.Select(method => method.Name).ToArray();
115+
MethodNames = Methods.Select(method => method.GetScriptName()).ToArray();
116116
}
117117

118118
public Type[] Types { get; private set; }

ClearScript/HostEvent.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ internal EventSource(ScriptEngine engine, object source, EventInfo eventInfo)
8989
this.eventInfo = eventInfo;
9090
}
9191

92+
internal object Source
93+
{
94+
get { return source; }
95+
}
96+
97+
internal EventInfo EventInfo
98+
{
99+
get { return eventInfo; }
100+
}
101+
92102
#region script-callable interface
93103

94104
// ReSharper disable InconsistentNaming

ClearScript/HostFunctions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public class HostFunctions : IScriptableObject
8686
/// </summary>
8787
public HostFunctions()
8888
{
89+
// the help file builder (SHFB) insists on an empty constructor here
8990
}
9091

9192
// ReSharper restore EmptyConstructor
@@ -656,6 +657,7 @@ public class ExtendedHostFunctions : HostFunctions
656657
/// </summary>
657658
public ExtendedHostFunctions()
658659
{
660+
// the help file builder (SHFB) insists on an empty constructor here
659661
}
660662

661663
// ReSharper restore EmptyConstructor

ClearScript/HostIndexer.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,19 @@ public override string[] GetAuxMethodNames(BindingFlags bindFlags)
119119
return auxMethodNames;
120120
}
121121

122-
public override bool TryInvokeAuxMember(string memberName, BindingFlags invokeFlags, object[] args, out object result)
122+
public override bool TryInvokeAuxMember(string memberName, BindingFlags invokeFlags, object[] args, object[] bindArgs, out object result)
123123
{
124124
if (invokeFlags.HasFlag(BindingFlags.InvokeMethod))
125125
{
126126
if (memberName == "get")
127127
{
128-
result = target.InvokeMember(name, BindingFlags.GetProperty, args, null);
128+
result = target.InvokeMember(name, BindingFlags.GetProperty, args, bindArgs, null);
129129
return true;
130130
}
131131

132132
if (memberName == "set")
133133
{
134-
result = target.InvokeMember(name, BindingFlags.SetProperty, args, null);
134+
result = target.InvokeMember(name, BindingFlags.SetProperty, args, bindArgs, null);
135135
return true;
136136
}
137137
}
@@ -140,9 +140,9 @@ public override bool TryInvokeAuxMember(string memberName, BindingFlags invokeFl
140140
return false;
141141
}
142142

143-
public override bool TryInvoke(BindingFlags invokeFlags, object[] args, out object result)
143+
public override bool TryInvoke(BindingFlags invokeFlags, object[] args, object[] bindArgs, out object result)
144144
{
145-
result = target.InvokeMember(name, invokeFlags.HasFlag(BindingFlags.SetField) ? BindingFlags.SetProperty : BindingFlags.GetProperty, args, null);
145+
result = target.InvokeMember(name, invokeFlags.HasFlag(BindingFlags.SetField) ? BindingFlags.SetProperty : BindingFlags.GetProperty, args, bindArgs, null);
146146
return true;
147147
}
148148

ClearScript/HostItem.Invoke.cs

Lines changed: 92 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -76,28 +76,33 @@ internal partial class HostItem
7676
{
7777
#region internal members
7878

79-
private object InvokeMethod(string name, object[] args)
79+
private object InvokeMethod(string name, object[] args, object[] bindArgs)
8080
{
8181
var typeArgs = GetTypeArgs(args).ToArray();
8282
if (typeArgs.Length > 0)
8383
{
8484
args = args.Skip(typeArgs.Length).ToArray();
85+
bindArgs = bindArgs.Skip(typeArgs.Length).ToArray();
8586
}
8687

87-
return InvokeMethod(name, typeArgs, args);
88+
return InvokeMethod(name, typeArgs, args, bindArgs);
8889
}
8990

90-
private object InvokeMethod(string name, Type[] typeArgs, object[] args)
91+
private object InvokeMethod(string name, Type[] typeArgs, object[] args, object[] bindArgs)
9192
{
92-
var bindResult = BindMethod(name, typeArgs, args);
93+
var bindResult = BindMethod(name, typeArgs, args, bindArgs);
9394
if ((bindResult is MethodBindFailure) && target.Flags.HasFlag(HostTargetFlags.AllowExtensionMethods))
9495
{
95-
var targetArg = new[] { target.DynamicInvokeTarget };
96+
var targetArg = new[] { target.Target };
9697
var extensionArgs = targetArg.Concat(args).ToArray();
98+
99+
var targetBindArg = new object[] { target };
100+
var extensionBindArgs = targetBindArg.Concat(bindArgs).ToArray();
101+
97102
foreach (var type in cachedExtensionMethodSummary.Types)
98103
{
99104
var extensionHostItem = (HostItem)Wrap(engine, HostType.Wrap(type));
100-
var extensionBindResult = extensionHostItem.BindMethod(name, typeArgs, extensionArgs);
105+
var extensionBindResult = extensionHostItem.BindMethod(name, typeArgs, extensionArgs, extensionBindArgs);
101106
if (extensionBindResult is MethodBindSuccess)
102107
{
103108
return extensionBindResult.Invoke(engine);
@@ -128,67 +133,73 @@ private static IEnumerable<Type> GetTypeArgs(object[] args)
128133
}
129134
}
130135

131-
private MethodBindResult BindMethod(string name, Type[] typeArgs, object[] args)
136+
private MethodBindResult BindMethod(string name, Type[] typeArgs, object[] args, object[] bindArgs)
132137
{
133-
MethodBindResult result = null;
134-
135138
// WARNING: BindSignature holds on to the specified typeArgs; subsequent modification
136139
// will result in bugs that are difficult to diagnose. Create a copy if necessary.
137140

141+
var signature = new BindSignature(target, name, typeArgs, bindArgs);
142+
MethodBindResult result;
143+
138144
object rawResult;
139-
var signature = new BindSignature(target, name, typeArgs, args);
140145
if (engine.TryGetCachedBindResult(signature, out rawResult))
141146
{
142147
result = MethodBindResult.Create(name, rawResult, target, args);
143148
}
144149
else
145150
{
146-
var entryList = new List<MethodBindEntry>();
147-
if ((target is HostType) || (target.InvokeTarget == null) || !target.Type.IsInterface)
151+
result = BindMethodInternal(name, typeArgs, args, bindArgs);
152+
if (!result.IsPreferredMethod(name))
148153
{
149-
entryList.Add(new MethodBindEntry(name, accessContext ?? engine.AccessContext));
150-
}
151-
else
152-
{
153-
foreach (var mapping in target.InvokeTarget.GetType().ExtGetInterfaceMaps(target.Type))
154-
{
155-
for (var index = 0; index < mapping.InterfaceMethods.Length; index++)
156-
{
157-
if (mapping.InterfaceMethods[index].Name == name)
158-
{
159-
var targetMethod = mapping.TargetMethods[index];
160-
entryList.Add(new MethodBindEntry(targetMethod.Name, targetMethod.DeclaringType));
161-
}
162-
}
163-
}
164-
165-
if (entryList.Count < 1)
154+
if (result is MethodBindSuccess)
166155
{
167-
entryList.Add(new MethodBindEntry(name, accessContext ?? engine.AccessContext));
156+
result = new MethodBindFailure(new MissingMemberException(MiscHelpers.FormatInvariant("Object has no method named '{0}' that matches the specified arguments", name)));
168157
}
169-
}
170-
171-
foreach (var entry in entryList)
172-
{
173-
const CSharpBinderFlags binderFlags = CSharpBinderFlags.InvokeSimpleName | CSharpBinderFlags.ResultDiscarded;
174-
var binder = Binder.InvokeMember(binderFlags, entry.Name, typeArgs, entry.AccessContext, CreateArgInfoEnum(args));
175158

176-
var binding = DynamicHelpers.Bind((DynamicMetaObjectBinder)binder, target.DynamicInvokeTarget, args);
177-
rawResult = (new MethodBindingVisitor(target.InvokeTarget, entry.Name, binding.Expression)).Result;
178-
result = MethodBindResult.Create(entry.Name, rawResult, target, args);
179-
if (result is MethodBindSuccess)
159+
foreach (var altName in GetAltMethodNames(name))
180160
{
181-
break;
161+
var altResult = BindMethodInternal(altName, typeArgs, args, bindArgs);
162+
if (altResult.IsUnblockedMethod())
163+
{
164+
result = altResult;
165+
break;
166+
}
182167
}
183168
}
184169

185-
Debug.Assert(rawResult != null);
186-
engine.CacheBindResult(signature, rawResult);
170+
engine.CacheBindResult(signature, result.RawResult);
187171
}
188172

189173
return result;
190174
}
191175

176+
private MethodBindResult BindMethodInternal(string name, Type[] typeArgs, object[] args, object[] bindArgs)
177+
{
178+
const CSharpBinderFlags binderFlags = CSharpBinderFlags.InvokeSimpleName | CSharpBinderFlags.ResultDiscarded;
179+
var binder = Binder.InvokeMember(binderFlags, name, typeArgs, accessContext ?? engine.AccessContext, CreateArgInfoEnum(bindArgs));
180+
181+
var binding = DynamicHelpers.Bind((DynamicMetaObjectBinder)binder, target, bindArgs);
182+
var rawResult = (new MethodBindingVisitor(target.InvokeTarget, name, binding.Expression)).Result;
183+
return MethodBindResult.Create(name, rawResult, target, args);
184+
}
185+
186+
private IEnumerable<string> GetAltMethodNames(string name)
187+
{
188+
return GetAltMethodNamesInternal(name).Distinct();
189+
}
190+
191+
private IEnumerable<string> GetAltMethodNamesInternal(string name)
192+
{
193+
foreach (var method in target.Type.GetScriptableMethods(name, GetMethodBindFlags()))
194+
{
195+
var methodName = method.GetShortName();
196+
if (methodName != name)
197+
{
198+
yield return methodName;
199+
}
200+
}
201+
}
202+
192203
private IEnumerable<CSharpArgumentInfo> CreateArgInfoEnum(object[] args)
193204
{
194205
if (target is HostType)
@@ -208,14 +219,14 @@ private IEnumerable<CSharpArgumentInfo> CreateArgInfoEnum(object[] args)
208219

209220
private static CSharpArgumentInfo CreateArgInfo(object arg)
210221
{
211-
var flags = CSharpArgumentInfoFlags.None;
222+
var flags = CSharpArgumentInfoFlags.UseCompileTimeType;
212223
if (arg is IOutArg)
213224
{
214-
flags |= CSharpArgumentInfoFlags.IsOut | CSharpArgumentInfoFlags.UseCompileTimeType;
225+
flags |= CSharpArgumentInfoFlags.IsOut;
215226
}
216227
else if (arg is IRefArg)
217228
{
218-
flags |= CSharpArgumentInfoFlags.IsRef | CSharpArgumentInfoFlags.UseCompileTimeType;
229+
flags |= CSharpArgumentInfoFlags.IsRef;
219230
}
220231

221232
return CSharpArgumentInfo.Create(flags, null);
@@ -228,23 +239,6 @@ private static CSharpArgumentInfo CreateStaticTypeArgInfo()
228239

229240
#endregion
230241

231-
#region Nested type: MethodBindEntry
232-
233-
private struct MethodBindEntry
234-
{
235-
public readonly string Name;
236-
237-
public readonly Type AccessContext;
238-
239-
public MethodBindEntry(string name, Type accessContext)
240-
{
241-
Name = name;
242-
AccessContext = accessContext;
243-
}
244-
}
245-
246-
#endregion
247-
248242
#region Nested type: MethodBindResult
249243

250244
private abstract class MethodBindResult
@@ -265,6 +259,12 @@ public static MethodBindResult Create(string name, object rawResult, HostTarget
265259
return new MethodBindFailure((rawResult as Exception) ?? new NotSupportedException(MiscHelpers.FormatInvariant("Invocation of method '{0}' failed (unrecognized binding)", name)));
266260
}
267261

262+
public abstract object RawResult { get; }
263+
264+
public abstract bool IsPreferredMethod(string name);
265+
266+
public abstract bool IsUnblockedMethod();
267+
268268
public abstract object Invoke(ScriptEngine engine);
269269
}
270270

@@ -289,6 +289,21 @@ public MethodBindSuccess(HostTarget hostTarget, MethodInfo method, object[] args
289289

290290
#region MethodBindResult overrides
291291

292+
public override object RawResult
293+
{
294+
get { return method; }
295+
}
296+
297+
public override bool IsPreferredMethod(string name)
298+
{
299+
return !method.IsBlockedFromScript() && (method.GetScriptName() == name);
300+
}
301+
302+
public override bool IsUnblockedMethod()
303+
{
304+
return !method.IsBlockedFromScript();
305+
}
306+
292307
public override object Invoke(ScriptEngine engine)
293308
{
294309
if (method == getTypeMethod)
@@ -317,6 +332,20 @@ public MethodBindFailure(Exception exception)
317332

318333
#region MethodBindResult overrides
319334

335+
public override object RawResult
336+
{
337+
get { return exception; }
338+
}
339+
340+
public override bool IsPreferredMethod(string name)
341+
{
342+
return false;
343+
}
344+
345+
public override bool IsUnblockedMethod()
346+
{
347+
return false;
348+
}
320349
public override object Invoke(ScriptEngine engine)
321350
{
322351
throw exception;

0 commit comments

Comments
 (0)