InteropTests.TypeReference.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. using Jint.Native;
  2. using Jint.Native.Symbol;
  3. using Jint.Runtime.Descriptors;
  4. using Jint.Runtime.Interop;
  5. using Jint.Tests.Runtime.Domain;
  6. using Microsoft.Extensions.DependencyInjection;
  7. namespace Jint.Tests.Runtime;
  8. public partial class InteropTests
  9. {
  10. [Fact]
  11. public void DelegateWithDefaultValueParametersCanBeInvoked()
  12. {
  13. var instance = new A();
  14. _engine.SetValue("Instance", instance);
  15. _engine.SetValue("Class", TypeReference.CreateTypeReference(_engine, typeof(A)));
  16. RunTest(@"
  17. assert(Instance.Call19() === 0);
  18. assert(Instance.Call19(1) === 1);
  19. assert(Instance.Call20(1) === 4);
  20. assert(Instance.Call20(1, 2) === 5);
  21. assert(Instance.Call20(1 , 2, 3) === 6);
  22. assert(Class.Call19Static() === 0);
  23. assert(Class.Call19Static(1) === 1);
  24. assert(Class.Call20Static(1) === 4);
  25. assert(Class.Call20Static(1, 2) === 5);
  26. assert(Class.Call20Static(1 , 2, 3) === 6);
  27. ");
  28. }
  29. [Fact]
  30. public void JavaScriptClassCanExtendClrType()
  31. {
  32. _engine.SetValue("TestClass", TypeReference.CreateTypeReference<TestClass>(_engine));
  33. _engine.Execute("class ExtendedType extends TestClass { constructor() { super(); this.a = 1; } get aProp() { return 'A'; } }");
  34. _engine.Execute("class MyExtendedType extends ExtendedType { constructor() { super(); this.b = 2; } get bProp() { return 'B'; } }");
  35. _engine.Evaluate("let obj = new MyExtendedType();");
  36. _engine.Evaluate("obj.setString('Hello World!');");
  37. Assert.Equal("Hello World!", _engine.Evaluate("obj.string"));
  38. Assert.Equal(1, _engine.Evaluate("obj.a"));
  39. Assert.Equal(2, _engine.Evaluate("obj.b"));
  40. Assert.Equal("A", _engine.Evaluate("obj.aProp"));
  41. Assert.Equal("B", _engine.Evaluate("obj.bProp"));
  42. // TODO we should have a special prototype based on wrapped type so we could differentiate between own and type properties
  43. // Assert.Equal("[\"a\"]", _engine.Evaluate("JSON.stringify(Object.getOwnPropertyNames(new ExtendedType()))"));
  44. // Assert.Equal("[\"a\",\"b\"]", _engine.Evaluate("JSON.stringify(Object.getOwnPropertyNames(new MyExtendedType()))"));
  45. }
  46. [Fact]
  47. public void ShouldAllowMethodsOnClrExtendedTypes()
  48. {
  49. _engine.SetValue("ClrBaseType", TypeReference.CreateTypeReference<TestClass>(_engine));
  50. _engine.Evaluate(@"
  51. class JsBaseType {}
  52. class ExtendsFromJs extends JsBaseType {
  53. constructor() {
  54. super();
  55. this.a = 1;
  56. }
  57. getA() {
  58. return this.a;
  59. }
  60. }
  61. class ExtendsFromClr extends ClrBaseType {
  62. constructor() {
  63. super();
  64. this.a = 1;
  65. }
  66. getA() {
  67. return this.a;
  68. }
  69. }
  70. ");
  71. var extendsFromJs = _engine.Construct("ExtendsFromJs");
  72. Assert.Equal(1, _engine.Evaluate("new ExtendsFromJs().getA();"));
  73. Assert.NotEqual(JsValue.Undefined, extendsFromJs.Get("getA"));
  74. var extendsFromClr = _engine.Construct("ExtendsFromClr");
  75. Assert.Equal(1, _engine.Evaluate("new ExtendsFromClr().getA();"));
  76. Assert.NotEqual(JsValue.Undefined, extendsFromClr.Get("getA"));
  77. }
  78. [Fact]
  79. public void ShouldBeInstanceOfTypeReferenceType()
  80. {
  81. _engine.SetValue("A", typeof(A));
  82. RunTest(@"
  83. var a = new A();
  84. assert(a instanceof A);
  85. ");
  86. }
  87. [Fact]
  88. public void IntegerEnumResolutionShouldWork()
  89. {
  90. var engine = new Engine(options => options.AllowClr(GetType().Assembly));
  91. engine.SetValue("a", new OverLoading());
  92. engine.SetValue("E", TypeReference.CreateTypeReference(engine, typeof(IntegerEnum)));
  93. Assert.Equal("integer-enum", engine.Evaluate("a.testFunc(E.a);").AsString());
  94. }
  95. [Fact]
  96. public void UnsignedIntegerEnumResolutionShouldWork()
  97. {
  98. var engine = new Engine(options => options.AllowClr(GetType().Assembly));
  99. engine.SetValue("E", TypeReference.CreateTypeReference(engine, typeof(UintEnum)));
  100. Assert.Equal(1, engine.Evaluate("E.b;").AsNumber());
  101. }
  102. [Fact]
  103. public void ExceptionFromConstructorShouldPropagate()
  104. {
  105. _engine.SetValue("Class", TypeReference.CreateTypeReference(_engine, typeof(MemberExceptionTest)));
  106. var ex = Assert.Throws<InvalidOperationException>(() => _engine.Evaluate("new Class(true);"));
  107. Assert.Equal("thrown as requested", ex.Message);
  108. }
  109. [Fact]
  110. public void ShouldScoreDoubleToDoubleParameterMatchHigherThanDoubleToFloat()
  111. {
  112. var engine = new Engine();
  113. var mathTypeReference = TypeReference.CreateTypeReference(engine, typeof(Math));
  114. engine.SetValue("Math2", mathTypeReference);
  115. var result = engine.Evaluate("Math2.Max(5.37, 5.56)").AsNumber();
  116. Assert.Equal(5.56d, result);
  117. }
  118. [Fact]
  119. public void TypeReferenceShouldGetIntermediaryPrototype()
  120. {
  121. var engine = new Engine();
  122. engine.SetValue("Person", TypeReference.CreateTypeReference<Person>(engine));
  123. var calls = new List<string>();
  124. engine.SetValue("log", new Action<string>(calls.Add));
  125. engine.Execute("Person.prototype.__defineGetter__('bar', function() { log('called'); return 5 });");
  126. engine.Execute("var instance = new Person();");
  127. engine.Execute("log(instance.bar)");
  128. engine.Execute("var z = {};");
  129. engine.Execute("z['bar'] = 20;");
  130. engine.Execute("log(z['bar']);");
  131. Assert.Equal("called#5#20", string.Join("#", calls));
  132. }
  133. [Fact]
  134. public void CanConfigureCustomInstanceCreator()
  135. {
  136. var collection = new ServiceCollection();
  137. collection.AddTransient<Injectable>();
  138. collection.AddTransient<Dependency>();
  139. var serviceProvider = collection.BuildServiceProvider();
  140. var engine = new Engine(options =>
  141. {
  142. options.Interop.CreateTypeReferenceObject = (e, type, arguments) =>
  143. {
  144. var instance = serviceProvider.GetRequiredService(type);
  145. return instance;
  146. };
  147. });
  148. engine.SetValue("Injectable", TypeReference.CreateTypeReference<Injectable>(engine));
  149. Assert.Equal("Hello world", engine.Evaluate("new Injectable(123, 'abc').getInjectedValue();"));
  150. }
  151. [Fact]
  152. public void CanRegisterToStringTag()
  153. {
  154. var reference = TypeReference.CreateTypeReference<Dependency>(_engine);
  155. reference.FastSetProperty(GlobalSymbolRegistry.ToStringTag, new PropertyDescriptor(nameof(Dependency), false, false, true));
  156. reference.FastSetDataProperty("abc", 123);
  157. _engine.SetValue("MyClass", reference);
  158. _engine.Execute("var c = new MyClass();");
  159. Assert.Equal("[object Dependency]", _engine.Evaluate("Object.prototype.toString.call(c);"));
  160. Assert.Equal(123, _engine.Evaluate("c.abc"));
  161. // engine uses registered type reference
  162. _engine.SetValue("c2", new Dependency());
  163. Assert.Equal("[object Dependency]", _engine.Evaluate("Object.prototype.toString.call(c2);"));
  164. Assert.Equal(123, _engine.Evaluate("c2.abc"));
  165. }
  166. private class Injectable
  167. {
  168. private readonly Dependency _dependency;
  169. public Injectable(Dependency dependency)
  170. {
  171. _dependency = dependency;
  172. }
  173. public string GetInjectedValue() => _dependency.Value;
  174. }
  175. private class Dependency
  176. {
  177. public string Value => "Hello world";
  178. }
  179. }