PageRenderTime 49ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/DLR_Main/Languages/IronPython/IronPython/Runtime/Binding/ConversionBinder.cs

https://bitbucket.org/mdavid/dlr
C# | 908 lines | 740 code | 123 blank | 45 comment | 250 complexity | 5387050ea7ee6014efa0527d6eda599b MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if !CLR2
  16. using System.Linq.Expressions;
  17. using System.Numerics;
  18. #else
  19. using Microsoft.Scripting.Ast;
  20. #endif
  21. using System;
  22. using System.Collections;
  23. using System.Collections.Generic;
  24. using System.Runtime.CompilerServices;
  25. using System.Dynamic;
  26. using IronPython.Runtime.Operations;
  27. using IronPython.Runtime.Types;
  28. using Microsoft.Scripting.Actions;
  29. using Microsoft.Scripting.Generation;
  30. using Microsoft.Scripting.Runtime;
  31. using Microsoft.Scripting.Utils;
  32. using Microsoft.Scripting;
  33. using System.Reflection;
  34. using System.Diagnostics;
  35. namespace IronPython.Runtime.Binding {
  36. using Ast = Expression;
  37. using AstUtils = Microsoft.Scripting.Ast.Utils;
  38. class PythonConversionBinder : DynamicMetaObjectBinder, IPythonSite, IExpressionSerializable {
  39. private readonly PythonContext/*!*/ _context;
  40. private readonly ConversionResultKind/*!*/ _kind;
  41. private readonly Type _type;
  42. private readonly bool _retObject;
  43. private CompatConversionBinder _compatConvert;
  44. public PythonConversionBinder(PythonContext/*!*/ context, Type/*!*/ type, ConversionResultKind resultKind) {
  45. Assert.NotNull(context, type);
  46. _context = context;
  47. _kind = resultKind;
  48. _type = type;
  49. }
  50. public PythonConversionBinder(PythonContext/*!*/ context, Type/*!*/ type, ConversionResultKind resultKind, bool retObject) {
  51. Assert.NotNull(context, type);
  52. _context = context;
  53. _kind = resultKind;
  54. _type = type;
  55. _retObject = retObject;
  56. }
  57. public Type Type {
  58. get {
  59. return _type;
  60. }
  61. }
  62. public ConversionResultKind ResultKind {
  63. get {
  64. return _kind;
  65. }
  66. }
  67. public override DynamicMetaObject Bind(DynamicMetaObject target, DynamicMetaObject[] args) {
  68. DynamicMetaObject self = target;
  69. DynamicMetaObject res = null;
  70. if (self.NeedsDeferral()) {
  71. return MyDefer(self);
  72. }
  73. IPythonConvertible convertible = target as IPythonConvertible;
  74. if (convertible != null) {
  75. res = convertible.BindConvert(this);
  76. } else if (res == null) {
  77. res = BindConvert(self);
  78. }
  79. if (_retObject) {
  80. res = new DynamicMetaObject(
  81. AstUtils.Convert(res.Expression, typeof(object)),
  82. res.Restrictions
  83. );
  84. }
  85. return res;
  86. }
  87. public override Type ReturnType {
  88. get {
  89. if (_retObject) {
  90. return typeof(object);
  91. }
  92. return (_kind == ConversionResultKind.ExplicitCast || _kind == ConversionResultKind.ImplicitCast) ?
  93. Type :
  94. _type.IsValueType ?
  95. typeof(object) :
  96. _type;
  97. }
  98. }
  99. private DynamicMetaObject MyDefer(DynamicMetaObject self) {
  100. return new DynamicMetaObject(
  101. Expression.Dynamic(
  102. this,
  103. ReturnType,
  104. self.Expression
  105. ),
  106. self.Restrictions
  107. );
  108. }
  109. private DynamicMetaObject BindConvert(DynamicMetaObject self) {
  110. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Convert " + Type.FullName + " " + self.LimitType);
  111. PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "Conversion");
  112. DynamicMetaObject res;
  113. #if !SILVERLIGHT
  114. DynamicMetaObject comConvert;
  115. if (Microsoft.Scripting.ComInterop.ComBinder.TryConvert(CompatBinder, self, out comConvert)) {
  116. res = comConvert;
  117. } else
  118. #endif
  119. {
  120. res = self.BindConvert(CompatBinder);
  121. }
  122. // if we return object and the interop binder had to put on an extra conversion
  123. // to the strong type go ahead and remove it now.
  124. if (ReturnType == typeof(object) &&
  125. res.Expression.Type != typeof(object) &&
  126. res.Expression.NodeType == ExpressionType.Convert) {
  127. res = new DynamicMetaObject(
  128. ((UnaryExpression)res.Expression).Operand,
  129. res.Restrictions
  130. );
  131. }
  132. return res;
  133. }
  134. internal CompatConversionBinder CompatBinder {
  135. get {
  136. if (_compatConvert == null) {
  137. _compatConvert = new CompatConversionBinder(this, Type, _kind == ConversionResultKind.ExplicitCast || _kind == ConversionResultKind.ExplicitTry);
  138. }
  139. return _compatConvert;
  140. }
  141. }
  142. internal DynamicMetaObject FallbackConvert(Type returnType, DynamicMetaObject self, DynamicMetaObject errorSuggestion) {
  143. Type type = Type;
  144. DynamicMetaObject res = null;
  145. switch (Type.GetTypeCode(type)) {
  146. case TypeCode.Boolean:
  147. res = MakeToBoolConversion(self);
  148. break;
  149. case TypeCode.Char:
  150. res = TryToCharConversion(self);
  151. break;
  152. case TypeCode.String:
  153. if (self.GetLimitType() == typeof(Bytes) && !_context.PythonOptions.Python30) {
  154. res = new DynamicMetaObject(
  155. Ast.Call(
  156. typeof(PythonOps).GetMethod("MakeString"),
  157. AstUtils.Convert(self.Expression, typeof(IList<byte>))
  158. ),
  159. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, typeof(Bytes))
  160. );
  161. }
  162. break;
  163. case TypeCode.Object:
  164. // !!! Deferral?
  165. if (type.IsArray && self.Value is PythonTuple && type.GetArrayRank() == 1) {
  166. res = MakeToArrayConversion(self, type);
  167. } else if (type.IsGenericType && !type.IsAssignableFrom(CompilerHelpers.GetType(self.Value))) {
  168. Type genTo = type.GetGenericTypeDefinition();
  169. // Interface conversion helpers...
  170. if (genTo == typeof(IList<>)) {
  171. if (self.LimitType == typeof(string)) {
  172. res = new DynamicMetaObject(
  173. Ast.Call(
  174. typeof(PythonOps).GetMethod("MakeByteArray"),
  175. AstUtils.Convert(self.Expression, typeof(string))
  176. ),
  177. BindingRestrictions.GetTypeRestriction(
  178. self.Expression,
  179. typeof(string)
  180. )
  181. );
  182. } else {
  183. res = TryToGenericInterfaceConversion(self, type, typeof(IList<object>), typeof(ListGenericWrapper<>));
  184. }
  185. } else if (genTo == typeof(IDictionary<,>)) {
  186. res = TryToGenericInterfaceConversion(self, type, typeof(IDictionary<object, object>), typeof(DictionaryGenericWrapper<,>));
  187. } else if (genTo == typeof(IEnumerable<>)) {
  188. res = TryToGenericInterfaceConversion(self, type, typeof(IEnumerable), typeof(IEnumerableOfTWrapper<>));
  189. }
  190. } else if (type == typeof(IEnumerable)) {
  191. if (!typeof(IEnumerable).IsAssignableFrom(self.GetLimitType()) && IsIndexless(self)) {
  192. res = ConvertToIEnumerable(this, self.Restrict(self.GetLimitType()));
  193. }
  194. } else if (type == typeof(IEnumerator)) {
  195. if (!typeof(IEnumerator).IsAssignableFrom(self.GetLimitType()) &&
  196. !typeof(IEnumerable).IsAssignableFrom(self.GetLimitType()) &&
  197. IsIndexless(self)) {
  198. res = ConvertToIEnumerator(this, self.Restrict(self.GetLimitType()));
  199. }
  200. }
  201. break;
  202. }
  203. if (type.IsEnum && Enum.GetUnderlyingType(type) == self.GetLimitType()) {
  204. // numeric type to enum, this is ok if the value is zero
  205. object value = Activator.CreateInstance(type);
  206. return new DynamicMetaObject(
  207. Ast.Condition(
  208. Ast.Equal(
  209. AstUtils.Convert(self.Expression, Enum.GetUnderlyingType(type)),
  210. AstUtils.Constant(Activator.CreateInstance(self.GetLimitType()))
  211. ),
  212. AstUtils.Constant(value),
  213. Ast.Call(
  214. typeof(PythonOps).GetMethod("TypeErrorForBadEnumConversion").MakeGenericMethod(type),
  215. AstUtils.Convert(self.Expression, typeof(object))
  216. )
  217. ),
  218. self.Restrictions.Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, self.GetLimitType())),
  219. value
  220. );
  221. }
  222. return res ?? EnsureReturnType(returnType, Context.Binder.ConvertTo(Type, ResultKind, self, _context.SharedOverloadResolverFactory, errorSuggestion));
  223. }
  224. private static DynamicMetaObject EnsureReturnType(Type returnType, DynamicMetaObject dynamicMetaObject) {
  225. if (dynamicMetaObject.Expression.Type != returnType) {
  226. dynamicMetaObject = new DynamicMetaObject(
  227. AstUtils.Convert(
  228. dynamicMetaObject.Expression,
  229. returnType
  230. ),
  231. dynamicMetaObject.Restrictions
  232. );
  233. }
  234. return dynamicMetaObject;
  235. }
  236. public override T BindDelegate<T>(CallSite<T> site, object[] args) {
  237. //Debug.Assert(typeof(T).GetMethod("Invoke").ReturnType == Type);
  238. object target = args[0];
  239. T res = null;
  240. if (typeof(T) == typeof(Func<CallSite, object, string>) && target is string) {
  241. res = (T)(object)new Func<CallSite, object, string>(StringConversion);
  242. } else if (typeof(T) == typeof(Func<CallSite, object, int>)) {
  243. if (target is int) {
  244. res = (T)(object)new Func<CallSite, object, int>(IntConversion);
  245. } else if (target is bool) {
  246. res = (T)(object)new Func<CallSite, object, int>(BoolToIntConversion);
  247. }
  248. } else if (typeof(T) == typeof(Func<CallSite, bool, int>)) {
  249. res = (T)(object)new Func<CallSite, bool, int>(BoolToIntConversion);
  250. } else if (typeof(T) == typeof(Func<CallSite, object, bool>)) {
  251. if (target is bool) {
  252. res = (T)(object)new Func<CallSite, object, bool>(BoolConversion);
  253. } else if (target is string) {
  254. res = (T)(object)new Func<CallSite, object, bool>(StringToBoolConversion);
  255. } else if (target is int) {
  256. res = (T)(object)new Func<CallSite, object, bool>(IntToBoolConversion);
  257. } else if (target == null) {
  258. res = (T)(object)new Func<CallSite, object, bool>(NullToBoolConversion);
  259. } else if (target.GetType() == typeof(object)) {
  260. res = (T)(object)new Func<CallSite, object, bool>(ObjectToBoolConversion);
  261. } else if (target.GetType() == typeof(List)) {
  262. res = (T)(object)new Func<CallSite, object, bool>(ListToBoolConversion);
  263. } else if (target.GetType() == typeof(PythonTuple)) {
  264. res = (T)(object)new Func<CallSite, object, bool>(TupleToBoolConversion);
  265. }
  266. } else if (target != null) {
  267. // Special cases:
  268. // - string or bytes to IEnumerable or IEnumerator
  269. // - CLR 4 only: BigInteger -> Complex
  270. #if !CLR2
  271. if (target is BigInteger) {
  272. if (typeof(T) == typeof(Func<CallSite, BigInteger, Complex>)) {
  273. res = (T)(object)new Func<CallSite, BigInteger, Complex>(BigIntegerToComplexConversion);
  274. } else if (typeof(T) == typeof(Func<CallSite, object, Complex>)) {
  275. res = (T)(object)new Func<CallSite, object, Complex>(BigIntegerObjectToComplexConversion);
  276. } else if (typeof(T) == typeof(Func<CallSite, BigInteger, object>)) {
  277. res = (T)(object)new Func<CallSite, BigInteger, object>(BigIntegerToComplexObjectConversion);
  278. }
  279. } else
  280. #endif
  281. if (target is string) {
  282. if (typeof(T) == typeof(Func<CallSite, string, IEnumerable>)) {
  283. res = (T)(object)new Func<CallSite, string, IEnumerable>(StringToIEnumerableConversion);
  284. } else if (typeof(T) == typeof(Func<CallSite, string, IEnumerator>)) {
  285. res = (T)(object)new Func<CallSite, string, IEnumerator>(StringToIEnumeratorConversion);
  286. } else if (typeof(T) == typeof(Func<CallSite, object, IEnumerable>)) {
  287. res = (T)(object)new Func<CallSite, object, IEnumerable>(ObjectToIEnumerableConversion);
  288. } else if (typeof(T) == typeof(Func<CallSite, object, IEnumerator>)) {
  289. res = (T)(object)new Func<CallSite, object, IEnumerator>(ObjectToIEnumeratorConversion);
  290. }
  291. } else if (target.GetType() == typeof(Bytes)) {
  292. if (typeof(T) == typeof(Func<CallSite, Bytes, IEnumerable>)) {
  293. res = (T)(object)new Func<CallSite, Bytes, IEnumerable>(BytesToIEnumerableConversion);
  294. } else if (typeof(T) == typeof(Func<CallSite, Bytes, IEnumerator>)) {
  295. res = (T)(object)new Func<CallSite, Bytes, IEnumerator>(BytesToIEnumeratorConversion);
  296. } else if (typeof(T) == typeof(Func<CallSite, object, IEnumerable>)) {
  297. res = (T)(object)new Func<CallSite, object, IEnumerable>(ObjectToIEnumerableConversion);
  298. } else if (typeof(T) == typeof(Func<CallSite, object, IEnumerator>)) {
  299. res = (T)(object)new Func<CallSite, object, IEnumerator>(ObjectToIEnumeratorConversion);
  300. }
  301. }
  302. if (res == null && (target.GetType() == Type || Type.IsAssignableFrom(target.GetType()))) {
  303. if (typeof(T) == typeof(Func<CallSite, object, object>)) {
  304. // called via a helper call site in the runtime (e.g. Converter.Convert)
  305. res = (T)(object)new Func<CallSite, object, object>(new IdentityConversion(target.GetType()).Convert);
  306. } else {
  307. // called via an embedded call site
  308. Debug.Assert(typeof(T).GetMethod("Invoke").ReturnType == Type);
  309. if (typeof(T).GetMethod("Invoke").GetParameters()[1].ParameterType == typeof(object)) {
  310. object identityConversion = Activator.CreateInstance(typeof(IdentityConversion<>).MakeGenericType(Type), target.GetType());
  311. res = (T)(object)Delegate.CreateDelegate(typeof(T), identityConversion, identityConversion.GetType().GetMethod("Convert"));
  312. }
  313. }
  314. }
  315. }
  316. if (res != null) {
  317. CacheTarget(res);
  318. return res;
  319. }
  320. PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Convert " + Type.FullName + " " + CompilerHelpers.GetType(args[0]) + " " + typeof(T));
  321. return base.BindDelegate(site, args);
  322. }
  323. public string StringConversion(CallSite site, object value) {
  324. string str = value as string;
  325. if (str != null) {
  326. return str;
  327. }
  328. return ((CallSite<Func<CallSite, object, string>>)site).Update(site, value);
  329. }
  330. public int IntConversion(CallSite site, object value) {
  331. if (value is int) {
  332. return (int)value;
  333. }
  334. return ((CallSite<Func<CallSite, object, int>>)site).Update(site, value);
  335. }
  336. public int BoolToIntConversion(CallSite site, object value) {
  337. if (value is bool) {
  338. return (bool)value ? 1 : 0;
  339. }
  340. return ((CallSite<Func<CallSite, object, int>>)site).Update(site, value);
  341. }
  342. public int BoolToIntConversion(CallSite site, bool value) {
  343. return (bool)value ? 1 : 0;
  344. }
  345. public bool BoolConversion(CallSite site, object value) {
  346. if (value is bool) {
  347. return (bool)value;
  348. } else if (value == null) {
  349. // improve perf of sites just polymorphic on bool & None
  350. return false;
  351. }
  352. return ((CallSite<Func<CallSite, object, bool>>)site).Update(site, value);
  353. }
  354. public bool IntToBoolConversion(CallSite site, object value) {
  355. if (value is int) {
  356. return (int)value != 0;
  357. } else if (value == null) {
  358. // improve perf of sites just polymorphic on int & None
  359. return false;
  360. }
  361. return ((CallSite<Func<CallSite, object, bool>>)site).Update(site, value);
  362. }
  363. public bool StringToBoolConversion(CallSite site, object value) {
  364. if (value is string) {
  365. return ((string)value).Length > 0;
  366. } else if (value == null) {
  367. // improve perf of sites just polymorphic on str & None
  368. return false;
  369. }
  370. return ((CallSite<Func<CallSite, object, bool>>)site).Update(site, value);
  371. }
  372. public bool NullToBoolConversion(CallSite site, object value) {
  373. if (value == null) {
  374. return false;
  375. }
  376. return ((CallSite<Func<CallSite, object, bool>>)site).Update(site, value);
  377. }
  378. public bool ObjectToBoolConversion(CallSite site, object value) {
  379. if (value != null && value.GetType() == typeof(Object)) {
  380. return true;
  381. } else if (value == null) {
  382. // improve perf of sites just polymorphic on object & None
  383. return false;
  384. }
  385. return ((CallSite<Func<CallSite, object, bool>>)site).Update(site, value);
  386. }
  387. public bool ListToBoolConversion(CallSite site, object value) {
  388. if (value == null) {
  389. return false;
  390. } else if (value.GetType() == typeof(List)) {
  391. return ((List)value).Count != 0;
  392. }
  393. return ((CallSite<Func<CallSite, object, bool>>)site).Update(site, value);
  394. }
  395. public bool TupleToBoolConversion(CallSite site, object value) {
  396. if (value == null) {
  397. return false;
  398. } else if (value.GetType() == typeof(PythonTuple)) {
  399. return ((PythonTuple)value).Count != 0;
  400. }
  401. return ((CallSite<Func<CallSite, object, bool>>)site).Update(site, value);
  402. }
  403. public IEnumerable StringToIEnumerableConversion(CallSite site, string value) {
  404. if (value == null) {
  405. return ((CallSite<Func<CallSite, string, IEnumerable>>)site).Update(site, value);
  406. }
  407. return PythonOps.StringEnumerable(value);
  408. }
  409. public IEnumerator StringToIEnumeratorConversion(CallSite site, string value) {
  410. if (value == null) {
  411. return ((CallSite<Func<CallSite, string, IEnumerator>>)site).Update(site, value);
  412. }
  413. return PythonOps.StringEnumerator(value).Key;
  414. }
  415. public IEnumerable BytesToIEnumerableConversion(CallSite site, Bytes value) {
  416. if (value == null) {
  417. return ((CallSite<Func<CallSite, Bytes, IEnumerable>>)site).Update(site, value);
  418. }
  419. return _context.PythonOptions.Python30 ?
  420. PythonOps.BytesIntEnumerable(value) :
  421. PythonOps.BytesEnumerable(value);
  422. }
  423. public IEnumerator BytesToIEnumeratorConversion(CallSite site, Bytes value) {
  424. if (value == null) {
  425. return ((CallSite<Func<CallSite, Bytes, IEnumerator>>)site).Update(site, value);
  426. }
  427. return _context.PythonOptions.Python30 ?
  428. (IEnumerator)PythonOps.BytesIntEnumerator(value).Key :
  429. (IEnumerator)PythonOps.BytesEnumerator(value).Key;
  430. }
  431. public IEnumerable ObjectToIEnumerableConversion(CallSite site, object value) {
  432. if (value != null) {
  433. if (value is string) {
  434. return PythonOps.StringEnumerable((string)value);
  435. } else if (value.GetType() == typeof(Bytes)) {
  436. return _context.PythonOptions.Python30 ?
  437. PythonOps.BytesIntEnumerable((Bytes)value) :
  438. PythonOps.BytesEnumerable((Bytes)value);
  439. }
  440. }
  441. return ((CallSite<Func<CallSite, object, IEnumerable>>)site).Update(site, value);
  442. }
  443. public IEnumerator ObjectToIEnumeratorConversion(CallSite site, object value) {
  444. if (value != null) {
  445. if (value is string) {
  446. return PythonOps.StringEnumerator((string)value).Key;
  447. } else if (value.GetType() == typeof(Bytes)) {
  448. return _context.PythonOptions.Python30 ?
  449. (IEnumerator)PythonOps.BytesIntEnumerator((Bytes)value).Key :
  450. (IEnumerator)PythonOps.BytesEnumerator((Bytes)value).Key;
  451. }
  452. }
  453. return ((CallSite<Func<CallSite, object, IEnumerator>>)site).Update(site, value);
  454. }
  455. #if !CLR2
  456. public Complex BigIntegerToComplexConversion(CallSite site, BigInteger value) {
  457. return BigIntegerOps.ConvertToComplex(value);
  458. }
  459. public Complex BigIntegerObjectToComplexConversion(CallSite site, object value) {
  460. if (value is BigInteger) {
  461. return BigIntegerOps.ConvertToComplex((BigInteger)value);
  462. }
  463. return ((CallSite<Func<CallSite, object, Complex>>)site).Update(site, value);
  464. }
  465. public object BigIntegerToComplexObjectConversion(CallSite site, BigInteger value) {
  466. return (object)BigIntegerOps.ConvertToComplex((BigInteger)value);
  467. }
  468. #endif
  469. class IdentityConversion {
  470. private readonly Type _type;
  471. public IdentityConversion(Type type) {
  472. _type = type;
  473. }
  474. public object Convert(CallSite site, object value) {
  475. if (value != null && value.GetType() == _type) {
  476. return value;
  477. }
  478. return ((CallSite<Func<CallSite, object, object>>)site).Update(site, value);
  479. }
  480. }
  481. class IdentityConversion<T> {
  482. private readonly Type _type;
  483. public IdentityConversion(Type type) {
  484. _type = type;
  485. }
  486. public T Convert(CallSite site, object value) {
  487. if (value != null && value.GetType() == _type) {
  488. return (T)value;
  489. }
  490. return ((CallSite<Func<CallSite, object, T>>)site).Update(site, value);
  491. }
  492. }
  493. internal static bool IsIndexless(DynamicMetaObject/*!*/ arg) {
  494. return arg.GetLimitType() != typeof(OldInstance);
  495. }
  496. public override int GetHashCode() {
  497. return base.GetHashCode() ^ _context.Binder.GetHashCode() ^ _kind.GetHashCode();
  498. }
  499. public override bool Equals(object obj) {
  500. PythonConversionBinder ob = obj as PythonConversionBinder;
  501. if (ob == null) {
  502. return false;
  503. }
  504. return ob._context.Binder == _context.Binder &&
  505. _kind == ob._kind && base.Equals(obj) &&
  506. _retObject == ob._retObject;
  507. }
  508. public PythonContext/*!*/ Context {
  509. get {
  510. return _context;
  511. }
  512. }
  513. #region Conversion Logic
  514. private static DynamicMetaObject TryToGenericInterfaceConversion(DynamicMetaObject/*!*/ self, Type/*!*/ toType, Type/*!*/ fromType, Type/*!*/ wrapperType) {
  515. if (fromType.IsAssignableFrom(CompilerHelpers.GetType(self.Value))) {
  516. Type making = wrapperType.MakeGenericType(toType.GetGenericArguments());
  517. self = self.Restrict(CompilerHelpers.GetType(self.Value));
  518. return new DynamicMetaObject(
  519. Ast.New(
  520. making.GetConstructor(new Type[] { fromType }),
  521. AstUtils.Convert(
  522. self.Expression,
  523. fromType
  524. )
  525. ),
  526. self.Restrictions
  527. );
  528. }
  529. return null;
  530. }
  531. private static DynamicMetaObject/*!*/ MakeToArrayConversion(DynamicMetaObject/*!*/ self, Type/*!*/ toType) {
  532. self = self.Restrict(typeof(PythonTuple));
  533. return new DynamicMetaObject(
  534. Ast.Call(
  535. typeof(PythonOps).GetMethod("ConvertTupleToArray").MakeGenericMethod(toType.GetElementType()),
  536. self.Expression
  537. ),
  538. self.Restrictions
  539. );
  540. }
  541. private DynamicMetaObject TryToCharConversion(DynamicMetaObject/*!*/ self) {
  542. DynamicMetaObject res;
  543. // we have an implicit conversion to char if the
  544. // string length == 1, but we can only represent
  545. // this is implicit via a rule.
  546. string strVal = self.Value as string;
  547. Expression strExpr = self.Expression;
  548. if (strVal == null) {
  549. Extensible<string> extstr = self.Value as Extensible<string>;
  550. if (extstr != null) {
  551. strVal = extstr.Value;
  552. strExpr =
  553. Ast.Property(
  554. AstUtils.Convert(
  555. strExpr,
  556. typeof(Extensible<string>)
  557. ),
  558. typeof(Extensible<string>).GetProperty("Value")
  559. );
  560. }
  561. }
  562. // we can only produce a conversion if we have a string value...
  563. if (strVal != null) {
  564. self = self.Restrict(self.GetRuntimeType());
  565. Expression getLen = Ast.Property(
  566. AstUtils.Convert(
  567. strExpr,
  568. typeof(string)
  569. ),
  570. typeof(string).GetProperty("Length")
  571. );
  572. if (strVal.Length == 1) {
  573. res = new DynamicMetaObject(
  574. Ast.Call(
  575. AstUtils.Convert(strExpr, typeof(string)),
  576. typeof(string).GetMethod("get_Chars"),
  577. AstUtils.Constant(0)
  578. ),
  579. self.Restrictions.Merge(BindingRestrictions.GetExpressionRestriction(Ast.Equal(getLen, AstUtils.Constant(1))))
  580. );
  581. } else {
  582. res = new DynamicMetaObject(
  583. this.Throw(
  584. Ast.Call(
  585. typeof(PythonOps).GetMethod("TypeError"),
  586. AstUtils.Constant("expected string of length 1 when converting to char, got '{0}'"),
  587. Ast.NewArrayInit(typeof(object), self.Expression)
  588. ),
  589. ReturnType
  590. ),
  591. self.Restrictions.Merge(BindingRestrictions.GetExpressionRestriction(Ast.NotEqual(getLen, AstUtils.Constant(1))))
  592. );
  593. }
  594. } else {
  595. // let the base class produce the rule
  596. res = null;
  597. }
  598. return res;
  599. }
  600. private DynamicMetaObject/*!*/ MakeToBoolConversion(DynamicMetaObject/*!*/ self) {
  601. DynamicMetaObject res;
  602. if (self.HasValue) {
  603. self = self.Restrict(self.GetRuntimeType());
  604. }
  605. // Optimization: if we already boxed it to a bool, and now
  606. // we're unboxing it, remove the unnecessary box.
  607. if (self.Expression.NodeType == ExpressionType.Convert && self.Expression.Type == typeof(object)) {
  608. var convert = (UnaryExpression)self.Expression;
  609. if (convert.Operand.Type == typeof(bool)) {
  610. return new DynamicMetaObject(convert.Operand, self.Restrictions);
  611. }
  612. }
  613. if (self.GetLimitType() == typeof(DynamicNull)) {
  614. // None has no __nonzero__ and no __len__ but it's always false
  615. res = MakeNoneToBoolConversion(self);
  616. } else if (self.GetLimitType() == typeof(bool)) {
  617. // nothing special to convert from bool to bool
  618. res = self;
  619. } else if (typeof(IStrongBox).IsAssignableFrom(self.GetLimitType())) {
  620. // Explictly block conversion of References to bool
  621. res = MakeStrongBoxToBoolConversionError(self);
  622. } else if (self.GetLimitType().IsPrimitive || self.GetLimitType().IsEnum) {
  623. // optimization - rather than doing a method call for primitives and enums generate
  624. // the comparison to zero directly.
  625. res = MakePrimitiveToBoolComparison(self);
  626. } else {
  627. // anything non-null that doesn't fall under one of the above rules is true. So we
  628. // fallback to the base Python conversion which will check for __nonzero__ and
  629. // __len__. The fallback is handled by our ConvertTo site binder.
  630. return
  631. PythonProtocol.ConvertToBool(this, self) ??
  632. new DynamicMetaObject(
  633. AstUtils.Constant(true),
  634. self.Restrictions
  635. );
  636. }
  637. return res;
  638. }
  639. private static DynamicMetaObject/*!*/ MakeNoneToBoolConversion(DynamicMetaObject/*!*/ self) {
  640. // null is never true
  641. return new DynamicMetaObject(
  642. AstUtils.Constant(false),
  643. self.Restrictions
  644. );
  645. }
  646. private static DynamicMetaObject/*!*/ MakePrimitiveToBoolComparison(DynamicMetaObject/*!*/ self) {
  647. object zeroVal = Activator.CreateInstance(self.GetLimitType());
  648. return new DynamicMetaObject(
  649. Ast.NotEqual(
  650. AstUtils.Constant(zeroVal),
  651. self.Expression
  652. ),
  653. self.Restrictions
  654. );
  655. }
  656. private DynamicMetaObject/*!*/ MakeStrongBoxToBoolConversionError(DynamicMetaObject/*!*/ self) {
  657. return new DynamicMetaObject(
  658. this.Throw(
  659. Ast.Call(
  660. typeof(ScriptingRuntimeHelpers).GetMethod("SimpleTypeError"),
  661. AstUtils.Constant("Can't convert a Reference<> instance to a bool")
  662. ),
  663. ReturnType
  664. ),
  665. self.Restrictions
  666. );
  667. }
  668. internal static DynamicMetaObject ConvertToIEnumerable(DynamicMetaObjectBinder/*!*/ conversion, DynamicMetaObject/*!*/ metaUserObject) {
  669. PythonType pt = MetaPythonObject.GetPythonType(metaUserObject);
  670. PythonContext pyContext = PythonContext.GetPythonContext(conversion);
  671. CodeContext context = pyContext.SharedContext;
  672. PythonTypeSlot pts;
  673. if (pt.TryResolveSlot(context, "__iter__", out pts)) {
  674. return MakeIterRule(metaUserObject, "CreatePythonEnumerable");
  675. } else if (pt.TryResolveSlot(context, "__getitem__", out pts)) {
  676. return MakeGetItemIterable(metaUserObject, pyContext, pts, "CreateItemEnumerable");
  677. }
  678. return null;
  679. }
  680. internal static DynamicMetaObject ConvertToIEnumerator(DynamicMetaObjectBinder/*!*/ conversion, DynamicMetaObject/*!*/ metaUserObject) {
  681. PythonType pt = MetaPythonObject.GetPythonType(metaUserObject);
  682. PythonContext state = PythonContext.GetPythonContext(conversion);
  683. CodeContext context = state.SharedContext;
  684. PythonTypeSlot pts;
  685. if (pt.TryResolveSlot(context, "__iter__", out pts)) {
  686. ParameterExpression tmp = Ast.Parameter(typeof(object), "iterVal");
  687. return new DynamicMetaObject(
  688. Expression.Block(
  689. new[] { tmp },
  690. Expression.Call(
  691. typeof(PythonOps).GetMethod("CreatePythonEnumerator"),
  692. Ast.Block(
  693. MetaPythonObject.MakeTryGetTypeMember(
  694. state,
  695. pts,
  696. metaUserObject.Expression,
  697. tmp
  698. ),
  699. Ast.Dynamic(
  700. new PythonInvokeBinder(
  701. state,
  702. new CallSignature(0)
  703. ),
  704. typeof(object),
  705. AstUtils.Constant(context),
  706. tmp
  707. )
  708. )
  709. )
  710. ),
  711. metaUserObject.Restrictions
  712. );
  713. } else if (pt.TryResolveSlot(context, "__getitem__", out pts)) {
  714. return MakeGetItemIterable(metaUserObject, state, pts, "CreateItemEnumerator");
  715. }
  716. return null;
  717. }
  718. private static DynamicMetaObject MakeGetItemIterable(DynamicMetaObject metaUserObject, PythonContext state, PythonTypeSlot pts, string method) {
  719. ParameterExpression tmp = Ast.Parameter(typeof(object), "getitemVal");
  720. return new DynamicMetaObject(
  721. Expression.Block(
  722. new[] { tmp },
  723. Expression.Call(
  724. typeof(PythonOps).GetMethod(method),
  725. Ast.Block(
  726. MetaPythonObject.MakeTryGetTypeMember(
  727. state,
  728. pts,
  729. tmp,
  730. metaUserObject.Expression,
  731. Ast.Call(
  732. typeof(DynamicHelpers).GetMethod("GetPythonType"),
  733. AstUtils.Convert(
  734. metaUserObject.Expression,
  735. typeof(object)
  736. )
  737. )
  738. ),
  739. tmp
  740. ),
  741. AstUtils.Constant(
  742. CallSite<Func<CallSite, CodeContext, object, int, object>>.Create(
  743. new PythonInvokeBinder(state, new CallSignature(1))
  744. )
  745. )
  746. )
  747. ),
  748. metaUserObject.Restrictions
  749. );
  750. }
  751. private static DynamicMetaObject/*!*/ MakeIterRule(DynamicMetaObject/*!*/ self, string methodName) {
  752. return new DynamicMetaObject(
  753. Ast.Call(
  754. typeof(PythonOps).GetMethod(methodName),
  755. AstUtils.Convert(self.Expression, typeof(object))
  756. ),
  757. self.Restrictions
  758. );
  759. }
  760. #endregion
  761. public override string ToString() {
  762. return String.Format("Python Convert {0} {1}", Type, ResultKind);
  763. }
  764. #region IExpressionSerializable Members
  765. public Expression CreateExpression() {
  766. return Ast.Call(
  767. typeof(PythonOps).GetMethod("MakeConversionAction"),
  768. BindingHelpers.CreateBinderStateExpression(),
  769. AstUtils.Constant(Type),
  770. AstUtils.Constant(ResultKind)
  771. );
  772. }
  773. #endregion
  774. }
  775. class CompatConversionBinder : ConvertBinder {
  776. private readonly PythonConversionBinder _binder;
  777. public CompatConversionBinder(PythonConversionBinder binder, Type toType, bool isExplicit)
  778. : base(toType, isExplicit) {
  779. _binder = binder;
  780. }
  781. public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, DynamicMetaObject errorSuggestion) {
  782. return _binder.FallbackConvert(ReturnType, target, errorSuggestion);
  783. }
  784. }
  785. }