ParserOptionsPropagationTests.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. // NOTE: Can't test eval as Esprima can't parse eval code in non-tolerant mode.
  45. //[InlineData(SourceKind.Script, @"'' + eval('!!({g\\u0065t y() {} })')", false, false, null)]
  46. //[InlineData(SourceKind.Script, @"'' + eval('!!({g\\u0065t y() {} })')", true, false, null)]
  47. //[InlineData(SourceKind.Script, @"({g\u0065t x() {} }) + eval('!!({g\\u0065t y() {} })')", false, true, "[object Object]true")]
  48. //[InlineData(SourceKind.Script, @"({g\u0065t x() {} }) + eval('!!({g\\u0065t y() {} })')", true, true, "[object Object]true")]
  49. //[InlineData(SourceKind.ModuleViaBuilder, @"export default '' + eval('!!({g\\u0065t y() {} })')", false, false, null)]
  50. //[InlineData(SourceKind.ModuleViaBuilder, @"export default '' + eval('!!({g\\u0065t y() {} })')", true, false, null)]
  51. //[InlineData(SourceKind.ModuleViaBuilder, @"export default ({g\u0065t x() {} }) + eval('!!({g\\u0065t y() {} })')", false, true, "[object Object]true")]
  52. //[InlineData(SourceKind.ModuleViaBuilder, @"export default ({g\u0065t x() {} }) + eval('!!({g\\u0065t y() {} })')", true, true, "[object Object]true")]
  53. //[InlineData(SourceKind.ModuleViaFactory, @"export default '' + eval('!!({g\\u0065t y() {} })')", false, false, null)]
  54. //[InlineData(SourceKind.ModuleViaFactory, @"export default '' + eval('!!({g\\u0065t y() {} })')", true, false, null)]
  55. //[InlineData(SourceKind.ModuleViaFactory, @"export default ({g\u0065t x() {} }) + eval('!!({g\\u0065t y() {} })')", false, true, "[object Object]true")]
  56. //[InlineData(SourceKind.ModuleViaFactory, @"export default ({g\u0065t x() {} }) + eval('!!({g\\u0065t y() {} })')", true, true, "[object Object]true")]
  57. [InlineData(SourceKind.Script, @"'' + new Function('return !!({g\\u0065t y() {} })')()", false, false, null)]
  58. [InlineData(SourceKind.Script, @"'' + new Function('return !!({g\\u0065t y() {} })')()", true, false, null)]
  59. [InlineData(SourceKind.Script, @"({g\u0065t x() {} }) + new Function('return !!({g\\u0065t y() {} })')()", false, true, "[object Object]true")]
  60. [InlineData(SourceKind.Script, @"({g\u0065t x() {} }) + new Function('return !!({g\\u0065t y() {} })')()", true, true, "[object Object]true")]
  61. [InlineData(SourceKind.ModuleViaBuilder, @"export default '' + new Function('return !!({g\\u0065t y() {} })')()", false, false, null)]
  62. [InlineData(SourceKind.ModuleViaBuilder, @"export default '' + new Function('return !!({g\\u0065t y() {} })')()", true, false, null)]
  63. [InlineData(SourceKind.ModuleViaBuilder, @"export default ({g\u0065t x() {} }) + new Function('return !!({g\\u0065t y() {} })')()", false, true, "[object Object]true")]
  64. [InlineData(SourceKind.ModuleViaBuilder, @"export default ({g\u0065t x() {} }) + new Function('return !!({g\\u0065t y() {} })')()", true, true, "[object Object]true")]
  65. [InlineData(SourceKind.ModuleViaFactory, @"export default '' + new Function('return !!({g\\u0065t y() {} })')()", false, false, null)]
  66. [InlineData(SourceKind.ModuleViaFactory, @"export default '' + new Function('return !!({g\\u0065t y() {} })')()", true, false, null)]
  67. [InlineData(SourceKind.ModuleViaFactory, @"export default ({g\u0065t x() {} }) + new Function('return !!({g\\u0065t y() {} })')()", false, true, "[object Object]true")]
  68. [InlineData(SourceKind.ModuleViaFactory, @"export default ({g\u0065t x() {} }) + new Function('return !!({g\\u0065t y() {} })')()", true, true, "[object Object]true")]
  69. [InlineData(SourceKind.Script, @"'' + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", false, false, null)]
  70. [InlineData(SourceKind.Script, @"'' + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", true, false, null)]
  71. [InlineData(SourceKind.Script, @"({g\u0065t x() {} }) + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", false, true, "[object Object]true")]
  72. [InlineData(SourceKind.Script, @"({g\u0065t x() {} }) + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", true, true, "[object Object]true")]
  73. [InlineData(SourceKind.ModuleViaBuilder, @"export default '' + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", false, false, null)]
  74. [InlineData(SourceKind.ModuleViaBuilder, @"export default '' + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", true, false, null)]
  75. [InlineData(SourceKind.ModuleViaBuilder, @"export default ({g\u0065t x() {} }) + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", false, true, "[object Object]true")]
  76. [InlineData(SourceKind.ModuleViaBuilder, @"export default ({g\u0065t x() {} }) + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", true, true, "[object Object]true")]
  77. [InlineData(SourceKind.ModuleViaFactory, @"export default '' + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", false, false, null)]
  78. [InlineData(SourceKind.ModuleViaFactory, @"export default '' + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", true, false, null)]
  79. [InlineData(SourceKind.ModuleViaFactory, @"export default ({g\u0065t x() {} }) + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", false, true, "[object Object]true")]
  80. [InlineData(SourceKind.ModuleViaFactory, @"export default ({g\u0065t x() {} }) + new ShadowRealm().evaluate('!!({g\\u0065t y() {} })')", true, true, "[object Object]true")]
  81. public void DynamicCodeShouldBeParsedWithCallerParserOptions(SourceKind sourceKind, string code, bool prepare, bool tolerant, string? expectedReturnValue)
  82. {
  83. Engine engine;
  84. Func<JsValue> evalAction;
  85. if (sourceKind == SourceKind.Script)
  86. {
  87. var parsingOptions = ScriptParsingOptions.Default with { Tolerant = tolerant };
  88. engine = new Engine();
  89. if (prepare)
  90. {
  91. var preparedScript = Engine.PrepareScript(code, options: new ScriptPreparationOptions { ParsingOptions = parsingOptions });
  92. evalAction = () => engine.Evaluate(preparedScript);
  93. }
  94. else
  95. {
  96. evalAction = () => engine.Evaluate(code, parsingOptions);
  97. }
  98. }
  99. else
  100. {
  101. var parsingOptions = ModuleParsingOptions.Default with { Tolerant = tolerant };
  102. if (sourceKind == SourceKind.ModuleViaBuilder)
  103. {
  104. engine = new Engine();
  105. if (prepare)
  106. {
  107. engine.Modules.Add("main", o =>
  108. {
  109. var preparedModule = Engine.PrepareModule(code, options: new ModulePreparationOptions { ParsingOptions = parsingOptions });
  110. o.AddModule(preparedModule);
  111. });
  112. }
  113. else
  114. {
  115. engine.Modules.Add("main", o => o.AddSource(code).WithOptions(_ => parsingOptions));
  116. }
  117. }
  118. else if (sourceKind == SourceKind.ModuleViaFactory)
  119. {
  120. var moduleScript = new ModuleScript(prepare, code, parsingOptions);
  121. engine = new Engine(o => o.EnableModules(moduleScript));
  122. }
  123. else
  124. {
  125. throw new InvalidOperationException();
  126. }
  127. evalAction = () =>
  128. {
  129. var ns = engine.Modules.Import("main");
  130. return ns.Get("default");
  131. };
  132. }
  133. if (expectedReturnValue is not null)
  134. {
  135. Assert.Equal(expectedReturnValue, evalAction());
  136. }
  137. else
  138. {
  139. var ex = Assert.ThrowsAny<Exception>(evalAction);
  140. Assert.True(ex is JavaScriptException or ParseErrorException);
  141. }
  142. }
  143. [Theory]
  144. [InlineData(false, false, null)]
  145. [InlineData(false, true, "true")]
  146. [InlineData(true, false, null)]
  147. [InlineData(true, true, "true")]
  148. public void TransitivelyImportedModuleShouldBeParsedWithOwnParserOptions(bool mainTolerant, bool otherTolerant, string? expectedReturnValue)
  149. {
  150. var engine = new Engine();
  151. engine.Modules.Add("main", o => o.AddSource("import { x } from 'other'; export default x").WithOptions(_ => ModuleParsingOptions.Default with { Tolerant = mainTolerant }));
  152. engine.Modules.Add("other", o => o.AddSource("export const x = '' + new Function('return !!({g\\\\u0065t y() {} })')()").WithOptions(_ => ModuleParsingOptions.Default with { Tolerant = otherTolerant }));
  153. var evalAction = () =>
  154. {
  155. var ns = engine.Modules.Import("main");
  156. return ns.Get("default");
  157. };
  158. if (expectedReturnValue is not null)
  159. {
  160. Assert.Equal(expectedReturnValue, evalAction());
  161. }
  162. else
  163. {
  164. var ex = Assert.Throws<JavaScriptException>(evalAction);
  165. Assert.True(ex.Error.InstanceofOperator(engine.Realm.Intrinsics.SyntaxError));
  166. }
  167. }
  168. [Fact]
  169. public void RealmsShouldBeIsolatedWithRegardToParserOptions()
  170. {
  171. var engine = new Engine();
  172. var parsingOptions = ScriptParsingOptions.Default with { AllowReturnOutsideFunction = true, Tolerant = true };
  173. Assert.Equal("true", engine.Evaluate("return '' + new Function('return !!({g\\\\u0065t y() {} })')()", parsingOptions));
  174. var shadowRealm1 = engine.Intrinsics.ShadowRealm.Construct();
  175. var shadowRealm2 = engine.Intrinsics.ShadowRealm.Construct();
  176. var ex = Assert.Throws<JavaScriptException>(() => shadowRealm1.Evaluate("'' + new Function('return !!({g\\\\u0065t y() {} })')()", parsingOptions with { Tolerant = false }));
  177. Assert.True(ex.Error.InstanceofOperator(engine.Intrinsics.TypeError));
  178. Assert.Equal("true", engine.Evaluate("'' + new Function('return !!({g\\\\u0065t y() {} })')()", parsingOptions));
  179. ex = Assert.Throws<JavaScriptException>(() => shadowRealm2.Evaluate("'' + new Function('return !!({g\\\\u0065t y() {} })')()", parsingOptions with { Tolerant = false }));
  180. Assert.True(ex.Error.InstanceofOperator(engine.Intrinsics.TypeError));
  181. Assert.Equal("true", shadowRealm1.Evaluate("'' + new Function('return !!({g\\\\u0065t y() {} })')()", parsingOptions));
  182. ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate("'' + new Function('return !!({g\\\\u0065t y() {} })')()", parsingOptions with { Tolerant = false }));
  183. Assert.True(ex.Error.InstanceofOperator(engine.Intrinsics.SyntaxError));
  184. Assert.Equal("true", shadowRealm2.Evaluate("'' + new Function('return !!({g\\\\u0065t y() {} })')()", parsingOptions));
  185. }
  186. }