PageRenderTime 70ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/Languages/Ruby/IronRuby.Tests/Runtime/DlrInteropTests.cs

http://github.com/IronLanguages/main
C# | 969 lines | 831 code | 101 blank | 37 comment | 35 complexity | c34ebd2516fc0e9a1cb7236b3e9d142d 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. using CsBinder = Microsoft.CSharp.RuntimeBinder.Binder;
  18. using CSharpBinderFlags = Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags;
  19. #else
  20. using Microsoft.Scripting.Ast;
  21. using dynamic = System.Object;
  22. #endif
  23. using System;
  24. using System.Diagnostics;
  25. using System.Dynamic;
  26. using System.Runtime.CompilerServices;
  27. using IronRuby.Builtins;
  28. using Microsoft.Scripting.Hosting;
  29. using Microsoft.Scripting.Math;
  30. using Microsoft.Scripting.Runtime;
  31. using Microsoft.Scripting.Utils;
  32. using System.Collections;
  33. using IronRuby.Runtime;
  34. using System.Collections.Generic;
  35. namespace IronRuby.Tests {
  36. #region Custom binders
  37. class MyInvokeMemberBinder : InvokeMemberBinder {
  38. public MyInvokeMemberBinder(string name, CallInfo callInfo)
  39. : base(name, false, callInfo) {
  40. }
  41. public override DynamicMetaObject FallbackInvokeMember(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) {
  42. return new DynamicMetaObject(
  43. Expression.Constant("FallbackInvokeMember"),
  44. target.Restrictions.Merge(BindingRestrictions.Combine(args))
  45. );
  46. }
  47. public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) {
  48. return new DynamicMetaObject(
  49. Expression.Dynamic(new MyInvokeBinder(CallInfo), typeof(object), DynamicUtils.GetExpressions(ArrayUtils.Insert(target, args))),
  50. target.Restrictions.Merge(BindingRestrictions.Combine(args))
  51. );
  52. }
  53. internal static object Invoke(object obj, string methodName) {
  54. var site = CallSite<Func<CallSite, object, object>>.Create(new MyInvokeMemberBinder(methodName, new CallInfo(0)));
  55. return site.Target(site, obj);
  56. }
  57. internal static object Invoke(object obj, string methodName, object arg) {
  58. var site = CallSite<Func<CallSite, object, object, object>>.Create(new MyInvokeMemberBinder(methodName, new CallInfo(1)));
  59. return site.Target(site, obj, arg);
  60. }
  61. }
  62. class MyInvokeBinder : InvokeBinder {
  63. public MyInvokeBinder(CallInfo callInfo)
  64. : base(callInfo) {
  65. }
  66. public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) {
  67. return new DynamicMetaObject(
  68. Expression.Call(
  69. typeof(String).GetMethod("Concat", new Type[] { typeof(object), typeof(object) }),
  70. Expression.Constant("FallbackInvoke"),
  71. target.Expression
  72. ),
  73. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target)
  74. );
  75. }
  76. internal static object Invoke(object obj) {
  77. var site = CallSite<Func<CallSite, object, object>>.Create(new MyInvokeBinder(new CallInfo(0)));
  78. return site.Target(site, obj);
  79. }
  80. internal static object Invoke(object obj, object arg) {
  81. var site = CallSite<Func<CallSite, object, object, object>>.Create(new MyInvokeBinder(new CallInfo(1)));
  82. return site.Target(site, obj, arg);
  83. }
  84. }
  85. class MyGetIndexBinder : GetIndexBinder {
  86. public MyGetIndexBinder(CallInfo args)
  87. : base(args) {
  88. }
  89. public override DynamicMetaObject FallbackGetIndex(DynamicMetaObject target, DynamicMetaObject[] indexes, DynamicMetaObject errorSuggestion) {
  90. return new DynamicMetaObject(
  91. Expression.Call(
  92. typeof(String).GetMethod("Concat", new Type[] { typeof(object), typeof(object) }),
  93. Expression.Constant("FallbackGetIndex:"),
  94. indexes[0].Expression
  95. ),
  96. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target)
  97. );
  98. }
  99. internal static object Invoke(object obj, object index) {
  100. var site = CallSite<Func<CallSite, object, object, object>>.Create(new MyGetIndexBinder(new CallInfo(1)));
  101. return site.Target(site, obj, index);
  102. }
  103. }
  104. class MySetIndexBinder : SetIndexBinder {
  105. public MySetIndexBinder(CallInfo args)
  106. : base(args) {
  107. }
  108. public override DynamicMetaObject FallbackSetIndex(DynamicMetaObject target, DynamicMetaObject[] indexes, DynamicMetaObject value, DynamicMetaObject errorSuggestion) {
  109. return new DynamicMetaObject(
  110. Expression.Call(
  111. typeof(String).GetMethod("Concat", new Type[] { typeof(object), typeof(object), typeof(object) }),
  112. Expression.Constant("FallbackSetIndex:"),
  113. indexes[0].Expression,
  114. value.Expression
  115. ),
  116. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target)
  117. );
  118. }
  119. internal static object Invoke(object obj, object index, object arg) {
  120. var site = CallSite<Func<CallSite, object, object, object, object>>.Create(new MySetIndexBinder(new CallInfo(1)));
  121. return site.Target(site, obj, index, arg);
  122. }
  123. }
  124. class MyGetMemberBinder : GetMemberBinder {
  125. public MyGetMemberBinder(string name)
  126. : base(name, false) {
  127. }
  128. public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) {
  129. return new DynamicMetaObject(
  130. Expression.Constant("FallbackGetMember"),
  131. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target)
  132. );
  133. }
  134. internal static object Invoke(object obj, string memberName) {
  135. var site = CallSite<Func<CallSite, object, object>>.Create(new MyGetMemberBinder(memberName));
  136. return site.Target(site, obj);
  137. }
  138. }
  139. class MySetMemberBinder : SetMemberBinder {
  140. public MySetMemberBinder(string name)
  141. : base(name, false) {
  142. }
  143. public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion) {
  144. return new DynamicMetaObject(
  145. Expression.Constant("FallbackSetMember"),
  146. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target)
  147. );
  148. }
  149. internal static object Invoke(object obj, string memberName, object val) {
  150. var site = CallSite<Func<CallSite, object, object, object>>.Create(new MySetMemberBinder(memberName));
  151. return site.Target(site, obj, val);
  152. }
  153. }
  154. class MyInvokeBinder2 : InvokeBinder {
  155. public MyInvokeBinder2(CallInfo args)
  156. : base(args) {
  157. }
  158. public override DynamicMetaObject FallbackInvoke(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) {
  159. Expression[] exprs = new Expression[args.Length + 1];
  160. exprs[0] = Expression.Constant("FallbackInvoke");
  161. for (int i = 0; i < args.Length; i++) {
  162. exprs[i + 1] = args[i].Expression;
  163. }
  164. return new DynamicMetaObject(
  165. Expression.Call(
  166. typeof(String).GetMethod("Concat", new Type[] { typeof(object[]) }),
  167. Expression.NewArrayInit(
  168. typeof(object),
  169. exprs
  170. )
  171. ),
  172. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target)
  173. );
  174. }
  175. internal static object Invoke(object obj, object arg) {
  176. var site = CallSite<Func<CallSite, object, object, object>>.Create(new MyInvokeBinder2(new CallInfo(1)));
  177. return site.Target(site, obj, arg);
  178. }
  179. }
  180. class MyConvertBinder : ConvertBinder {
  181. private object _result;
  182. public MyConvertBinder(Type type, object result)
  183. : base(type, true) {
  184. _result = result;
  185. }
  186. public override DynamicMetaObject FallbackConvert(DynamicMetaObject target, DynamicMetaObject errorSuggestion) {
  187. return new DynamicMetaObject(
  188. Expression.Constant(_result, ReturnType),
  189. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target)
  190. );
  191. }
  192. internal static T Convert<T>(object obj, T fallbackResult) {
  193. var site = CallSite<Func<CallSite, object, T>>.Create(new MyConvertBinder(typeof(T), fallbackResult));
  194. return site.Target(site, obj);
  195. }
  196. }
  197. class MyBinaryOperationBinder : BinaryOperationBinder {
  198. public MyBinaryOperationBinder(ExpressionType operation)
  199. : base(operation) {
  200. }
  201. public override DynamicMetaObject FallbackBinaryOperation(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion) {
  202. return new DynamicMetaObject(
  203. Expression.Call(
  204. typeof(String).GetMethod("Concat", new Type[] { typeof(object), typeof(object) }),
  205. Expression.Constant("FallbackInvoke:"),
  206. arg.Expression
  207. ),
  208. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target)
  209. );
  210. }
  211. internal static object Invoke(ExpressionType operation, object obj, object arg) {
  212. var site = CallSite<Func<CallSite, object, object, object>>.Create(new MyBinaryOperationBinder(operation));
  213. return site.Target(site, obj, arg);
  214. }
  215. }
  216. class MyUnaryOperationBinder : UnaryOperationBinder {
  217. public MyUnaryOperationBinder(ExpressionType operation)
  218. : base(operation) {
  219. }
  220. public override DynamicMetaObject FallbackUnaryOperation(DynamicMetaObject target, DynamicMetaObject errorSuggestion) {
  221. return new DynamicMetaObject(
  222. Expression.Constant("FallbackInvoke"),
  223. BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target)
  224. );
  225. }
  226. internal static object Invoke(ExpressionType operation, object obj) {
  227. var site = CallSite<Func<CallSite, object, object>>.Create(new MyUnaryOperationBinder(operation));
  228. return site.Target(site, obj);
  229. }
  230. }
  231. #endregion
  232. public partial class Tests {
  233. #region Ruby snippet
  234. const string RubySnippet = @"
  235. ArrayList = System::Collections::ArrayList
  236. #------------------------------------------------------------------------------
  237. # Mixin which allows responding to arbitrary methods, properties, and indexing
  238. module DynamicAttributes
  239. def initialize *args
  240. @attrs = Hash.new
  241. @elems = Hash.new
  242. super
  243. end
  244. def explicit_attribute
  245. 'explicit_attribute'.to_clr_string
  246. end
  247. def method_missing *args
  248. case args.size
  249. when 1
  250. attr_name = args[0].to_s
  251. if @attrs.key? attr_name
  252. @attrs[attr_name]
  253. else
  254. ('dynamic_' + attr_name).to_clr_string
  255. end
  256. when 2
  257. if args[0] == :[] then
  258. if @elems.key? args[1] then
  259. @elems[args[1]]
  260. else
  261. ('dynamic_element_' + args[1].to_s).to_clr_string
  262. end
  263. else
  264. attr_name = args[0].to_s[0..-2] # Strip the trailing '='
  265. @attrs[attr_name] = args[1]
  266. end
  267. when 3
  268. # args[0] will be :[]=
  269. @elems[args[1]] = args[2]
  270. end
  271. end
  272. end
  273. #------------------------------------------------------------------------------
  274. # If the file is run from the command-line as 'ir.exe test.rb',
  275. # some extra initialization needs to be done so that the rest of the code
  276. # works both from the command-line and in a hosted ScriptRuntime.
  277. if $0 == __FILE__ then
  278. # Mimic DynamicAttributes#initialize
  279. self.instance_variable_set(:@attrs, Hash.new)
  280. self.instance_variable_set(:@elems, Hash.new)
  281. class << self
  282. include DynamicAttributes
  283. end
  284. end
  285. #------------------------------------------------------------------------------
  286. # Inherit from a CLR type
  287. class RubyArrayList < ArrayList
  288. def initialize *args
  289. super(*args)
  290. end
  291. def ruby_method
  292. 'Hi from Ruby'.to_clr_string
  293. end
  294. attr_accessor :ruby_attribute
  295. # override a CLR virtual method
  296. def IndexOf obj
  297. 123456789
  298. end
  299. end
  300. self.ruby_array_list = RubyArrayList.new()
  301. self.ruby_array_list.Add(100)
  302. self.ruby_array_list.Add(200)
  303. #------------------------------------------------------------------------------
  304. class DynamicObject
  305. include DynamicAttributes
  306. end
  307. self.dynamic_object = DynamicObject.new
  308. #------------------------------------------------------------------------------
  309. class DynamicArrayList < RubyArrayList
  310. include DynamicAttributes
  311. end
  312. self.dynamic_array_list = DynamicArrayList.new
  313. #------------------------------------------------------------------------------
  314. class Miscellaneous
  315. def self.static_method
  316. 'static_method'.to_clr_string
  317. end
  318. #self.class_instance_method = static_method
  319. attr :ruby_callable_called
  320. def get_a_ruby_callable
  321. @ruby_callable_called = false
  322. proc { @ruby_callable_called = true }
  323. end
  324. def to_s
  325. 'to_s'
  326. end
  327. end
  328. self.misc = Miscellaneous.new
  329. #------------------------------------------------------------------------------
  330. class Indexable
  331. def initialize a=nil
  332. if a then
  333. @array = a
  334. else
  335. @array = []
  336. end
  337. end
  338. def [](index)
  339. @array[index]
  340. end
  341. def []=(index, value)
  342. @array[index] = value
  343. end
  344. end
  345. self.indexable = Indexable.new [0, 1, 2]
  346. #------------------------------------------------------------------------------
  347. class Methods
  348. def self.named_params(a, b)
  349. %Q(a:#{a} b:#{b}).to_clr_string
  350. end
  351. def self.default_values(a = 1, b = 2)
  352. %Q(a:#{a} b:#{b}).to_clr_string
  353. end
  354. def self.varargs(*args)
  355. args.collect {|elem| elem.to_s }.join(' ').to_clr_string
  356. end
  357. def self.multiple_return_values
  358. return 100, 200
  359. end
  360. def self.with_block
  361. yield 100
  362. end
  363. end
  364. #------------------------------------------------------------------------------
  365. class SanityTest
  366. def self.assert_equal o1, o2
  367. if not o1 == o2 then raise %Q(Fail: Expected #{o1} to equal #{o2}) end
  368. end
  369. def self.assert_error l, exception_type
  370. begin
  371. l.call
  372. raise 'Unreachable'
  373. rescue Exception => e
  374. if not e.kind_of? exception_type then raise %Q(Expected #{e.class} to equal #{exception_type}) end
  375. end
  376. end
  377. def self.sanity_test main
  378. # $ruby_array_list
  379. assert_equal main.ruby_array_list.Count, 2
  380. main.ruby_array_list[0]
  381. assert_equal main.ruby_array_list.ruby_method, 'Hi from Ruby'.to_clr_string
  382. assert_equal main.ruby_array_list.IndexOf(nil), 123456789
  383. # main.dynamic_object
  384. assert_equal main.dynamic_object.foo, 'dynamic_foo'.to_clr_string
  385. main.dynamic_object.bar = 'my bar'
  386. assert_equal main.dynamic_object.bar, 'my bar'
  387. assert_equal main.dynamic_object.explicit_attribute, 'explicit_attribute'.to_clr_string
  388. assert_equal main.dynamic_object[:hello], 'dynamic_element_hello'.to_clr_string
  389. main.dynamic_object[:hello] = 1
  390. assert_equal main.dynamic_object[:hello], 1
  391. # main.dynamic_array_list
  392. assert_equal main.dynamic_array_list.foo, 'dynamic_foo'.to_clr_string
  393. main.dynamic_array_list.bar = 'my bar'
  394. assert_equal main.dynamic_array_list.bar, 'my bar'
  395. assert_equal main.dynamic_array_list.explicit_attribute, 'explicit_attribute'.to_clr_string
  396. main.dynamic_array_list.Count = 1
  397. assert_equal main.dynamic_array_list.Count, 0
  398. assert_equal main.dynamic_array_list.IndexOf(0), 123456789
  399. # main.misc
  400. assert_equal Miscellaneous.static_method, 'static_method'.to_clr_string
  401. assert_error lambda { main.misc.static_method }, NoMethodError
  402. c = main.misc.get_a_ruby_callable()
  403. assert_equal main.misc.ruby_callable_called, false
  404. c.call(nil)
  405. assert_equal main.misc.ruby_callable_called, true
  406. assert_equal main.misc.ToString(), 'to_s'
  407. # main.indexable
  408. assert_equal main.indexable[2], 2
  409. main.indexable[10] = 100
  410. assert_equal main.indexable[10], 100
  411. assert_equal main.indexable[9], nil
  412. # Methods
  413. assert_equal Methods.default_values(100), 'a:100 b:2'.to_clr_string
  414. assert_equal Methods.varargs(100, 200), '100 200'.to_clr_string
  415. assert_equal Methods.multiple_return_values, [100, 200]
  416. assert_equal Methods.with_block {|x| x + 1000}, 1100
  417. # Features to try from other languages
  418. # Pass in ref/out params
  419. # Named arguments
  420. end
  421. end
  422. if $0 == __FILE__ then
  423. SanityTest.sanity_test self
  424. end
  425. ";
  426. #endregion
  427. private ScriptScope CreateInteropScope() {
  428. var scope = Runtime.CreateScope();
  429. Engine.Execute(RubySnippet, scope);
  430. return scope;
  431. }
  432. public void Dlr_RubySnippet() {
  433. var scope = CreateInteropScope();
  434. Engine.Execute("SanityTest.sanity_test self", scope);
  435. }
  436. public void Dlr_ClrSubtype() {
  437. var scope = CreateInteropScope();
  438. object ruby_array_list = scope.GetVariable("ruby_array_list");
  439. // CLR properties are accessible as methods
  440. AreEqual(MyInvokeMemberBinder.Invoke(ruby_array_list, "Count"), "FallbackInvokeMember");
  441. // CLR properties are accessible as members
  442. AreEqual(MyGetMemberBinder.Invoke(ruby_array_list, "Count"), "FallbackGetMember");
  443. // Overriden CLR member
  444. AreEqual(MyInvokeMemberBinder.Invoke(ruby_array_list, "IndexOf", null), 123456789);
  445. // CLR indexer
  446. AreEqual(MySetIndexBinder.Invoke(ruby_array_list, 10, 100), "FallbackSetIndex:10100");
  447. AreEqual(MyGetIndexBinder.Invoke(ruby_array_list, 10), "FallbackGetIndex:10");
  448. AreEqual(MyInvokeMemberBinder.Invoke(ruby_array_list, "ruby_method"), "Hi from Ruby");
  449. // CLR properties accessed with Ruby name.
  450. AreEqual(MyInvokeMemberBinder.Invoke(ruby_array_list, "count"), "FallbackInvokeMember");
  451. // CLR methods accessed with Ruby name.
  452. AreEqual(MyInvokeMemberBinder.Invoke(ruby_array_list, "index_of", null), "FallbackInvokeMember");
  453. AreEqual(MyInvokeMemberBinder.Invoke(ruby_array_list, "non_existent"), "FallbackInvokeMember");
  454. AreEqual(MySetMemberBinder.Invoke(ruby_array_list, "Count", 100000), "FallbackSetMember");
  455. // Ruby attributes are invoked directly via SetMember/GetMember:
  456. AreEqual(MySetMemberBinder.Invoke(ruby_array_list, "ruby_attribute", 123), 123);
  457. AreEqual(MyGetMemberBinder.Invoke(ruby_array_list, "ruby_attribute"), 123);
  458. #if !CLR2
  459. List<object> result = new List<object>();
  460. foreach (object item in (dynamic)ruby_array_list) {
  461. result.Add(item);
  462. }
  463. Assert(result.Count == 2 && (int)result[0] == 100 && (int)result[1] == 200);
  464. #endif
  465. }
  466. public void Dlr_MethodMissing() {
  467. var scope = CreateInteropScope();
  468. object dynamic_object = scope.GetVariable("dynamic_object");
  469. AreEqual(MyInvokeMemberBinder.Invoke(dynamic_object, "non_existent_method"), "dynamic_non_existent_method");
  470. AreEqual(MySetMemberBinder.Invoke(dynamic_object, "non_existent_member", 100), 100);
  471. // Ruby doesn't have "mising_property" so we get a method, not the value:
  472. AreEqual(MyInvokeBinder.Invoke(MyGetMemberBinder.Invoke(dynamic_object, "non_existent_member")), 100);
  473. AreEqual(MyGetIndexBinder.Invoke(dynamic_object, "non_existent_index"), "dynamic_element_non_existent_index");
  474. AreEqual(MySetIndexBinder.Invoke(dynamic_object, "non_existent_index", 100), 100);
  475. AreEqual(MyGetIndexBinder.Invoke(dynamic_object, "non_existent_index"), 100);
  476. AreEqual(MyInvokeMemberBinder.Invoke(dynamic_object, "explicit_attribute"), "explicit_attribute");
  477. }
  478. public void Dlr_Miscellaneous() {
  479. var scope = CreateInteropScope();
  480. object misc_object = scope.GetVariable("misc");
  481. object misc_class = MyInvokeMemberBinder.Invoke(misc_object, "class");
  482. AreEqual(Engine.Runtime.Globals.GetVariable<object>("Miscellaneous"), misc_class);
  483. // singleton methods are only invokable on the class object, not the instance:
  484. AreEqual(MyInvokeMemberBinder.Invoke(misc_class, "static_method"), "static_method");
  485. AreEqual(MyInvokeMemberBinder.Invoke(misc_object, "static_method"), "FallbackInvokeMember");
  486. object callable = MyInvokeMemberBinder.Invoke(misc_object, "get_a_ruby_callable");
  487. AreEqual(MyInvokeMemberBinder.Invoke(misc_object, "ruby_callable_called"), false);
  488. MyInvokeBinder.Invoke(callable);
  489. AreEqual(MyInvokeMemberBinder.Invoke(misc_object, "ruby_callable_called"), true);
  490. // "ToString" is not handled in any special way by Ruby binder.
  491. // The call falls back to the caller's binder that should then call .NET ToString method.
  492. // ToString is overridden by all Ruby objects to call to_s.
  493. AreEqual(MyInvokeMemberBinder.Invoke(misc_class, "ToString"), "FallbackInvokeMember");
  494. }
  495. public void Dlr_Conversions1() {
  496. Engine.Execute(@"
  497. class C
  498. def to_int
  499. 1
  500. end
  501. def to_f
  502. 2.0
  503. end
  504. def to_str
  505. 'str'
  506. end
  507. end
  508. ");
  509. object classC = Runtime.Globals.GetVariable("C");
  510. object c = Engine.Operations.CreateInstance(classC);
  511. AreEqual(MyConvertBinder.Convert<sbyte>(c, 10), (sbyte)1);
  512. AreEqual(MyConvertBinder.Convert<byte>(c, 10), (byte)1);
  513. AreEqual(MyConvertBinder.Convert<short>(c, 10), (short)1);
  514. AreEqual(MyConvertBinder.Convert<ushort>(c, 10), (ushort)1);
  515. AreEqual(MyConvertBinder.Convert<int>(c, 10), 1);
  516. AreEqual(MyConvertBinder.Convert<uint>(c, 10), (uint)1);
  517. AreEqual(MyConvertBinder.Convert<long>(c, 10), (long)1);
  518. AreEqual(MyConvertBinder.Convert<ulong>(c, 10), (ulong)1);
  519. AreEqual(MyConvertBinder.Convert<BigInteger>(c, 10), (BigInteger)1);
  520. AreEqual(MyConvertBinder.Convert<double>(c, -8.0), 2.0);
  521. AreEqual(MyConvertBinder.Convert<float>(c, -8.0f), 2.0f);
  522. AreEqual(MyConvertBinder.Convert<string>(c, "FallbackConvert"), "str");
  523. }
  524. public class DynamicList : IDynamicMetaObjectProvider {
  525. public DynamicMetaObject/*!*/ GetMetaObject(Expression/*!*/ parameter) {
  526. return new Meta(parameter, BindingRestrictions.Empty, this);
  527. }
  528. public class Meta : DynamicMetaObject {
  529. internal Meta(Expression/*!*/ expression, BindingRestrictions/*!*/ restrictions, object/*!*/ value)
  530. : base(expression, restrictions, value) {
  531. }
  532. public override DynamicMetaObject/*!*/ BindConvert(ConvertBinder/*!*/ binder) {
  533. if (binder.Type != typeof(IList)) {
  534. return binder.FallbackConvert(this);
  535. }
  536. return new DynamicMetaObject(
  537. Expression.Constant(new object[] { 1, 2, 3}),
  538. BindingRestrictions.GetTypeRestriction(Expression, Value.GetType())
  539. );
  540. }
  541. }
  542. }
  543. public void Dlr_Splatting1() {
  544. var scope = Engine.CreateScope();
  545. scope.SetVariable("x", new DynamicList());
  546. var a = Engine.Execute<RubyArray>(@"a,b,c = *x; [a,b,c]", scope);
  547. Assert((int)a[0] == 1);
  548. Assert((int)a[1] == 2);
  549. Assert((int)a[2] == 3);
  550. a = Engine.Execute<RubyArray>(@"a,b,c = x; [a,b,c]", scope);
  551. Assert((int)a[0] == 1);
  552. Assert((int)a[1] == 2);
  553. Assert((int)a[2] == 3);
  554. var expando = new ExpandoObject();
  555. scope.SetVariable("x", expando);
  556. a = Engine.Execute<RubyArray>(@"a,b = *x; [a,b]", scope);
  557. Assert(a[0] == expando);
  558. Assert(a[1] == null);
  559. a = Engine.Execute<RubyArray>(@"a,b = x; [a,b]", scope);
  560. Assert(a[0] == expando);
  561. Assert(a[1] == null);
  562. }
  563. public void Dlr_Indexable() {
  564. var scope = CreateInteropScope();
  565. object indexable = scope.GetVariable("indexable");
  566. AreEqual(MyGetIndexBinder.Invoke(indexable, 2), 2);
  567. AreEqual(MySetIndexBinder.Invoke(indexable, 10, 100), 100);
  568. AreEqual(MyGetIndexBinder.Invoke(indexable, 10), 100);
  569. AreEqual(MyGetIndexBinder.Invoke(indexable, 9), null);
  570. }
  571. public void Dlr_Number() {
  572. object one_hundred = Engine.Execute(@"
  573. class Number
  574. def initialize(v)
  575. @val = v
  576. end
  577. def +(other)
  578. @val + other
  579. end
  580. def -(other)
  581. @val - other
  582. end
  583. def *(other)
  584. @val * other
  585. end
  586. def /(other)
  587. @val / other
  588. end
  589. def -@
  590. -@val
  591. end
  592. def ~
  593. ~@val
  594. end
  595. end
  596. Number.new(100)
  597. ");
  598. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.Add, one_hundred, 1), 100 + 1);
  599. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.Subtract, one_hundred, 1), 100 - 1);
  600. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.Multiply, one_hundred, 2), 2 * 100);
  601. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.Divide, one_hundred, 2), 100/2);
  602. AreEqual(MyUnaryOperationBinder.Invoke(ExpressionType.Negate, one_hundred), -100);
  603. AreEqual(MyUnaryOperationBinder.Invoke(ExpressionType.OnesComplement, one_hundred), ~100);
  604. AreEqual(MyUnaryOperationBinder.Invoke(ExpressionType.Increment, one_hundred), 100 + 1);
  605. AreEqual(MyUnaryOperationBinder.Invoke(ExpressionType.Decrement, one_hundred), 100 - 1);
  606. #if !CLR2
  607. dynamic number = one_hundred;
  608. number--;
  609. Assert(number == 99);
  610. number++;
  611. Assert(number == 100);
  612. #endif
  613. }
  614. public void Dlr_Comparable() {
  615. object comparable = Engine.Execute(@"
  616. class RubyComparable
  617. include Comparable
  618. def initialize val
  619. @val = val
  620. end
  621. def <=>(other)
  622. @val <=> other
  623. end
  624. end
  625. RubyComparable.new(100)
  626. ");
  627. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.Equal, comparable, 100), true);
  628. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.GreaterThan, comparable, 100), false);
  629. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.LessThanOrEqual, comparable, 100), true);
  630. }
  631. public void Dlr_Equatable() {
  632. object equatable = Engine.Execute(@"
  633. class RubyEquatable
  634. def initialize val
  635. @val = val
  636. end
  637. def ==(other)
  638. @val == other
  639. end
  640. end
  641. RubyEquatable.new(100)
  642. ");
  643. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.Equal, equatable, 100), true);
  644. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.Equal, equatable, 101), false);
  645. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.NotEqual, equatable, 100), false);
  646. AreEqual(MyBinaryOperationBinder.Invoke(ExpressionType.NotEqual, equatable, 101), true);
  647. #if CLR4
  648. dynamic dynamicEquatable = equatable;
  649. Assert((bool)(dynamicEquatable == 100));
  650. Assert(!(bool)(dynamicEquatable == 101));
  651. Assert(!(bool)(dynamicEquatable != 100));
  652. Assert((bool)(dynamicEquatable != 101));
  653. #endif
  654. }
  655. public void Dlr_RubyObjects() {
  656. var scope = Engine.CreateScope();
  657. Engine.Execute(@"
  658. scope.ruby_array = [100, 200]
  659. scope.ruby_hash = { 1 => 3, 2 => 4 }
  660. def inc(a)
  661. a + 1
  662. end
  663. scope.ruby_method = method(:inc)
  664. scope.ruby_proc = proc { |a| a + 2 }
  665. ", scope);
  666. #if !CLR2
  667. dynamic s = scope;
  668. AreEqual(s.ruby_array[0], 100);
  669. AreEqual(s.ruby_hash[1], 3);
  670. AreEqual(s.ruby_method(1), 2);
  671. AreEqual(s.ruby_proc(1), 3);
  672. #endif
  673. object method = scope.GetVariable("ruby_method");
  674. AreEqual(MyInvokeBinder.Invoke(method, 1), 2);
  675. object proc = scope.GetVariable("ruby_proc");
  676. AreEqual(MyInvokeBinder.Invoke(proc, 1), 3);
  677. }
  678. public void Dlr_Methods() {
  679. //assert_equal Methods.default_values(100), 'a:100 b:2'
  680. //assert_equal Methods.varargs(100, 200), '100 200'
  681. //assert_equal Methods.multiple_return_values, [100, 200]
  682. //assert_equal Methods.with_block {|x| x + 1000}, 1100
  683. }
  684. public void Dlr_Visibility() {
  685. Engine.Execute(@"
  686. class D < Hash
  687. end
  688. class C
  689. def public_m
  690. 0
  691. end
  692. private
  693. def private_m
  694. 1
  695. end
  696. protected
  697. def protected_m
  698. 2
  699. end
  700. end
  701. ");
  702. var classC = Runtime.Globals.GetVariable("C");
  703. // TODO: CLR4 bug #772803 - c can't be dynamic:
  704. object c = Engine.Operations.CreateInstance(classC);
  705. AssertExceptionThrown<MissingMethodException>(() => MyInvokeMemberBinder.Invoke(c, "private_m"));
  706. AssertExceptionThrown<MissingMethodException>(() => MyInvokeMemberBinder.Invoke(c, "protected_m"));
  707. var r1 = MyInvokeMemberBinder.Invoke(c, "public_m");
  708. Assert(r1 is int && (int)r1 == 0);
  709. Engine.Execute(@"
  710. class C
  711. def method_missing name
  712. 3
  713. end
  714. end");
  715. var r2 = MyInvokeMemberBinder.Invoke(c, "private_m");
  716. Assert(r2 is int && (int)r2 == 3);
  717. // private initialize method can be called if called via new:
  718. var classD = Runtime.Globals.GetVariable("D");
  719. var d = Engine.Operations.CreateInstance(classD);
  720. Assert(d is Hash);
  721. }
  722. public void Dlr_Languages() {
  723. //# Pass in ref/out params
  724. //# Named arguments
  725. }
  726. public class DynamicObject1 : DynamicObject {
  727. public string Test() {
  728. return "Hello, Test";
  729. }
  730. public override IEnumerable<string> GetDynamicMemberNames() {
  731. return new string[] { "Test2", "Test3" };
  732. }
  733. public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
  734. result = "Invoke Member " + binder.Name;
  735. return true;
  736. }
  737. public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) {
  738. result = "Invoke";
  739. return true;
  740. }
  741. }
  742. public void Dlr_DynamicObject1() {
  743. Context.ObjectClass.SetConstant("C", new DynamicObject1());
  744. TestOutput(@"
  745. p C.Test2
  746. p C.call
  747. p C.methods(false)
  748. ", @"
  749. 'Invoke Member Test2'
  750. 'Invoke'
  751. ['test2', 'test3']
  752. ");
  753. }
  754. public class DynamicObject2 : DynamicObject {
  755. public override bool TryInvokeMember(InvokeMemberBinder binder, dynamic[] args, out dynamic result) {
  756. if (binder.Name == "Foo") {
  757. result = 1;
  758. return true;
  759. } else {
  760. result = null;
  761. return false;
  762. }
  763. }
  764. public override bool TryGetMember(GetMemberBinder binder, out dynamic result) {
  765. if (binder.Name == "Bar") {
  766. result = 2;
  767. return true;
  768. } else {
  769. result = null;
  770. return false;
  771. }
  772. }
  773. }
  774. public void Dlr_DynamicObject2() {
  775. Context.ObjectClass.SetConstant("C", new DynamicObject2());
  776. TestOutput(@"
  777. p C.foo
  778. p C.Foo
  779. p C.bar
  780. p C.Bar
  781. C.xxx rescue puts $!.to_s[0, 22]
  782. ", @"
  783. 1
  784. 1
  785. 2
  786. 2
  787. undefined method `xxx'
  788. ");
  789. }
  790. public class DynamicObject3 : DynamicObject {
  791. public List<string> Lookups = new List<string>();
  792. public override bool TryGetMember(GetMemberBinder binder, out object result) {
  793. Lookups.Add(binder.Name);
  794. result = null;
  795. return false;
  796. }
  797. }
  798. /// <summary>
  799. /// Scope variable lookup uses InteropBinder.TryGetMemberExact which should use errorSuggestion correctly.
  800. /// </summary>
  801. public void Dlr_DynamicObject3() {
  802. var obj = new DynamicObject3();
  803. var s = Engine.CreateScope(obj);
  804. Assert(Engine.Execute<int>("foo_bar rescue 123", s) == 123);
  805. Assert(obj.Lookups.ToArray().ValueEquals(new[] { "foo_bar", "FooBar" }));
  806. }
  807. }
  808. }