PageRenderTime 56ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/Runtime/Samples/sympl/csharp/Runtime.cs

http://github.com/IronLanguages/main
C# | 1177 lines | 831 code | 89 blank | 257 comment | 157 complexity | e829cf35031e90fdbfc1b1ed42034e45 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Dynamic;
  4. using System.Reflection;
  5. using System.Linq.Expressions;
  6. using Microsoft.Scripting.ComInterop;
  7. using Microsoft.Scripting.Utils;
  8. using System.Linq;
  9. using System.Runtime.CompilerServices;
  10. using Path = System.IO.Path;
  11. using File = System.IO.File;
  12. using Directory = System.IO.Directory;
  13. using Debug = System.Diagnostics.Debug;
  14. namespace SymplSample {
  15. // RuntimeHelpers is a collection of functions that perform operations at
  16. // runtime of Sympl code, such as performing an import or eq.
  17. //
  18. public static class RuntimeHelpers {
  19. // SymplImport takes the runtime and module as context for the import.
  20. // It takes a list of names, what, that either identify a (possibly dotted
  21. // sequence) of names to fetch from Globals or a file name to load. Names
  22. // is a list of names to fetch from the final object that what indicates
  23. // and then set each name in module. Renames is a list of names to add to
  24. // module instead of names. If names is empty, then the name set in
  25. // module is the last name in what. If renames is not empty, it must have
  26. // the same cardinality as names.
  27. //
  28. public static object SymplImport(Sympl runtime, IDynamicMetaObjectProvider module,
  29. string[] what, string[] names,
  30. string[] renames) {
  31. // Get object or file scope.
  32. object value = null;
  33. if (what.Length == 1) {
  34. string name = what[0];
  35. if (DynamicObjectHelpers.HasMember(runtime.Globals, name)) {
  36. value = DynamicObjectHelpers.GetMember(runtime.Globals, name);
  37. // Since runtime.Globals has Sympl's reflection of namespaces and
  38. // types, we pick those up first above and don't risk hitting a
  39. // NamespaceTracker for assemblies added when we initialized Sympl.
  40. // The next check will correctly look up case-INsensitively for
  41. // globals the host adds to ScriptRuntime.Globals.
  42. } else if (DynamicObjectHelpers.HasMember(runtime.DlrGlobals, name)) {
  43. value = DynamicObjectHelpers.GetMember(runtime.DlrGlobals, name);
  44. } else {
  45. string f = (string)(DynamicObjectHelpers
  46. .GetMember(module, "__file__"));
  47. f = Path.Combine(Path.GetDirectoryName(f), name + ".sympl");
  48. if (File.Exists(f)) {
  49. value = runtime.ExecuteFile(f);
  50. } else {
  51. throw new ArgumentException(
  52. "Import: can't find name in globals " +
  53. "or as file to load -- " + name + " " + f);
  54. }
  55. }
  56. } else {
  57. // What has more than one name, must be Globals access.
  58. value = runtime.Globals;
  59. // For more correctness and generality, shouldn't assume all
  60. // globals are dynamic objects, or that a look up like foo.bar.baz
  61. // cascades through all dynamic objects.
  62. // Would need to manually create a CallSite here with Sympl's
  63. // GetMemberBinder, and think about a caching strategy per name.
  64. foreach (string name in what) {
  65. value = DynamicObjectHelpers.GetMember(
  66. (IDynamicMetaObjectProvider)value, name);
  67. }
  68. }
  69. // Assign variables in module.
  70. if (names.Length == 0) {
  71. DynamicObjectHelpers.SetMember((IDynamicMetaObjectProvider)module,
  72. what[what.Length - 1], value);
  73. } else {
  74. if (renames.Length == 0) renames = names;
  75. for (int i = 0; i < names.Length; i++) {
  76. string name = names[i];
  77. string rename = renames[i];
  78. DynamicObjectHelpers.SetMember(
  79. (IDynamicMetaObjectProvider)module, rename,
  80. DynamicObjectHelpers.GetMember(
  81. (IDynamicMetaObjectProvider)value, name));
  82. }
  83. }
  84. return null;
  85. } // SymplImport
  86. // Uses of the 'eq' keyword form in Sympl compile to a call to this
  87. // helper function.
  88. //
  89. public static bool SymplEq (object x, object y) {
  90. if (x == null)
  91. return y == null;
  92. else if (y == null)
  93. return x == null;
  94. else {
  95. var xtype = x.GetType();
  96. var ytype = y.GetType();
  97. if (xtype.IsPrimitive && xtype != typeof(string) &&
  98. ytype.IsPrimitive && ytype != typeof(string))
  99. return x.Equals(y);
  100. else
  101. return object.ReferenceEquals(x, y);
  102. }
  103. }
  104. // Uses of the 'cons' keyword form in Sympl compile to a call to this
  105. // helper function.
  106. //
  107. public static Cons MakeCons (object x, object y) {
  108. return new Cons(x, y);
  109. }
  110. // Gets the i-th element in the Cons list.
  111. //
  112. public static object GetConsElt(Cons lst, int i) {
  113. return NthCdr(lst, i).First;
  114. }
  115. // Sets the i-th element in the Cons list with the specified value.
  116. //
  117. public static object SetConsElt(Cons lst, int i, object value) {
  118. lst = NthCdr(lst, i);
  119. lst.First = value;
  120. return value;
  121. }
  122. private static Cons NthCdr (Cons lst, int i) {
  123. while (i > 0 && lst != null) {
  124. lst = lst.Rest as Cons;
  125. i--;
  126. }
  127. if (i == 0 && lst != null) {
  128. return lst;
  129. } else {
  130. throw new ArgumentOutOfRangeException("i");
  131. }
  132. }
  133. //////////////////////////////////////////////////
  134. // Array Utilities (slicing) and some LINQ helpers
  135. //////////////////////////////////////////////////
  136. public static T[] RemoveFirstElt<T>(IList<T> list) {
  137. // Make array ...
  138. if (list.Count == 0) {
  139. return new T[0];
  140. }
  141. T[] res = new T[list.Count];
  142. list.CopyTo(res, 0);
  143. // Shift result
  144. return ShiftLeft(res, 1);
  145. }
  146. public static T[] RemoveFirstElt<T>(T[] array) {
  147. return ShiftLeft(array, 1);
  148. }
  149. private static T[] ShiftLeft<T>(T[] array, int count) {
  150. //ContractUtils.RequiresNotNull(array, "array");
  151. if (count < 0) throw new ArgumentOutOfRangeException("count");
  152. T[] result = new T[array.Length - count];
  153. System.Array.Copy(array, count, result, 0, result.Length);
  154. return result;
  155. }
  156. public static T[] RemoveLast<T>(T[] array) {
  157. //ContractUtils.RequiresNotNull(array, "array");
  158. System.Array.Resize(ref array, array.Length - 1);
  159. return array;
  160. }
  161. ///////////////////////////////////////
  162. // Utilities used by binders at runtime
  163. ///////////////////////////////////////
  164. // ParamsMatchArgs returns whether the args are assignable to the parameters.
  165. // We specially check for our TypeModel that wraps .NET's RuntimeType, and
  166. // elsewhere we detect the same situation to convert the TypeModel for calls.
  167. //
  168. // Consider checking p.IsByRef and returning false since that's not CLS.
  169. //
  170. // Could check for a.HasValue and a.Value is None and
  171. // ((paramtype is class or interface) or (paramtype is generic and
  172. // nullable<t>)) to support passing nil anywhere.
  173. //
  174. public static bool ParametersMatchArguments(ParameterInfo[] parameters,
  175. DynamicMetaObject[] args) {
  176. // We only call this after filtering members by this constraint.
  177. Debug.Assert(args.Length == parameters.Length,
  178. "Internal: args are not same len as params?!");
  179. for (int i = 0; i < args.Length; i++) {
  180. var paramType = parameters[i].ParameterType;
  181. // We consider arg of TypeModel and param of Type to be compatible.
  182. if (paramType == typeof(Type) &&
  183. (args[i].LimitType == typeof(TypeModel))) {
  184. continue;
  185. }
  186. if (!paramType
  187. // Could check for HasValue and Value==null AND
  188. // (paramtype is class or interface) or (is generic
  189. // and nullable<T>) ... to bind nullables and null.
  190. .IsAssignableFrom(args[i].LimitType)) {
  191. return false;
  192. }
  193. }
  194. return true;
  195. }
  196. // Returns a DynamicMetaObject with an expression that fishes the .NET
  197. // RuntimeType object from the TypeModel MO.
  198. //
  199. public static DynamicMetaObject GetRuntimeTypeMoFromModel(
  200. DynamicMetaObject typeModelMO) {
  201. Debug.Assert((typeModelMO.LimitType == typeof(TypeModel)),
  202. "Internal: MO is not a TypeModel?!");
  203. // Get tm.ReflType
  204. var pi = typeof(TypeModel).GetProperty("ReflType");
  205. Debug.Assert(pi != null);
  206. return new DynamicMetaObject(
  207. Expression.Property(
  208. Expression.Convert(typeModelMO.Expression, typeof(TypeModel)),
  209. pi),
  210. typeModelMO.Restrictions.Merge(
  211. BindingRestrictions.GetTypeRestriction(
  212. typeModelMO.Expression, typeof(TypeModel)))//,
  213. // Must supply a value to prevent binder FallbackXXX methods
  214. // from infinitely looping if they do not check this MO for
  215. // HasValue == false and call Defer. After Sympl added Defer
  216. // checks, we could verify, say, FallbackInvokeMember by no
  217. // longer passing a value here.
  218. //((TypeModel)typeModelMO.Value).ReflType
  219. );
  220. }
  221. // Returns list of Convert exprs converting args to param types. If an arg
  222. // is a TypeModel, then we treat it special to perform the binding. We need
  223. // to map from our runtime model to .NET's RuntimeType object to match.
  224. //
  225. // To call this function, args and pinfos must be the same length, and param
  226. // types must be assignable from args.
  227. //
  228. // NOTE, if using this function, then need to use GetTargetArgsRestrictions
  229. // and make sure you're performing the same conversions as restrictions.
  230. //
  231. public static Expression[] ConvertArguments(
  232. DynamicMetaObject[] args, ParameterInfo[] ps) {
  233. Debug.Assert(args.Length == ps.Length,
  234. "Internal: args are not same len as params?!");
  235. Expression[] callArgs = new Expression[args.Length];
  236. for (int i = 0; i < args.Length; i++) {
  237. Expression argExpr = args[i].Expression;
  238. if (args[i].LimitType == typeof(TypeModel) &&
  239. ps[i].ParameterType == typeof(Type)) {
  240. // Get arg.ReflType
  241. argExpr = GetRuntimeTypeMoFromModel(args[i]).Expression;
  242. }
  243. argExpr = Expression.Convert(argExpr, ps[i].ParameterType);
  244. callArgs[i] = argExpr;
  245. }
  246. return callArgs;
  247. }
  248. // GetTargetArgsRestrictions generates the restrictions needed for the
  249. // MO resulting from binding an operation. This combines all existing
  250. // restrictions and adds some for arg conversions. targetInst indicates
  251. // whether to restrict the target to an instance (for operations on type
  252. // objects) or to a type (for operations on an instance of that type).
  253. //
  254. // NOTE, this function should only be used when the caller is converting
  255. // arguments to the same types as these restrictions.
  256. //
  257. public static BindingRestrictions GetTargetArgsRestrictions(
  258. DynamicMetaObject target, DynamicMetaObject[] args,
  259. bool instanceRestrictionOnTarget){
  260. // Important to add existing restriction first because the
  261. // DynamicMetaObjects (and possibly values) we're looking at depend
  262. // on the pre-existing restrictions holding true.
  263. var restrictions = target.Restrictions.Merge(BindingRestrictions
  264. .Combine(args));
  265. if (instanceRestrictionOnTarget) {
  266. restrictions = restrictions.Merge(
  267. BindingRestrictions.GetInstanceRestriction(
  268. target.Expression,
  269. target.Value
  270. ));
  271. } else {
  272. restrictions = restrictions.Merge(
  273. BindingRestrictions.GetTypeRestriction(
  274. target.Expression,
  275. target.LimitType
  276. ));
  277. }
  278. for (int i = 0; i < args.Length; i++) {
  279. BindingRestrictions r;
  280. if (args[i].HasValue && args[i].Value == null) {
  281. r = BindingRestrictions.GetInstanceRestriction(
  282. args[i].Expression, null);
  283. } else {
  284. r = BindingRestrictions.GetTypeRestriction(
  285. args[i].Expression, args[i].LimitType);
  286. }
  287. restrictions = restrictions.Merge(r);
  288. }
  289. return restrictions;
  290. }
  291. // Return the expression for getting target[indexes]
  292. //
  293. // Note, callers must ensure consistent restrictions are added for
  294. // the conversions on args and target.
  295. //
  296. public static Expression GetIndexingExpression(
  297. DynamicMetaObject target,
  298. DynamicMetaObject[] indexes) {
  299. Debug.Assert(target.HasValue && target.LimitType != typeof(Array));
  300. var indexExpressions = indexes.Select(
  301. i => Expression.Convert(i.Expression, i.LimitType))
  302. .ToArray();
  303. // CONS
  304. if (target.LimitType == typeof(Cons)) {
  305. // Call RuntimeHelper.GetConsElt
  306. var args = new List<Expression>();
  307. // The first argument is the list
  308. args.Add(
  309. Expression.Convert(
  310. target.Expression,
  311. target.LimitType)
  312. );
  313. args.AddRange(indexExpressions);
  314. return Expression.Call(
  315. typeof(RuntimeHelpers),
  316. "GetConsElt",
  317. null,
  318. args.ToArray());
  319. // ARRAY
  320. } else if (target.LimitType.IsArray) {
  321. return Expression.ArrayAccess(
  322. Expression.Convert(target.Expression,
  323. target.LimitType),
  324. indexExpressions
  325. );
  326. // INDEXER
  327. } else {
  328. var props = target.LimitType.GetProperties();
  329. var indexers = props.
  330. Where(p => p.GetIndexParameters().Length > 0).ToArray();
  331. indexers = indexers.
  332. Where(idx => idx.GetIndexParameters().Length ==
  333. indexes.Length).ToArray();
  334. var res = new List<PropertyInfo>();
  335. foreach (var idxer in indexers) {
  336. if (RuntimeHelpers.ParametersMatchArguments(
  337. idxer.GetIndexParameters(), indexes)) {
  338. // all parameter types match
  339. res.Add(idxer);
  340. }
  341. }
  342. if (res.Count == 0) {
  343. return Expression.Throw(
  344. Expression.New(
  345. typeof(MissingMemberException)
  346. .GetConstructor(new Type[] { typeof(string) }),
  347. Expression.Constant(
  348. "Can't bind because there is no matching indexer.")
  349. )
  350. );
  351. }
  352. return Expression.MakeIndex(
  353. Expression.Convert(target.Expression, target.LimitType),
  354. res[0], indexExpressions);
  355. }
  356. }
  357. // CreateThrow is a convenience function for when binders cannot bind.
  358. // They need to return a DynamicMetaObject with appropriate restrictions
  359. // that throws. Binders never just throw due to the protocol since
  360. // a binder or MO down the line may provide an implementation.
  361. //
  362. // It returns a DynamicMetaObject whose expr throws the exception, and
  363. // ensures the expr's type is object to satisfy the CallSite return type
  364. // constraint.
  365. //
  366. // A couple of calls to CreateThrow already have the args and target
  367. // restrictions merged in, but BindingRestrictions.Merge doesn't add
  368. // duplicates.
  369. //
  370. public static DynamicMetaObject CreateThrow
  371. (DynamicMetaObject target, DynamicMetaObject[] args,
  372. BindingRestrictions moreTests,
  373. Type exception, params object[] exceptionArgs) {
  374. Expression[] argExprs = null;
  375. Type[] argTypes = Type.EmptyTypes;
  376. int i;
  377. if (exceptionArgs != null) {
  378. i = exceptionArgs.Length;
  379. argExprs = new Expression[i];
  380. argTypes = new Type[i];
  381. i = 0;
  382. foreach (object o in exceptionArgs) {
  383. Expression e = Expression.Constant(o);
  384. argExprs[i] = e;
  385. argTypes[i] = e.Type;
  386. i += 1;
  387. }
  388. }
  389. ConstructorInfo constructor = exception.GetConstructor(argTypes);
  390. if (constructor == null) {
  391. throw new ArgumentException(
  392. "Type doesn't have constructor with a given signature");
  393. }
  394. return new DynamicMetaObject(
  395. Expression.Throw(
  396. Expression.New(constructor, argExprs),
  397. // Force expression to be type object so that DLR CallSite
  398. // code things only type object flows out of the CallSite.
  399. typeof(object)),
  400. target.Restrictions.Merge(BindingRestrictions.Combine(args))
  401. .Merge(moreTests));
  402. }
  403. // EnsureObjectResult wraps expr if necessary so that any binder or
  404. // DynamicMetaObject result expression returns object. This is required
  405. // by CallSites.
  406. //
  407. public static Expression EnsureObjectResult (Expression expr) {
  408. if (! expr.Type.IsValueType)
  409. return expr;
  410. if (expr.Type == typeof(void))
  411. return Expression.Block(
  412. expr, Expression.Default(typeof(object)));
  413. else
  414. return Expression.Convert(expr, typeof(object));
  415. }
  416. } // RuntimeHelpers
  417. //#####################################################
  418. //# Dynamic Helpers for HasMember, GetMember, SetMember
  419. //#####################################################
  420. // DynamicObjectHelpers provides access to IDynObj members given names as
  421. // data at runtime. When the names are known at compile time (o.foo), then
  422. // they get baked into specific sites with specific binders that encapsulate
  423. // the name. We need this in python because hasattr et al are case-sensitive.
  424. //
  425. class DynamicObjectHelpers {
  426. static private object _sentinel = new object();
  427. static internal object Sentinel { get { return _sentinel; } }
  428. internal static bool HasMember(IDynamicMetaObjectProvider o,
  429. string name) {
  430. return (DynamicObjectHelpers.GetMember(o, name) !=
  431. DynamicObjectHelpers.Sentinel);
  432. //Alternative impl used when EOs had bug and didn't call fallback ...
  433. //var mo = o.GetMetaObject(Expression.Parameter(typeof(object), null));
  434. //foreach (string member in mo.GetDynamicMemberNames()) {
  435. // if (string.Equals(member, name, StringComparison.OrdinalIgnoreCase)) {
  436. // return true;
  437. // }
  438. //}
  439. //return false;
  440. }
  441. static private Dictionary<string,
  442. CallSite<Func<CallSite, object, object>>>
  443. _getSites = new Dictionary<string,
  444. CallSite<Func<CallSite, object, object>>>();
  445. internal static object GetMember(IDynamicMetaObjectProvider o,
  446. string name) {
  447. CallSite<Func<CallSite, object, object>> site;
  448. if (! DynamicObjectHelpers._getSites.TryGetValue(name, out site)) {
  449. site = CallSite<Func<CallSite, object, object>>
  450. .Create(new DoHelpersGetMemberBinder(name));
  451. DynamicObjectHelpers._getSites[name] = site;
  452. }
  453. return site.Target(site, o);
  454. }
  455. static private Dictionary<string,
  456. CallSite<Action<CallSite, object, object>>>
  457. _setSites = new Dictionary<string,
  458. CallSite<Action<CallSite, object, object>>>();
  459. internal static void SetMember(IDynamicMetaObjectProvider o, string name,
  460. object value) {
  461. CallSite<Action<CallSite, object, object>> site;
  462. if (! DynamicObjectHelpers._setSites.TryGetValue(name, out site)) {
  463. site = CallSite<Action<CallSite, object, object>>
  464. .Create(new DoHelpersSetMemberBinder(name));
  465. DynamicObjectHelpers._setSites[name] = site;
  466. }
  467. site.Target(site, o, value);
  468. }
  469. }
  470. class DoHelpersGetMemberBinder : GetMemberBinder {
  471. internal DoHelpersGetMemberBinder(string name) : base(name, true) { }
  472. public override DynamicMetaObject FallbackGetMember(
  473. DynamicMetaObject target, DynamicMetaObject errorSuggestion) {
  474. return errorSuggestion ??
  475. new DynamicMetaObject(
  476. Expression.Constant(DynamicObjectHelpers.Sentinel),
  477. target.Restrictions.Merge(
  478. BindingRestrictions.GetTypeRestriction(
  479. target.Expression, target.LimitType)));
  480. }
  481. }
  482. class DoHelpersSetMemberBinder : SetMemberBinder {
  483. internal DoHelpersSetMemberBinder(string name) : base(name, true) { }
  484. public override DynamicMetaObject FallbackSetMember(
  485. DynamicMetaObject target, DynamicMetaObject value,
  486. DynamicMetaObject errorSuggestion) {
  487. return errorSuggestion ??
  488. RuntimeHelpers.CreateThrow(
  489. target, null, BindingRestrictions.Empty,
  490. typeof(MissingMemberException),
  491. "If IDynObj doesn't support setting members, " +
  492. "DOHelpers can't do it for the IDO.");
  493. }
  494. }
  495. //########################
  496. // General Runtime Binders
  497. //########################
  498. // SymplGetMemberBinder is used for general dotted expressions for fetching
  499. // members.
  500. //
  501. public class SymplGetMemberBinder : GetMemberBinder {
  502. public SymplGetMemberBinder(string name) : base(name, true) {
  503. }
  504. public override DynamicMetaObject FallbackGetMember(
  505. DynamicMetaObject targetMO, DynamicMetaObject errorSuggestion) {
  506. // First try COM binding.
  507. DynamicMetaObject result;
  508. if (ComBinder.TryBindGetMember(this, targetMO, out result, true)) {
  509. return result;
  510. }
  511. // Defer if any object has no value so that we evaulate their
  512. // Expressions and nest a CallSite for the InvokeMember.
  513. if (!targetMO.HasValue) return Defer(targetMO);
  514. // Find our own binding.
  515. var flags = BindingFlags.IgnoreCase | BindingFlags.Static |
  516. BindingFlags.Instance | BindingFlags.Public;
  517. var members = targetMO.LimitType.GetMember(this.Name, flags);
  518. if (members.Length == 1) {
  519. return new DynamicMetaObject(
  520. RuntimeHelpers.EnsureObjectResult(
  521. Expression.MakeMemberAccess(
  522. Expression.Convert(targetMO.Expression,
  523. members[0].DeclaringType),
  524. members[0])),
  525. // Don't need restriction test for name since this
  526. // rule is only used where binder is used, which is
  527. // only used in sites with this binder.Name.
  528. BindingRestrictions.GetTypeRestriction(targetMO.Expression,
  529. targetMO.LimitType));
  530. } else {
  531. return errorSuggestion ??
  532. RuntimeHelpers.CreateThrow(
  533. targetMO, null,
  534. BindingRestrictions.GetTypeRestriction(targetMO.Expression,
  535. targetMO.LimitType),
  536. typeof(MissingMemberException),
  537. "cannot bind member, " + this.Name +
  538. ", on object " + targetMO.Value.ToString());
  539. }
  540. }
  541. }
  542. // SymplSetMemberBinder is used for general dotted expressions for setting
  543. // members.
  544. //
  545. public class SymplSetMemberBinder : SetMemberBinder {
  546. public SymplSetMemberBinder(string name)
  547. : base(name, true) {
  548. }
  549. public override DynamicMetaObject FallbackSetMember(
  550. DynamicMetaObject targetMO, DynamicMetaObject value,
  551. DynamicMetaObject errorSuggestion) {
  552. // First try COM binding.
  553. DynamicMetaObject result;
  554. if (ComBinder.TryBindSetMember(this, targetMO, value, out result)) {
  555. return result;
  556. }
  557. // Defer if any object has no value so that we evaulate their
  558. // Expressions and nest a CallSite for the InvokeMember.
  559. if (!targetMO.HasValue) return Defer(targetMO);
  560. // Find our own binding.
  561. var flags = BindingFlags.IgnoreCase | BindingFlags.Static |
  562. BindingFlags.Instance | BindingFlags.Public;
  563. var members = targetMO.LimitType.GetMember(this.Name, flags);
  564. if (members.Length == 1) {
  565. MemberInfo mem = members[0];
  566. Expression val;
  567. // Should check for member domain type being Type and value being
  568. // TypeModel, similar to ConvertArguments, and building an
  569. // expression like GetRuntimeTypeMoFromModel.
  570. if (mem.MemberType == MemberTypes.Property)
  571. val = Expression.Convert(value.Expression,
  572. ((PropertyInfo)mem).PropertyType);
  573. else if (mem.MemberType == MemberTypes.Field)
  574. val = Expression.Convert(value.Expression,
  575. ((FieldInfo)mem).FieldType);
  576. else
  577. return (errorSuggestion ??
  578. RuntimeHelpers.CreateThrow(
  579. targetMO, null,
  580. BindingRestrictions.GetTypeRestriction(
  581. targetMO.Expression,
  582. targetMO.LimitType),
  583. typeof(InvalidOperationException),
  584. "Sympl only supports setting Properties and " +
  585. "fields at this time."));
  586. return new DynamicMetaObject(
  587. // Assign returns the stored value, so we're good for Sympl.
  588. RuntimeHelpers.EnsureObjectResult(
  589. Expression.Assign(
  590. Expression.MakeMemberAccess(
  591. Expression.Convert(targetMO.Expression,
  592. members[0].DeclaringType),
  593. members[0]),
  594. val)),
  595. // Don't need restriction test for name since this
  596. // rule is only used where binder is used, which is
  597. // only used in sites with this binder.Name.
  598. BindingRestrictions.GetTypeRestriction(targetMO.Expression,
  599. targetMO.LimitType));
  600. } else {
  601. return errorSuggestion ??
  602. RuntimeHelpers.CreateThrow(
  603. targetMO, null,
  604. BindingRestrictions.GetTypeRestriction(targetMO.Expression,
  605. targetMO.LimitType),
  606. typeof(MissingMemberException),
  607. "IDynObj member name conflict.");
  608. }
  609. }
  610. }
  611. // SymplInvokeMemberBinder is used for general dotted expressions in function
  612. // calls for invoking members.
  613. //
  614. public class SymplInvokeMemberBinder : InvokeMemberBinder {
  615. public SymplInvokeMemberBinder(string name, CallInfo callinfo)
  616. : base(name, true, callinfo) { // true = ignoreCase
  617. }
  618. public override DynamicMetaObject FallbackInvokeMember(
  619. DynamicMetaObject targetMO, DynamicMetaObject[] args,
  620. DynamicMetaObject errorSuggestion) {
  621. // First try COM binding.
  622. DynamicMetaObject result;
  623. if (ComBinder.TryBindInvokeMember(this, targetMO, args, out result)) {
  624. return result;
  625. }
  626. // Defer if any object has no value so that we evaulate their
  627. // Expressions and nest a CallSite for the InvokeMember.
  628. if (!targetMO.HasValue || args.Any((a) => !a.HasValue)) {
  629. var deferArgs = new DynamicMetaObject[args.Length + 1];
  630. for (int i = 0; i < args.Length; i++) {
  631. deferArgs[i + 1] = args[i];
  632. }
  633. deferArgs[0] = targetMO;
  634. return Defer(deferArgs);
  635. }
  636. // Find our own binding.
  637. // Could consider allowing invoking static members from an instance.
  638. var flags = BindingFlags.IgnoreCase | BindingFlags.Instance |
  639. BindingFlags.Public;
  640. var members = targetMO.LimitType.GetMember(this.Name, flags);
  641. if ((members.Length == 1) && (members[0] is PropertyInfo ||
  642. members[0] is FieldInfo)) {
  643. // NEED TO TEST, should check for delegate value too
  644. var mem = members[0];
  645. throw new NotImplementedException();
  646. //return new DynamicMetaObject(
  647. // Expression.Dynamic(
  648. // new SymplInvokeBinder(new CallInfo(args.Length)),
  649. // typeof(object),
  650. // args.Select(a => a.Expression).AddFirst(
  651. // Expression.MakeMemberAccess(this.Expression, mem)));
  652. // Don't test for eventinfos since we do nothing with them now.
  653. } else {
  654. // Get MethodInfos with right arg counts.
  655. var mi_mems = members.
  656. Select(m => m as MethodInfo).
  657. Where(m => m is MethodInfo &&
  658. ((MethodInfo)m).GetParameters().Length ==
  659. args.Length);
  660. // Get MethodInfos with param types that work for args. This works
  661. // except for value args that need to pass to reftype params.
  662. // We could detect that to be smarter and then explicitly StrongBox
  663. // the args.
  664. List<MethodInfo> res = new List<MethodInfo>();
  665. foreach (var mem in mi_mems) {
  666. if (RuntimeHelpers.ParametersMatchArguments(
  667. mem.GetParameters(), args)) {
  668. res.Add(mem);
  669. }
  670. }
  671. // False below means generate a type restriction on the MO.
  672. // We are looking at the members targetMO's Type.
  673. var restrictions = RuntimeHelpers.GetTargetArgsRestrictions(
  674. targetMO, args, false);
  675. if (res.Count == 0) {
  676. return errorSuggestion ??
  677. RuntimeHelpers.CreateThrow(
  678. targetMO, args, restrictions,
  679. typeof(MissingMemberException),
  680. "Can't bind member invoke -- " + args.ToString());
  681. }
  682. // restrictions and conversion must be done consistently.
  683. var callArgs = RuntimeHelpers.ConvertArguments(
  684. args, res[0].GetParameters());
  685. return new DynamicMetaObject(
  686. RuntimeHelpers.EnsureObjectResult(
  687. Expression.Call(
  688. Expression.Convert(targetMO.Expression,
  689. targetMO.LimitType),
  690. res[0], callArgs)),
  691. restrictions);
  692. // Could hve tried just letting Expr.Call factory do the work,
  693. // but if there is more than one applicable method using just
  694. // assignablefrom, Expr.Call throws. It does not pick a "most
  695. // applicable" method or any method.
  696. }
  697. }
  698. public override DynamicMetaObject FallbackInvoke(
  699. DynamicMetaObject targetMO, DynamicMetaObject[] args,
  700. DynamicMetaObject errorSuggestion) {
  701. var argexprs = new Expression[args.Length + 1];
  702. for (int i = 0; i < args.Length; i++) {
  703. argexprs[i + 1] = args[i].Expression;
  704. }
  705. argexprs[0] = targetMO.Expression;
  706. // Just "defer" since we have code in SymplInvokeBinder that knows
  707. // what to do, and typically this fallback is from a language like
  708. // Python that passes a DynamicMetaObject with HasValue == false.
  709. return new DynamicMetaObject(
  710. Expression.Dynamic(
  711. // This call site doesn't share any L2 caching
  712. // since we don't call GetInvokeBinder from Sympl.
  713. // We aren't plumbed to get the runtime instance here.
  714. new SymplInvokeBinder(new CallInfo(args.Length)),
  715. typeof(object), // ret type
  716. argexprs),
  717. // No new restrictions since SymplInvokeBinder will handle it.
  718. targetMO.Restrictions.Merge(
  719. BindingRestrictions.Combine(args)));
  720. }
  721. }
  722. public class SymplInvokeBinder : InvokeBinder {
  723. public SymplInvokeBinder(CallInfo callinfo) : base(callinfo) {
  724. }
  725. public override DynamicMetaObject FallbackInvoke(
  726. DynamicMetaObject targetMO, DynamicMetaObject[] argMOs,
  727. DynamicMetaObject errorSuggestion) {
  728. // First try COM binding.
  729. DynamicMetaObject result;
  730. if (ComBinder.TryBindInvoke(this, targetMO, argMOs, out result)) {
  731. return result;
  732. }
  733. // Defer if any object has no value so that we evaulate their
  734. // Expressions and nest a CallSite for the InvokeMember.
  735. if (!targetMO.HasValue || argMOs.Any((a) => !a.HasValue)) {
  736. var deferArgs = new DynamicMetaObject[argMOs.Length + 1];
  737. for (int i = 0; i < argMOs.Length; i++) {
  738. deferArgs[i + 1] = argMOs[i];
  739. }
  740. deferArgs[0] = targetMO;
  741. return Defer(deferArgs);
  742. }
  743. // Find our own binding.
  744. if (targetMO.LimitType.IsSubclassOf(typeof(Delegate))) {
  745. var parms = targetMO.LimitType.GetMethod("Invoke").GetParameters();
  746. if (parms.Length == argMOs.Length) {
  747. // Don't need to check if argument types match parameters.
  748. // If they don't, users get an argument conversion error.
  749. var callArgs = RuntimeHelpers.ConvertArguments(argMOs, parms);
  750. var expression = Expression.Invoke(
  751. Expression.Convert(targetMO.Expression, targetMO.LimitType),
  752. callArgs);
  753. return new DynamicMetaObject(
  754. RuntimeHelpers.EnsureObjectResult(expression),
  755. BindingRestrictions.GetTypeRestriction(targetMO.Expression,
  756. targetMO.LimitType));
  757. }
  758. }
  759. return errorSuggestion ??
  760. RuntimeHelpers.CreateThrow(
  761. targetMO, argMOs,
  762. BindingRestrictions.GetTypeRestriction(targetMO.Expression,
  763. targetMO.LimitType),
  764. typeof(InvalidOperationException),
  765. "Wrong number of arguments for function -- " +
  766. targetMO.LimitType.ToString() + " got " + argMOs.ToString());
  767. }
  768. }
  769. public class SymplCreateInstanceBinder : CreateInstanceBinder {
  770. public SymplCreateInstanceBinder(CallInfo callinfo)
  771. : base(callinfo) {
  772. }
  773. public override DynamicMetaObject FallbackCreateInstance(
  774. DynamicMetaObject target,
  775. DynamicMetaObject[] args,
  776. DynamicMetaObject errorSuggestion) {
  777. // Defer if any object has no value so that we evaulate their
  778. // Expressions and nest a CallSite for the InvokeMember.
  779. if (!target.HasValue || args.Any((a) => !a.HasValue)) {
  780. var deferArgs = new DynamicMetaObject[args.Length + 1];
  781. for (int i = 0; i < args.Length; i++) {
  782. deferArgs[i + 1] = args[i];
  783. }
  784. deferArgs[0] = target;
  785. return Defer(deferArgs);
  786. }
  787. // Make sure target actually contains a Type.
  788. if (!typeof(Type).IsAssignableFrom(target.LimitType)) {
  789. return errorSuggestion ??
  790. RuntimeHelpers.CreateThrow(
  791. target, args, BindingRestrictions.Empty,
  792. typeof(InvalidOperationException),
  793. "Type object must be used when creating instance -- " +
  794. args.ToString());
  795. }
  796. var type = target.Value as Type;
  797. Debug.Assert(type != null);
  798. var constructors = type.GetConstructors();
  799. // Get constructors with right arg counts.
  800. var ctors = constructors.
  801. Where(c => c.GetParameters().Length == args.Length);
  802. List<ConstructorInfo> res = new List<ConstructorInfo>();
  803. foreach (var c in ctors) {
  804. if (RuntimeHelpers.ParametersMatchArguments(c.GetParameters(),
  805. args)) {
  806. res.Add(c);
  807. }
  808. }
  809. // We generate an instance restriction on the target since it is a
  810. // Type and the constructor is associate with the actual Type instance.
  811. var restrictions =
  812. RuntimeHelpers.GetTargetArgsRestrictions(
  813. target, args, true);
  814. if (res.Count == 0) {
  815. return errorSuggestion ??
  816. RuntimeHelpers.CreateThrow(
  817. target, args, restrictions,
  818. typeof(MissingMemberException),
  819. "Can't bind create instance -- " + args.ToString());
  820. }
  821. var ctorArgs =
  822. RuntimeHelpers.ConvertArguments(
  823. args, res[0].GetParameters());
  824. return new DynamicMetaObject(
  825. // Creating an object, so don't need EnsureObjectResult.
  826. Expression.New(res[0], ctorArgs),
  827. restrictions);
  828. }
  829. }
  830. public class SymplGetIndexBinder : GetIndexBinder {
  831. public SymplGetIndexBinder(CallInfo callinfo)
  832. : base(callinfo) {
  833. }
  834. public override DynamicMetaObject FallbackGetIndex(
  835. DynamicMetaObject target, DynamicMetaObject[] indexes,
  836. DynamicMetaObject errorSuggestion) {
  837. // First try COM binding.
  838. DynamicMetaObject result;
  839. if (ComBinder.TryBindGetIndex(this, target, indexes, out result)) {
  840. return result;
  841. }
  842. // Defer if any object has no value so that we evaulate their
  843. // Expressions and nest a CallSite for the InvokeMember.
  844. if (!target.HasValue || indexes.Any((a) => !a.HasValue)) {
  845. var deferArgs = new DynamicMetaObject[indexes.Length + 1];
  846. for (int i = 0; i < indexes.Length; i++) {
  847. deferArgs[i + 1] = indexes[i];
  848. }
  849. deferArgs[0] = target;
  850. return Defer(deferArgs);
  851. }
  852. // Give good error for Cons.
  853. if (target.LimitType == typeof(Cons)) {
  854. if (indexes.Length != 1)
  855. return errorSuggestion ??
  856. RuntimeHelpers.CreateThrow(
  857. target, indexes, BindingRestrictions.Empty,
  858. typeof(InvalidOperationException),
  859. "Indexing list takes single index. " + "Got " +
  860. indexes.Length.ToString());
  861. }
  862. // Find our own binding.
  863. //
  864. // Conversions created in GetIndexExpression must be consistent with
  865. // restrictions made in GetTargetArgsRestrictions.
  866. var indexingExpr = RuntimeHelpers.EnsureObjectResult(
  867. RuntimeHelpers.GetIndexingExpression(target,
  868. indexes));
  869. var restrictions = RuntimeHelpers.GetTargetArgsRestrictions(
  870. target, indexes, false);
  871. return new DynamicMetaObject(indexingExpr, restrictions);
  872. }
  873. }
  874. public class SymplSetIndexBinder : SetIndexBinder {
  875. public SymplSetIndexBinder(CallInfo callinfo)
  876. : base(callinfo) {
  877. }
  878. public override DynamicMetaObject FallbackSetIndex(
  879. DynamicMetaObject target, DynamicMetaObject[] indexes,
  880. DynamicMetaObject value, DynamicMetaObject errorSuggestion) {
  881. // First try COM binding.
  882. DynamicMetaObject result;
  883. if (ComBinder.TryBindSetIndex(this, target, indexes, value, out result)) {
  884. return result;
  885. }
  886. // Defer if any object has no value so that we evaulate their
  887. // Expressions and nest a CallSite for the InvokeMember.
  888. if (!target.HasValue || indexes.Any((a) => !a.HasValue) ||
  889. !value.HasValue) {
  890. var deferArgs = new DynamicMetaObject[indexes.Length + 2];
  891. for (int i = 0; i < indexes.Length; i++) {
  892. deferArgs[i + 1] = indexes[i];
  893. }
  894. deferArgs[0] = target;
  895. deferArgs[indexes.Length + 1] = value;
  896. return Defer(deferArgs);
  897. }
  898. // Find our own binding.
  899. Expression valueExpr = value.Expression;
  900. //we convert a value of TypeModel to Type.
  901. if (value.LimitType == typeof(TypeModel)) {
  902. valueExpr = RuntimeHelpers.GetRuntimeTypeMoFromModel(value).Expression;
  903. }
  904. Debug.Assert(target.HasValue && target.LimitType != typeof(Array));
  905. Expression setIndexExpr;
  906. if (target.LimitType == typeof(Cons)) {
  907. if (indexes.Length != 1) {
  908. return errorSuggestion ??
  909. RuntimeHelpers.CreateThrow(
  910. target, indexes, BindingRestrictions.Empty,
  911. typeof(InvalidOperationException),
  912. "Indexing list takes single index. " + "Got " + indexes);
  913. }
  914. // Call RuntimeHelper.SetConsElt
  915. List<Expression> args = new List<Expression>();
  916. // The first argument is the list
  917. args.Add(
  918. Expression.Convert(
  919. target.Expression,
  920. target.LimitType)
  921. );
  922. // The second argument is the index.
  923. args.Add(Expression.Convert(indexes[0].Expression,
  924. indexes[0].LimitType));
  925. // The last argument is the value
  926. args.Add(Expression.Convert(valueExpr, typeof(object)));
  927. // Sympl helper returns value stored.
  928. setIndexExpr = Expression.Call(
  929. typeof(RuntimeHelpers),
  930. "SetConsElt",
  931. null,
  932. args.ToArray());
  933. } else {
  934. Expression indexingExpr = RuntimeHelpers.GetIndexingExpression(
  935. target, indexes);
  936. // Assign returns the stored value, so we're good for Sympl.
  937. setIndexExpr = Expression.Assign(indexingExpr, valueExpr);
  938. }
  939. BindingRestrictions restrictions =
  940. RuntimeHelpers.GetTargetArgsRestrictions(target, indexes, false);
  941. return new DynamicMetaObject(
  942. RuntimeHelpers.EnsureObjectResult(setIndexExpr),
  943. restrictions);
  944. }
  945. }
  946. public class SymplBinaryOperationBinder : BinaryOperationBinder {
  947. public SymplBinaryOperationBinder(ExpressionType operation)
  948. : base(operation) {
  949. }
  950. public override DynamicMetaObject FallbackBinaryOperation(
  951. DynamicMetaObject target, DynamicMetaObject arg,
  952. DynamicMetaObject errorSuggestion) {
  953. // Defer if any object has no value so that we evaulate their
  954. // Expressions and nest a CallSite for the InvokeMember.
  955. if (!target.HasValue || !arg.HasValue) {
  956. return Defer(target, arg);
  957. }
  958. var restrictions = target.Restrictions.Merge(arg.Restrictions)
  959. .Merge(BindingRestrictions.GetTypeRestriction(
  960. target.Expression, target.LimitType))
  961. .Merge(BindingRestrictions.GetTypeRestriction(
  962. arg.Expression, arg.LimitType));
  963. return new DynamicMetaObject(
  964. RuntimeHelpers.EnsureObjectResult(
  965. Expression.MakeBinary(
  966. this.Operation,
  967. Expression.Convert(target.Expression, target.LimitType),
  968. Expression.Convert(arg.Expression, arg.LimitType))),
  969. restrictions
  970. );
  971. }
  972. }
  973. public class SymplUnaryOperationBinder : UnaryOperationBinder {
  974. public SymplUnaryOperationBinder(ExpressionType operation)
  975. : base(operation) {
  976. }
  977. public override DynamicMetaObject FallbackUnaryOperation(
  978. DynamicMetaObject target, DynamicMetaObject errorSuggestion) {
  979. // Defer if any object has no value so that we evaulate their
  980. // Expressions and nest a CallSite for the InvokeMember.
  981. if (!target.HasValue) {
  982. return Defer(target);
  983. }
  984. return new DynamicMetaObject(
  985. RuntimeHelpers.EnsureObjectResult(
  986. Expression.MakeUnary(
  987. this.Operation,
  988. Expression.Convert(target.Expression, ta

Large files files are truncated, but you can click here to view the full file