SourceTextModule.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. using System.Diagnostics;
  2. using Jint.Native.Object;
  3. using Jint.Native.Promise;
  4. using Jint.Runtime.Environments;
  5. using Jint.Runtime.Interpreter;
  6. namespace Jint.Runtime.Modules;
  7. /// <summary>
  8. /// https://tc39.es/ecma262/#importentry-record
  9. /// </summary>
  10. internal sealed record ImportEntry(ModuleRequest ModuleRequest, string? ImportName, string LocalName);
  11. /// <summary>
  12. /// https://tc39.es/ecma262/#exportentry-record
  13. /// </summary>
  14. internal sealed record ExportEntry(string? ExportName, ModuleRequest? ModuleRequest, string? ImportName, string? LocalName);
  15. /// <summary>
  16. /// https://tc39.es/ecma262/#sec-source-text-module-records
  17. /// </summary>
  18. internal class SourceTextModule : CyclicModule
  19. {
  20. internal readonly AstModule _source;
  21. private readonly ParserOptions _parserOptions;
  22. private ExecutionContext _context;
  23. private ObjectInstance? _importMeta;
  24. private readonly List<ImportEntry>? _importEntries;
  25. internal readonly List<ExportEntry> _localExportEntries;
  26. private readonly List<ExportEntry> _indirectExportEntries;
  27. private readonly List<ExportEntry> _starExportEntries;
  28. internal SourceTextModule(Engine engine, Realm realm, in Prepared<AstModule> source, string? location, bool async)
  29. : base(engine, realm, location, async)
  30. {
  31. Debug.Assert(source.IsValid);
  32. _source = source.Program!;
  33. _parserOptions = source.ParserOptions!;
  34. // https://tc39.es/ecma262/#sec-parsemodule
  35. HoistingScope.GetImportsAndExports(
  36. _source,
  37. out _requestedModules,
  38. out _importEntries,
  39. out _localExportEntries,
  40. out _indirectExportEntries,
  41. out _starExportEntries);
  42. //ToDo async modules
  43. }
  44. internal ObjectInstance ImportMeta
  45. {
  46. get
  47. {
  48. if (_importMeta is null)
  49. {
  50. _importMeta = _realm.Intrinsics.Object.Construct(1);
  51. _importMeta.CreateDataProperty("url", Location);
  52. }
  53. return _importMeta;
  54. }
  55. set => _importMeta = value;
  56. }
  57. /// <summary>
  58. /// https://tc39.es/ecma262/#sec-getexportednames
  59. /// </summary>
  60. public override List<string?> GetExportedNames(List<CyclicModule>? exportStarSet = null)
  61. {
  62. exportStarSet ??= new List<CyclicModule>();
  63. if (exportStarSet.Contains(this))
  64. {
  65. //Reached the starting point of an export * circularity
  66. return new List<string?>();
  67. }
  68. exportStarSet.Add(this);
  69. var exportedNames = new List<string?>();
  70. for (var i = 0; i < _localExportEntries.Count; i++)
  71. {
  72. var e = _localExportEntries[i];
  73. exportedNames.Add(e.ExportName);
  74. }
  75. for (var i = 0; i < _indirectExportEntries.Count; i++)
  76. {
  77. var e = _indirectExportEntries[i];
  78. exportedNames.Add(e.ExportName);
  79. }
  80. for (var i = 0; i < _starExportEntries.Count; i++)
  81. {
  82. var e = _starExportEntries[i];
  83. var requestedModule = _engine._host.GetImportedModule(this, e.ModuleRequest!.Value);
  84. var starNames = requestedModule.GetExportedNames(exportStarSet);
  85. for (var j = 0; j < starNames.Count; j++)
  86. {
  87. var n = starNames[j];
  88. if (!"default".Equals(n, StringComparison.Ordinal) && !exportedNames.Contains(n))
  89. {
  90. exportedNames.Add(n);
  91. }
  92. }
  93. }
  94. return exportedNames;
  95. }
  96. /// <summary>
  97. /// https://tc39.es/ecma262/#sec-resolveexport
  98. /// </summary>
  99. internal override ResolvedBinding? ResolveExport(string? exportName, List<ExportResolveSetItem>? resolveSet = null)
  100. {
  101. resolveSet ??= new List<ExportResolveSetItem>();
  102. for (var i = 0; i < resolveSet.Count; i++)
  103. {
  104. var r = resolveSet[i];
  105. if (ReferenceEquals(this, r.Module) && string.Equals(exportName, r.ExportName, StringComparison.Ordinal))
  106. {
  107. // circular import request
  108. return null;
  109. }
  110. }
  111. resolveSet.Add(new ExportResolveSetItem(this, exportName));
  112. for (var i = 0; i < _localExportEntries.Count; i++)
  113. {
  114. var e = _localExportEntries[i];
  115. if (string.Equals(exportName, e.ExportName, StringComparison.Ordinal))
  116. {
  117. // i. Assert: module provides the direct binding for this export.
  118. return new ResolvedBinding(this, e.LocalName);
  119. }
  120. }
  121. for (var i = 0; i < _indirectExportEntries.Count; i++)
  122. {
  123. var e = _indirectExportEntries[i];
  124. if (string.Equals(exportName, e.ExportName, StringComparison.Ordinal))
  125. {
  126. var importedModule = _engine._host.GetImportedModule(this, e.ModuleRequest!.Value);
  127. if (string.Equals(e.ImportName, "*", StringComparison.Ordinal))
  128. {
  129. // 1. Assert: module does not provide the direct binding for this export.
  130. return new ResolvedBinding(importedModule, "*namespace*");
  131. }
  132. else
  133. {
  134. // 1. Assert: module imports a specific binding for this export.
  135. return importedModule.ResolveExport(e.ImportName, resolveSet);
  136. }
  137. }
  138. }
  139. if ("default".Equals(exportName, StringComparison.Ordinal))
  140. {
  141. // Assert: A default export was not explicitly defined by this module
  142. return null;
  143. }
  144. ResolvedBinding? starResolution = null;
  145. for (var i = 0; i < _starExportEntries.Count; i++)
  146. {
  147. var e = _starExportEntries[i];
  148. var importedModule = _engine._host.GetImportedModule(this, e.ModuleRequest!.Value);
  149. var resolution = importedModule.ResolveExport(exportName, resolveSet);
  150. if (resolution == ResolvedBinding.Ambiguous)
  151. {
  152. return resolution;
  153. }
  154. if (resolution is not null)
  155. {
  156. if (starResolution is null)
  157. {
  158. starResolution = resolution;
  159. }
  160. else
  161. {
  162. if (resolution.Module != starResolution.Module || !string.Equals(resolution.BindingName, starResolution.BindingName, StringComparison.Ordinal))
  163. {
  164. return ResolvedBinding.Ambiguous;
  165. }
  166. }
  167. }
  168. }
  169. return starResolution;
  170. }
  171. /// <summary>
  172. /// https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment
  173. /// </summary>
  174. protected override void InitializeEnvironment()
  175. {
  176. for (var i = 0; i < _indirectExportEntries.Count; i++)
  177. {
  178. var e = _indirectExportEntries[i];
  179. var resolution = ResolveExport(e.ExportName);
  180. if (resolution is null || resolution == ResolvedBinding.Ambiguous)
  181. {
  182. Throw.SyntaxError(_realm, "Ambiguous import statement for identifier: " + e.ExportName);
  183. }
  184. }
  185. var realm = _realm;
  186. var env = JintEnvironment.NewModuleEnvironment(_engine, realm.GlobalEnv);
  187. _environment = env;
  188. if (_importEntries != null)
  189. {
  190. for (var i = 0; i < _importEntries.Count; i++)
  191. {
  192. var ie = _importEntries[i];
  193. var importedModule = _engine._host.GetImportedModule(this, ie.ModuleRequest);
  194. if (string.Equals(ie.ImportName, "*", StringComparison.Ordinal))
  195. {
  196. var ns = GetModuleNamespace(importedModule);
  197. env.CreateImmutableBinding(ie.LocalName, strict: true);
  198. env.InitializeBinding(ie.LocalName, ns, DisposeHint.Normal);
  199. }
  200. else
  201. {
  202. var resolution = importedModule.ResolveExport(ie.ImportName);
  203. if (resolution is null || resolution == ResolvedBinding.Ambiguous)
  204. {
  205. Throw.SyntaxError(_realm, "Ambiguous import statement for identifier " + ie.ImportName);
  206. }
  207. if (string.Equals(resolution.BindingName, "*namespace*", StringComparison.Ordinal))
  208. {
  209. var ns = GetModuleNamespace(resolution.Module);
  210. env.CreateImmutableBinding(ie.LocalName, strict: true);
  211. env.InitializeBinding(ie.LocalName, ns, DisposeHint.Normal);
  212. }
  213. else
  214. {
  215. env.CreateImportBinding(ie.LocalName, resolution.Module, resolution.BindingName);
  216. }
  217. }
  218. }
  219. }
  220. var moduleContext = new ExecutionContext(this, _environment, _environment, null, realm, null);
  221. _context = moduleContext;
  222. _engine.EnterExecutionContext(_context);
  223. var hoistingScope = HoistingScope.GetModuleLevelDeclarations(_source);
  224. var varDeclarations = hoistingScope._variablesDeclarations;
  225. var declaredVarNames = new HashSet<Key>();
  226. if (varDeclarations != null)
  227. {
  228. var boundNames = new List<Key>();
  229. for (var i = 0; i < varDeclarations.Count; i++)
  230. {
  231. var d = varDeclarations[i];
  232. boundNames.Clear();
  233. d.GetBoundNames(boundNames);
  234. for (var j = 0; j < boundNames.Count; j++)
  235. {
  236. var dn = boundNames[j];
  237. if (declaredVarNames.Add(dn))
  238. {
  239. env.CreateMutableBinding(dn);
  240. env.InitializeBinding(dn, Undefined, d.Kind.GetDisposeHint());
  241. }
  242. }
  243. }
  244. }
  245. if (hoistingScope._lexicalDeclarations != null)
  246. {
  247. var cache = DeclarationCacheBuilder.Build(hoistingScope._lexicalDeclarations);
  248. for (var i = 0; i < cache.Declarations.Count; i++)
  249. {
  250. var declaration = cache.Declarations[i];
  251. foreach (var bn in declaration.BoundNames)
  252. {
  253. if (declaration.IsConstantDeclaration)
  254. {
  255. env.CreateImmutableBinding(bn);
  256. }
  257. else
  258. {
  259. env.CreateMutableBinding(bn);
  260. }
  261. }
  262. }
  263. }
  264. var functionDeclarations = hoistingScope._functionDeclarations;
  265. if (functionDeclarations != null)
  266. {
  267. for (var i = 0; i < functionDeclarations.Count; i++)
  268. {
  269. var d = functionDeclarations[i];
  270. var fn = d.Id?.Name ?? "*default*";
  271. var fd = new JintFunctionDefinition(d);
  272. env.CreateMutableBinding(fn, canBeDeleted: true);
  273. var fo = realm.Intrinsics.Function.InstantiateFunctionObject(fd, env, privateEnv: null);
  274. if (string.Equals(fn, "*default*", StringComparison.Ordinal))
  275. {
  276. fo.SetFunctionName("default");
  277. }
  278. env.InitializeBinding(fn, fo, DisposeHint.Normal);
  279. }
  280. }
  281. _engine.LeaveExecutionContext();
  282. }
  283. /// <summary>
  284. /// https://tc39.es/ecma262/#sec-source-text-module-record-execute-module
  285. /// </summary>
  286. internal override Completion ExecuteModule(PromiseCapability? capability = null)
  287. {
  288. var moduleContext = new ExecutionContext(this, _environment, _environment, privateEnvironment: null, _realm, parserOptions: _parserOptions);
  289. if (!_hasTLA)
  290. {
  291. using (new StrictModeScope(strict: true, force: true))
  292. {
  293. var result = Completion.Empty();
  294. _engine.EnterExecutionContext(moduleContext);
  295. try
  296. {
  297. var statementList = new JintStatementList(statement: null, _source.Body);
  298. //Create new evaluation context when called from e.g. module tests
  299. var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
  300. result = statementList.Execute(context);
  301. }
  302. finally
  303. {
  304. result = _environment.DisposeResources(result);
  305. _engine.LeaveExecutionContext();
  306. }
  307. return result;
  308. }
  309. }
  310. else
  311. {
  312. Throw.NotImplementedException("async modules not implemented");
  313. return default;
  314. }
  315. }
  316. }