ParserOptionsPropagationTests.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. #nullable enable
  2. using Jint.Native;
  3. using Jint.Runtime;
  4. using Jint.Runtime.Modules;
  5. namespace Jint.Tests.Runtime;
  6. public class ParserOptionsPropagationTests
  7. {
  8. public enum SourceKind
  9. {
  10. Script,
  11. ModuleViaBuilder,
  12. ModuleViaFactory,
  13. }
  14. private sealed class ModuleScript : IModuleLoader
  15. {
  16. public const string MainSpecifier = "main";
  17. private readonly bool _prepare;
  18. private readonly string _code;
  19. private readonly ModuleParsingOptions _parsingOptions;
  20. public ModuleScript(bool prepare, string code, ModuleParsingOptions parsingOptions)
  21. {
  22. _prepare = prepare;
  23. _code = code;
  24. _parsingOptions = parsingOptions;
  25. }
  26. ResolvedSpecifier IModuleLoader.Resolve(string? referencingModuleLocation, ModuleRequest moduleRequest)
  27. {
  28. if (moduleRequest.Specifier == MainSpecifier)
  29. return new ResolvedSpecifier(moduleRequest, MainSpecifier, Uri: null, SpecifierType.Bare);
  30. throw new ArgumentException(null, nameof(moduleRequest));
  31. }
  32. Jint.Runtime.Modules.Module IModuleLoader.LoadModule(Engine engine, ResolvedSpecifier resolved)
  33. {
  34. if (resolved.ModuleRequest.Specifier == MainSpecifier)
  35. {
  36. return _prepare
  37. ? ModuleFactory.BuildSourceTextModule(engine, Engine.PrepareModule(_code, MainSpecifier, options: new ModulePreparationOptions { ParsingOptions = _parsingOptions }))
  38. : ModuleFactory.BuildSourceTextModule(engine, resolved, _code, _parsingOptions);
  39. }
  40. throw new ArgumentException(null, nameof(resolved));
  41. }
  42. }
  43. [Theory]
  44. [InlineData(SourceKind.Script, @"'' + eval('(1, 2,)')", false, false, null)]
  45. [InlineData(SourceKind.Script, @"'' + eval('(1, 2,)')", true, false, null)]
  46. [InlineData(SourceKind.Script, @"('',) + eval('(1, 2,)')", false, true, "2")]
  47. [InlineData(SourceKind.Script, @"('',) + eval('(1, 2,)')", true, true, "2")]
  48. [InlineData(SourceKind.ModuleViaBuilder, @"export default '' + eval('(1, 2,)')", false, false, null)]
  49. [InlineData(SourceKind.ModuleViaBuilder, @"export default '' + eval('(1, 2,)')", true, false, null)]
  50. [InlineData(SourceKind.ModuleViaBuilder, @"export default ('',) + eval('(1, 2,)')", false, true, "2")]
  51. [InlineData(SourceKind.ModuleViaBuilder, @"export default ('',) + eval('(1, 2,)')", true, true, "2")]
  52. [InlineData(SourceKind.ModuleViaFactory, @"export default '' + eval('(1, 2,)')", false, false, null)]
  53. [InlineData(SourceKind.ModuleViaFactory, @"export default '' + eval('(1, 2,)')", true, false, null)]
  54. [InlineData(SourceKind.ModuleViaFactory, @"export default ('',) + eval('(1, 2,)')", false, true, "2")]
  55. [InlineData(SourceKind.ModuleViaFactory, @"export default ('',) + eval('(1, 2,)')", true, true, "2")]
  56. [InlineData(SourceKind.Script, @"'' + new Function('return (1, 2,)')()", false, false, null)]
  57. [InlineData(SourceKind.Script, @"'' + new Function('return (1, 2,)')()", true, false, null)]
  58. [InlineData(SourceKind.Script, @"('',) + new Function('return (1, 2,)')()", false, true, "2")]
  59. [InlineData(SourceKind.Script, @"('',) + new Function('return (1, 2,)')()", true, true, "2")]
  60. [InlineData(SourceKind.ModuleViaBuilder, @"export default '' + new Function('return (1, 2,)')()", false, false, null)]
  61. [InlineData(SourceKind.ModuleViaBuilder, @"export default '' + new Function('return (1, 2,)')()", true, false, null)]
  62. [InlineData(SourceKind.ModuleViaBuilder, @"export default ('',) + new Function('return (1, 2,)')()", false, true, "2")]
  63. [InlineData(SourceKind.ModuleViaBuilder, @"export default ('',) + new Function('return (1, 2,)')()", true, true, "2")]
  64. [InlineData(SourceKind.ModuleViaFactory, @"export default '' + new Function('return (1, 2,)')()", false, false, null)]
  65. [InlineData(SourceKind.ModuleViaFactory, @"export default '' + new Function('return (1, 2,)')()", true, false, null)]
  66. [InlineData(SourceKind.ModuleViaFactory, @"export default ('',) + new Function('return (1, 2,)')()", false, true, "2")]
  67. [InlineData(SourceKind.ModuleViaFactory, @"export default ('',) + new Function('return (1, 2,)')()", true, true, "2")]
  68. [InlineData(SourceKind.Script, @"'' + new ShadowRealm().evaluate('(1, 2,)')", false, false, null)]
  69. [InlineData(SourceKind.Script, @"'' + new ShadowRealm().evaluate('(1, 2,)')", true, false, null)]
  70. [InlineData(SourceKind.Script, @"('',) + new ShadowRealm().evaluate('(1, 2,)')", false, true, "2")]
  71. [InlineData(SourceKind.Script, @"('',) + new ShadowRealm().evaluate('(1, 2,)')", true, true, "2")]
  72. [InlineData(SourceKind.ModuleViaBuilder, @"export default '' + new ShadowRealm().evaluate('(1, 2,)')", false, false, null)]
  73. [InlineData(SourceKind.ModuleViaBuilder, @"export default '' + new ShadowRealm().evaluate('(1, 2,)')", true, false, null)]
  74. [InlineData(SourceKind.ModuleViaBuilder, @"export default ('',) + new ShadowRealm().evaluate('(1, 2,)')", false, true, "2")]
  75. [InlineData(SourceKind.ModuleViaBuilder, @"export default ('',) + new ShadowRealm().evaluate('(1, 2,)')", true, true, "2")]
  76. [InlineData(SourceKind.ModuleViaFactory, @"export default '' + new ShadowRealm().evaluate('(1, 2,)')", false, false, null)]
  77. [InlineData(SourceKind.ModuleViaFactory, @"export default '' + new ShadowRealm().evaluate('(1, 2,)')", true, false, null)]
  78. [InlineData(SourceKind.ModuleViaFactory, @"export default ('',) + new ShadowRealm().evaluate('(1, 2,)')", false, true, "2")]
  79. [InlineData(SourceKind.ModuleViaFactory, @"export default ('',) + new ShadowRealm().evaluate('(1, 2,)')", true, true, "2")]
  80. public void DynamicCodeShouldBeParsedWithCallerParserOptions(SourceKind sourceKind, string code, bool prepare, bool tolerant, string? expectedReturnValue)
  81. {
  82. Engine engine;
  83. Func<JsValue> evalAction;
  84. if (sourceKind == SourceKind.Script)
  85. {
  86. var parsingOptions = ScriptParsingOptions.Default with { Tolerant = tolerant };
  87. engine = new Engine();
  88. if (prepare)
  89. {
  90. var preparedScript = Engine.PrepareScript(code, options: new ScriptPreparationOptions { ParsingOptions = parsingOptions });
  91. evalAction = () => engine.Evaluate(preparedScript);
  92. }
  93. else
  94. {
  95. evalAction = () => engine.Evaluate(code, parsingOptions);
  96. }
  97. }
  98. else
  99. {
  100. var parsingOptions = ModuleParsingOptions.Default with { Tolerant = tolerant };
  101. if (sourceKind == SourceKind.ModuleViaBuilder)
  102. {
  103. engine = new Engine();
  104. if (prepare)
  105. {
  106. engine.Modules.Add("main", o =>
  107. {
  108. var preparedModule = Engine.PrepareModule(code, options: new ModulePreparationOptions { ParsingOptions = parsingOptions });
  109. o.AddModule(preparedModule);
  110. });
  111. }
  112. else
  113. {
  114. engine.Modules.Add("main", o => o.AddSource(code).WithOptions(_ => parsingOptions));
  115. }
  116. }
  117. else if (sourceKind == SourceKind.ModuleViaFactory)
  118. {
  119. var moduleScript = new ModuleScript(prepare, code, parsingOptions);
  120. engine = new Engine(o => o.EnableModules(moduleScript));
  121. }
  122. else
  123. {
  124. throw new InvalidOperationException();
  125. }
  126. evalAction = () =>
  127. {
  128. var ns = engine.Modules.Import("main");
  129. return ns.Get("default");
  130. };
  131. }
  132. if (expectedReturnValue is not null)
  133. {
  134. Assert.Equal(expectedReturnValue, evalAction());
  135. }
  136. else
  137. {
  138. var ex = Assert.ThrowsAny<Exception>(evalAction);
  139. Assert.True(ex is JavaScriptException or ParseErrorException);
  140. }
  141. }
  142. [Theory]
  143. [InlineData(false, false, null)]
  144. [InlineData(false, true, "2")]
  145. [InlineData(true, false, null)]
  146. [InlineData(true, true, "2")]
  147. public void TransitivelyImportedModuleShouldBeParsedWithOwnParserOptions(bool mainTolerant, bool otherTolerant, string? expectedReturnValue)
  148. {
  149. var engine = new Engine();
  150. engine.Modules.Add("main", o => o.AddSource("import { x } from 'other'; export default x").WithOptions(o => o with { Tolerant = mainTolerant }));
  151. engine.Modules.Add("other", o => o.AddSource("export const x = '' + eval('(1, 2,)')").WithOptions(o => o with { Tolerant = otherTolerant }));
  152. var evalAction = () =>
  153. {
  154. var ns = engine.Modules.Import("main");
  155. return ns.Get("default");
  156. };
  157. if (expectedReturnValue is not null)
  158. {
  159. Assert.Equal(expectedReturnValue, evalAction());
  160. }
  161. else
  162. {
  163. var ex = Assert.Throws<JavaScriptException>(evalAction);
  164. Assert.True(ex.Error.InstanceofOperator(engine.Realm.Intrinsics.SyntaxError));
  165. }
  166. }
  167. [Fact]
  168. public void RealmsShouldBeIsolatedWithRegardToParserOptions()
  169. {
  170. var engine = new Engine();
  171. var parsingOptions = ScriptParsingOptions.Default with { AllowReturnOutsideFunction = true, Tolerant = true };
  172. Assert.Equal("2", engine.Evaluate("return '' + eval('(1, 2,)')", parsingOptions));
  173. var shadowRealm1 = engine.Intrinsics.ShadowRealm.Construct();
  174. var shadowRealm2 = engine.Intrinsics.ShadowRealm.Construct();
  175. var ex = Assert.Throws<JavaScriptException>(() => shadowRealm1.Evaluate("'' + eval('(1, 2,)')", parsingOptions with { Tolerant = false }));
  176. Assert.True(ex.Error.InstanceofOperator(engine.Intrinsics.TypeError));
  177. Assert.Equal("2", engine.Evaluate("'' + eval('(1, 2,)')", parsingOptions));
  178. ex = Assert.Throws<JavaScriptException>(() => shadowRealm2.Evaluate("'' + eval('(1, 2,)')", parsingOptions with { Tolerant = false }));
  179. Assert.True(ex.Error.InstanceofOperator(engine.Intrinsics.TypeError));
  180. Assert.Equal("2", shadowRealm1.Evaluate("'' + eval('(1, 2,)')", parsingOptions));
  181. ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate("'' + eval('(1, 2,)')", parsingOptions with { Tolerant = false }));
  182. Assert.True(ex.Error.InstanceofOperator(engine.Intrinsics.SyntaxError));
  183. Assert.Equal("2", shadowRealm2.Evaluate("'' + new Function('return (1, 2,)')()", parsingOptions));
  184. }
  185. }