PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

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

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