InteropExplicitTypeTests.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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.NotEqual(_engine.Evaluate("holder.I1"), _engine.Evaluate("holder.ci1"));
  76. Assert.Equal(_engine.Evaluate("holder.Super"), _engine.Evaluate("holder.super"));
  77. Assert.NotEqual(_engine.Evaluate("holder.Super"), _engine.Evaluate("holder.ci1"));
  78. }
  79. [Fact]
  80. public void ExplicitInterfaceFromField()
  81. {
  82. Assert.Equal(holder.i1.Name, _engine.Evaluate("holder.i1.Name"));
  83. Assert.NotEqual(holder.i1.Name, _engine.Evaluate("holder.ci1.Name"));
  84. }
  85. [Fact]
  86. public void ExplicitInterfaceFromProperty()
  87. {
  88. Assert.Equal(holder.I1.Name, _engine.Evaluate("holder.I1.Name"));
  89. Assert.NotEqual(holder.I1.Name, _engine.Evaluate("holder.CI1.Name"));
  90. }
  91. [Fact]
  92. public void ExplicitInterfaceFromMethod()
  93. {
  94. Assert.Equal(holder.GetI1().Name, _engine.Evaluate("holder.GetI1().Name"));
  95. Assert.NotEqual(holder.GetI1().Name, _engine.Evaluate("holder.GetCI1().Name"));
  96. }
  97. [Fact]
  98. public void ExplicitInterfaceFromIndexer()
  99. {
  100. Assert.Equal(holder.IndexerI1[0].Name, _engine.Evaluate("holder.IndexerI1[0].Name"));
  101. }
  102. [Fact]
  103. public void SuperClassFromField()
  104. {
  105. Assert.Equal(holder.super.Name, _engine.Evaluate("holder.super.Name"));
  106. Assert.NotEqual(holder.super.Name, _engine.Evaluate("holder.ci1.Name"));
  107. }
  108. [Fact]
  109. public void SuperClassFromProperty()
  110. {
  111. Assert.Equal(holder.Super.Name, _engine.Evaluate("holder.Super.Name"));
  112. Assert.NotEqual(holder.Super.Name, _engine.Evaluate("holder.CI1.Name"));
  113. }
  114. [Fact]
  115. public void SuperClassFromMethod()
  116. {
  117. Assert.Equal(holder.GetSuper().Name, _engine.Evaluate("holder.GetSuper().Name"));
  118. Assert.NotEqual(holder.GetSuper().Name, _engine.Evaluate("holder.GetCI1().Name"));
  119. }
  120. [Fact]
  121. public void SuperClassFromIndexer()
  122. {
  123. Assert.Equal(holder.IndexerSuper[0].Name, _engine.Evaluate("holder.IndexerSuper[0].Name"));
  124. }
  125. public struct NullabeStruct : I1
  126. {
  127. public NullabeStruct()
  128. {
  129. }
  130. public string name = "NullabeStruct";
  131. public string Name => name;
  132. string I1.Name => "NullabeStruct as I1";
  133. }
  134. public class NullableHolder
  135. {
  136. public I1 I1 { get; set; }
  137. public NullabeStruct? NullabeStruct { get; set; }
  138. }
  139. [Fact]
  140. public void TypedObjectWrapperForNullableType()
  141. {
  142. var nullableHolder = new NullableHolder();
  143. _engine.SetValue("nullableHolder", nullableHolder);
  144. _engine.SetValue("nullabeStruct", new NullabeStruct());
  145. Assert.Equal(_engine.Evaluate("nullableHolder.NullabeStruct"), JsValue.Null);
  146. _engine.Evaluate("nullableHolder.NullabeStruct = nullabeStruct");
  147. Assert.Equal(_engine.Evaluate("nullableHolder.NullabeStruct.Name"), nullableHolder.NullabeStruct?.Name);
  148. }
  149. [Fact]
  150. public void ClrHelperUnwrap()
  151. {
  152. Assert.NotEqual(holder.CI1.Name, _engine.Evaluate("holder.I1.Name"));
  153. Assert.Equal(holder.CI1.Name, _engine.Evaluate("clrHelper.unwrap(holder.I1).Name"));
  154. }
  155. [Fact]
  156. public void ClrHelperWrap()
  157. {
  158. _engine.Execute("Jint = importNamespace('Jint');");
  159. Assert.NotEqual(holder.I1.Name, _engine.Evaluate("holder.CI1.Name"));
  160. Assert.Equal(holder.I1.Name, _engine.Evaluate("clrHelper.wrap(holder.CI1, Jint.Tests.Runtime.InteropExplicitTypeTests.I1).Name"));
  161. }
  162. [Fact]
  163. public void ClrHelperTypeOf()
  164. {
  165. Action<Engine> runner = engine =>
  166. {
  167. engine.SetValue("clrobj", new object());
  168. Assert.Equal(engine.Evaluate("System.Object"), engine.Evaluate("clrHelper.typeOf(clrobj)"));
  169. };
  170. runner.Invoke(new Engine(cfg =>
  171. {
  172. cfg.AllowClr();
  173. cfg.Interop.AllowGetType = true;
  174. }));
  175. var ex = Assert.Throws<InvalidOperationException>(() =>
  176. {
  177. runner.Invoke(new Engine(cfg =>
  178. {
  179. cfg.AllowClr();
  180. }));
  181. });
  182. Assert.Equal("Invalid when Engine.Options.Interop.AllowGetType == false", ex.Message);
  183. }
  184. [Fact]
  185. public void ClrHelperTypeOfForNestedType()
  186. {
  187. var engine = new Engine(cfg =>
  188. {
  189. cfg.AllowClr(GetType().Assembly);
  190. cfg.Interop.AllowGetType = true;
  191. });
  192. engine.SetValue("holder", holder);
  193. engine.Execute("Jint = importNamespace('Jint');");
  194. Assert.Equal(engine.Evaluate("Jint.Tests.Runtime.InteropExplicitTypeTests.CI1"), engine.Evaluate("clrHelper.typeOf(holder.CI1)"));
  195. Assert.Equal(engine.Evaluate("Jint.Tests.Runtime.InteropExplicitTypeTests.I1"), engine.Evaluate("clrHelper.typeOf(holder.I1)"));
  196. }
  197. public class TypeHolder
  198. {
  199. public static Type Type => typeof(TypeHolder);
  200. }
  201. [Fact]
  202. public void ClrHelperTypeToObject()
  203. {
  204. Action<Engine> runner = engine =>
  205. {
  206. engine.SetValue("TypeHolder", typeof(TypeHolder));
  207. Assert.True(engine.Evaluate("TypeHolder") is TypeReference);
  208. Assert.True(engine.Evaluate("clrHelper.typeToObject(TypeHolder)") is ObjectWrapper);
  209. };
  210. runner.Invoke(new Engine(cfg =>
  211. {
  212. cfg.AllowClr();
  213. cfg.Interop.AllowGetType = true;
  214. }));
  215. var ex = Assert.Throws<InvalidOperationException>(() =>
  216. {
  217. runner.Invoke(new Engine(cfg =>
  218. {
  219. cfg.AllowClr();
  220. }));
  221. });
  222. Assert.Equal("Invalid when Engine.Options.Interop.AllowGetType == false", ex.Message);
  223. }
  224. [Fact]
  225. public void ClrHelperObjectToType()
  226. {
  227. Action<Engine> runner = engine =>
  228. {
  229. engine.SetValue("TypeHolder", typeof(TypeHolder));
  230. Assert.True(engine.Evaluate("TypeHolder.Type") is ObjectWrapper);
  231. Assert.True(engine.Evaluate("clrHelper.objectToType(TypeHolder.Type)") is TypeReference);
  232. };
  233. runner.Invoke(new Engine(cfg =>
  234. {
  235. cfg.AllowClr();
  236. cfg.Interop.AllowGetType = true;
  237. }));
  238. var ex = Assert.Throws<InvalidOperationException>(() =>
  239. {
  240. runner.Invoke(new Engine(cfg =>
  241. {
  242. cfg.AllowClr();
  243. }));
  244. });
  245. Assert.Equal("Invalid when Engine.Options.Interop.AllowGetType == false", ex.Message);
  246. }
  247. public interface ICallObjectMethodFromInterface
  248. {
  249. ICallObjectMethodFromInterface Instance { get; }
  250. // hide Object.GetHashCode
  251. string GetHashCode();
  252. // overload Object.Equals
  253. string Equals();
  254. }
  255. public class CallObjectMethodFromInterface : ICallObjectMethodFromInterface
  256. {
  257. public ICallObjectMethodFromInterface Instance => this;
  258. public override string ToString() => nameof(CallObjectMethodFromInterface);
  259. public new string GetHashCode() => "new GetHashCode, hide Object.GetHashCode";
  260. public string Equals() => "overload Object.Equals";
  261. }
  262. // issue#1626 ToString method is now unavailable in some CLR Interop scenarios
  263. [Fact]
  264. public void CallObjectMethodFromInterfaceWrapper()
  265. {
  266. var inst = new CallObjectMethodFromInterface();
  267. _engine.SetValue("inst", inst);
  268. Assert.Equal(inst.Instance.ToString(), _engine.Evaluate("inst.Instance.ToString()"));
  269. }
  270. [Fact]
  271. public void CallInterfaceMethodWhichHideObjectMethod()
  272. {
  273. var inst = new CallObjectMethodFromInterface();
  274. _engine.SetValue("inst", inst);
  275. Assert.Equal(inst.Instance.GetHashCode(), _engine.Evaluate("inst.Instance.GetHashCode()"));
  276. }
  277. [Fact(Skip = "TODO, no solution now.")]
  278. public void CallObjectMethodHiddenByInterface()
  279. {
  280. var inst = new CallObjectMethodFromInterface();
  281. _engine.SetValue("inst", inst);
  282. Assert.Equal(
  283. (inst.Instance as object).GetHashCode(),
  284. _engine.Evaluate("clrHelper.unwrap(inst.Instance).GetHashCode()")
  285. );
  286. }
  287. [Fact]
  288. public void CallInterfaceMethodWhichOverloadObjectMethod()
  289. {
  290. var inst = new CallObjectMethodFromInterface();
  291. _engine.SetValue("inst", inst);
  292. Assert.Equal(inst.Instance.Equals(), _engine.Evaluate("inst.Instance.Equals()"));
  293. Assert.Equal(inst.Instance.Equals(inst), _engine.Evaluate("inst.Instance.Equals(inst)"));
  294. }
  295. }