InteropExplicitTypeTests.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. namespace Jint.Tests.Runtime;
  2. using Jint.Native;
  3. using Jint.Runtime.Interop;
  4. public class InteropExplicitTypeTests
  5. {
  6. public interface I1
  7. {
  8. string Name { get; }
  9. }
  10. public class Super
  11. {
  12. public string Name { get; } = "Super";
  13. }
  14. public class CI1 : Super, I1
  15. {
  16. public new string Name { get; } = "CI1";
  17. string I1.Name { get; } = "CI1 as I1";
  18. }
  19. public class Indexer<T>
  20. {
  21. private readonly T t;
  22. public Indexer(T t)
  23. {
  24. this.t = t;
  25. }
  26. public T this[int index]
  27. {
  28. get { return t; }
  29. }
  30. }
  31. public class InterfaceHolder
  32. {
  33. public InterfaceHolder()
  34. {
  35. var ci1 = new CI1();
  36. this.ci1 = ci1;
  37. this.i1 = ci1;
  38. this.super = ci1;
  39. this.IndexerCI1 = new Indexer<CI1>(ci1);
  40. this.IndexerI1 = new Indexer<I1>(ci1);
  41. this.IndexerSuper = new Indexer<Super>(ci1);
  42. }
  43. public readonly CI1 ci1;
  44. public readonly I1 i1;
  45. public readonly Super super;
  46. public CI1 CI1 { get => ci1; }
  47. public I1 I1 { get => i1; }
  48. public Super Super { get => super; }
  49. public CI1 GetCI1() => ci1;
  50. public I1 GetI1() => i1;
  51. public Super GetSuper() => super;
  52. public Indexer<CI1> IndexerCI1 { get; }
  53. public Indexer<I1> IndexerI1 { get; }
  54. public Indexer<Super> IndexerSuper { get; }
  55. }
  56. private readonly Engine _engine;
  57. private readonly InterfaceHolder holder;
  58. public InteropExplicitTypeTests()
  59. {
  60. holder = new InterfaceHolder();
  61. _engine = new Engine(cfg => cfg.AllowClr(
  62. typeof(CI1).Assembly,
  63. typeof(Console).Assembly,
  64. typeof(File).Assembly))
  65. .SetValue("log", new Action<object>(Console.WriteLine))
  66. .SetValue("assert", new Action<bool>(Assert.True))
  67. .SetValue("equal", new Action<object, object>(Assert.Equal))
  68. .SetValue("holder", holder)
  69. ;
  70. }
  71. [Fact]
  72. public void EqualTest()
  73. {
  74. Assert.Equal(_engine.Evaluate("holder.I1"), _engine.Evaluate("holder.i1"));
  75. Assert.Equal(_engine.Evaluate("holder.Super"), _engine.Evaluate("holder.super"));
  76. }
  77. [Fact]
  78. public void ExplicitInterfaceFromField()
  79. {
  80. Assert.Equal(holder.i1.Name, _engine.Evaluate("holder.i1.Name"));
  81. Assert.NotEqual(holder.i1.Name, _engine.Evaluate("holder.ci1.Name"));
  82. }
  83. [Fact]
  84. public void ExplicitInterfaceFromProperty()
  85. {
  86. Assert.Equal(holder.I1.Name, _engine.Evaluate("holder.I1.Name"));
  87. Assert.NotEqual(holder.I1.Name, _engine.Evaluate("holder.CI1.Name"));
  88. }
  89. [Fact]
  90. public void ExplicitInterfaceFromMethod()
  91. {
  92. Assert.Equal(holder.GetI1().Name, _engine.Evaluate("holder.GetI1().Name"));
  93. Assert.NotEqual(holder.GetI1().Name, _engine.Evaluate("holder.GetCI1().Name"));
  94. }
  95. [Fact]
  96. public void ExplicitInterfaceFromIndexer()
  97. {
  98. Assert.Equal(holder.IndexerI1[0].Name, _engine.Evaluate("holder.IndexerI1[0].Name"));
  99. }
  100. [Fact]
  101. public void SuperClassFromField()
  102. {
  103. Assert.Equal(holder.super.Name, _engine.Evaluate("holder.super.Name"));
  104. Assert.NotEqual(holder.super.Name, _engine.Evaluate("holder.ci1.Name"));
  105. }
  106. [Fact]
  107. public void SuperClassFromProperty()
  108. {
  109. Assert.Equal(holder.Super.Name, _engine.Evaluate("holder.Super.Name"));
  110. Assert.NotEqual(holder.Super.Name, _engine.Evaluate("holder.CI1.Name"));
  111. }
  112. [Fact]
  113. public void SuperClassFromMethod()
  114. {
  115. Assert.Equal(holder.GetSuper().Name, _engine.Evaluate("holder.GetSuper().Name"));
  116. Assert.NotEqual(holder.GetSuper().Name, _engine.Evaluate("holder.GetCI1().Name"));
  117. }
  118. [Fact]
  119. public void SuperClassFromIndexer()
  120. {
  121. Assert.Equal(holder.IndexerSuper[0].Name, _engine.Evaluate("holder.IndexerSuper[0].Name"));
  122. }
  123. public struct NullabeStruct : I1
  124. {
  125. public NullabeStruct()
  126. {
  127. }
  128. public string name = "NullabeStruct";
  129. public string Name => name;
  130. string I1.Name => "NullabeStruct as I1";
  131. }
  132. public class NullableHolder
  133. {
  134. public I1 I1 { get; set; }
  135. public NullabeStruct? NullabeStruct { get; set; }
  136. }
  137. [Fact]
  138. public void TypedObjectWrapperForNullableType()
  139. {
  140. var nullableHolder = new NullableHolder();
  141. _engine.SetValue("nullableHolder", nullableHolder);
  142. _engine.SetValue("nullabeStruct", new NullabeStruct());
  143. Assert.Equal(_engine.Evaluate("nullableHolder.NullabeStruct"), JsValue.Null);
  144. _engine.Evaluate("nullableHolder.NullabeStruct = nullabeStruct");
  145. Assert.Equal(_engine.Evaluate("nullableHolder.NullabeStruct.Name"), nullableHolder.NullabeStruct?.Name);
  146. }
  147. [Fact]
  148. public void ClrHelperUnwrap()
  149. {
  150. Assert.NotEqual(holder.CI1.Name, _engine.Evaluate("holder.I1.Name"));
  151. Assert.Equal(holder.CI1.Name, _engine.Evaluate("clrHelper.unwrap(holder.I1).Name"));
  152. }
  153. [Fact]
  154. public void ClrHelperWrap()
  155. {
  156. _engine.Execute("Jint = importNamespace('Jint');");
  157. Assert.NotEqual(holder.I1.Name, _engine.Evaluate("holder.CI1.Name"));
  158. Assert.Equal(holder.I1.Name, _engine.Evaluate("clrHelper.wrap(holder.CI1, Jint.Tests.Runtime.InteropExplicitTypeTests.I1).Name"));
  159. }
  160. [Fact]
  161. public void ClrHelperTypeOf()
  162. {
  163. Action<Engine> runner = engine =>
  164. {
  165. engine.SetValue("clrobj", new object());
  166. Assert.Equal(engine.Evaluate("System.Object"), engine.Evaluate("clrHelper.typeOf(clrobj)"));
  167. };
  168. runner.Invoke(new Engine(cfg =>
  169. {
  170. cfg.AllowClr();
  171. cfg.Interop.AllowGetType = true;
  172. }));
  173. var ex = Assert.Throws<InvalidOperationException>(() =>
  174. {
  175. runner.Invoke(new Engine(cfg =>
  176. {
  177. cfg.AllowClr();
  178. }));
  179. });
  180. Assert.Equal("Invalid when Engine.Options.Interop.AllowGetType == false", ex.Message);
  181. }
  182. [Fact]
  183. public void ClrHelperTypeOfForNestedType()
  184. {
  185. var engine = new Engine(cfg =>
  186. {
  187. cfg.AllowClr(GetType().Assembly);
  188. cfg.Interop.AllowGetType = true;
  189. });
  190. engine.SetValue("holder", holder);
  191. engine.Execute("Jint = importNamespace('Jint');");
  192. Assert.Equal(engine.Evaluate("Jint.Tests.Runtime.InteropExplicitTypeTests.CI1"), engine.Evaluate("clrHelper.typeOf(holder.CI1)"));
  193. Assert.Equal(engine.Evaluate("Jint.Tests.Runtime.InteropExplicitTypeTests.I1"), engine.Evaluate("clrHelper.typeOf(holder.I1)"));
  194. }
  195. public class TypeHolder
  196. {
  197. public static Type Type => typeof(TypeHolder);
  198. }
  199. [Fact]
  200. public void ClrHelperTypeToObject()
  201. {
  202. Action<Engine> runner = engine =>
  203. {
  204. engine.SetValue("TypeHolder", typeof(TypeHolder));
  205. Assert.True(engine.Evaluate("TypeHolder") is TypeReference);
  206. Assert.True(engine.Evaluate("clrHelper.typeToObject(TypeHolder)") is ObjectWrapper);
  207. };
  208. runner.Invoke(new Engine(cfg =>
  209. {
  210. cfg.AllowClr();
  211. cfg.Interop.AllowGetType = true;
  212. }));
  213. var ex = Assert.Throws<InvalidOperationException>(() =>
  214. {
  215. runner.Invoke(new Engine(cfg =>
  216. {
  217. cfg.AllowClr();
  218. }));
  219. });
  220. Assert.Equal("Invalid when Engine.Options.Interop.AllowGetType == false", ex.Message);
  221. }
  222. [Fact]
  223. public void ClrHelperObjectToType()
  224. {
  225. Action<Engine> runner = engine =>
  226. {
  227. engine.SetValue("TypeHolder", typeof(TypeHolder));
  228. Assert.True(engine.Evaluate("TypeHolder.Type") is ObjectWrapper);
  229. Assert.True(engine.Evaluate("clrHelper.objectToType(TypeHolder.Type)") is TypeReference);
  230. };
  231. runner.Invoke(new Engine(cfg =>
  232. {
  233. cfg.AllowClr();
  234. cfg.Interop.AllowGetType = true;
  235. }));
  236. var ex = Assert.Throws<InvalidOperationException>(() =>
  237. {
  238. runner.Invoke(new Engine(cfg =>
  239. {
  240. cfg.AllowClr();
  241. }));
  242. });
  243. Assert.Equal("Invalid when Engine.Options.Interop.AllowGetType == false", ex.Message);
  244. }
  245. public interface ICallObjectMethodFromInterface
  246. {
  247. ICallObjectMethodFromInterface Instance { get; }
  248. // hide Object.GetHashCode
  249. string GetHashCode();
  250. // overload Object.Equals
  251. string Equals();
  252. }
  253. public class CallObjectMethodFromInterface : ICallObjectMethodFromInterface
  254. {
  255. public ICallObjectMethodFromInterface Instance => this;
  256. public override string ToString() => nameof(CallObjectMethodFromInterface);
  257. public new string GetHashCode() => "new GetHashCode, hide Object.GetHashCode";
  258. public string Equals() => "overload Object.Equals";
  259. }
  260. // issue#1626 ToString method is now unavailable in some CLR Interop scenarios
  261. [Fact]
  262. public void CallObjectMethodFromInterfaceWrapper()
  263. {
  264. var inst = new CallObjectMethodFromInterface();
  265. _engine.SetValue("inst", inst);
  266. Assert.Equal(inst.Instance.ToString(), _engine.Evaluate("inst.Instance.ToString()"));
  267. }
  268. [Fact]
  269. public void CallInterfaceMethodWhichHideObjectMethod()
  270. {
  271. var inst = new CallObjectMethodFromInterface();
  272. _engine.SetValue("inst", inst);
  273. Assert.Equal(inst.Instance.GetHashCode(), _engine.Evaluate("inst.Instance.GetHashCode()"));
  274. }
  275. [Fact(Skip = "TODO, no solution now.")]
  276. public void CallObjectMethodHiddenByInterface()
  277. {
  278. var inst = new CallObjectMethodFromInterface();
  279. _engine.SetValue("inst", inst);
  280. Assert.Equal(
  281. (inst.Instance as object).GetHashCode(),
  282. _engine.Evaluate("clrHelper.unwrap(inst.Instance).GetHashCode()")
  283. );
  284. }
  285. [Fact]
  286. public void CallInterfaceMethodWhichOverloadObjectMethod()
  287. {
  288. var inst = new CallObjectMethodFromInterface();
  289. _engine.SetValue("inst", inst);
  290. Assert.Equal(inst.Instance.Equals(), _engine.Evaluate("inst.Instance.Equals()"));
  291. Assert.Equal(inst.Instance.Equals(inst), _engine.Evaluate("inst.Instance.Equals(inst)"));
  292. }
  293. }