JsModule.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  1. using System;
  2. using Esprima.Ast;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using Esprima;
  6. using Jint.Native;
  7. using Jint.Native.Object;
  8. using Jint.Native.Promise;
  9. using Jint.Runtime.Descriptors;
  10. using Jint.Runtime.Environments;
  11. using Jint.Runtime.Interop;
  12. using Jint.Runtime.Interpreter;
  13. namespace Jint.Runtime.Modules;
  14. #pragma warning disable CS0649 // never assigned to, waiting for new functionalities in spec
  15. internal sealed record ResolvedBinding(JsModule Module, string BindingName)
  16. {
  17. internal static ResolvedBinding Ambiguous => new(null, "ambiguous");
  18. }
  19. internal sealed record ImportEntry(
  20. string ModuleRequest,
  21. string ImportName,
  22. string LocalName
  23. );
  24. internal sealed record ExportEntry(
  25. string ExportName,
  26. string ModuleRequest,
  27. string ImportName,
  28. string LocalName
  29. );
  30. internal sealed record ExportResolveSetItem(
  31. JsModule Module,
  32. string ExportName
  33. );
  34. /// <summary>
  35. /// Represents a module record
  36. /// https://tc39.es/ecma262/#sec-abstract-module-records
  37. /// https://tc39.es/ecma262/#sec-cyclic-module-records
  38. /// https://tc39.es/ecma262/#sec-source-text-module-records
  39. /// </summary>
  40. public sealed class JsModule : JsValue, IScriptOrModule
  41. {
  42. private readonly Engine _engine;
  43. private readonly Realm _realm;
  44. internal ModuleEnvironmentRecord _environment;
  45. private ObjectInstance _namespace;
  46. private Completion? _evalError;
  47. private int _dfsIndex;
  48. private int _dfsAncestorIndex;
  49. private readonly HashSet<string> _requestedModules;
  50. private JsModule _cycleRoot;
  51. private bool _hasTLA;
  52. private bool _asyncEvaluation;
  53. private PromiseCapability _topLevelCapability;
  54. private List<JsModule> _asyncParentModules;
  55. private int _asyncEvalOrder;
  56. private int _pendingAsyncDependencies;
  57. private readonly Module _source;
  58. private ExecutionContext _context;
  59. private readonly ObjectInstance _importMeta;
  60. private readonly List<ImportEntry> _importEntries;
  61. private readonly List<ExportEntry> _localExportEntries;
  62. private readonly List<ExportEntry> _indirectExportEntries;
  63. private readonly List<ExportEntry> _starExportEntries;
  64. internal JsValue _evalResult;
  65. internal JsModule(Engine engine, Realm realm, Module source, string location, bool async) : base(InternalTypes.Module)
  66. {
  67. _engine = engine;
  68. _realm = realm;
  69. _source = source;
  70. Location = location;
  71. _importMeta = _realm.Intrinsics.Object.Construct(1);
  72. _importMeta.DefineOwnProperty("url", new PropertyDescriptor(location, PropertyFlag.ConfigurableEnumerableWritable));
  73. HoistingScope.GetImportsAndExports(
  74. _source,
  75. out _requestedModules,
  76. out _importEntries,
  77. out _localExportEntries,
  78. out _indirectExportEntries,
  79. out _starExportEntries);
  80. //ToDo async modules
  81. }
  82. public string Location { get; }
  83. internal ModuleStatus Status { get; private set; }
  84. /// <summary>
  85. /// https://tc39.es/ecma262/#sec-getmodulenamespace
  86. /// </summary>
  87. public static ObjectInstance GetModuleNamespace(JsModule module)
  88. {
  89. var ns = module._namespace;
  90. if(ns is null)
  91. {
  92. var exportedNames = module.GetExportedNames();
  93. var unambiguousNames = new List<string>();
  94. for (var i = 0; i < exportedNames.Count; i++)
  95. {
  96. var name = exportedNames[i];
  97. var resolution = module.ResolveExport(name);
  98. if(resolution is not null)
  99. {
  100. unambiguousNames.Add(name);
  101. }
  102. }
  103. ns = CreateModuleNamespace(module, unambiguousNames);
  104. }
  105. return ns;
  106. }
  107. /// <summary>
  108. /// https://tc39.es/ecma262/#sec-modulenamespacecreate
  109. /// </summary>
  110. private static ObjectInstance CreateModuleNamespace(JsModule module, List<string> unambiguousNames)
  111. {
  112. var m = new ModuleNamespace(module._engine, module, unambiguousNames);
  113. module._namespace = m;
  114. return m;
  115. }
  116. internal void BindExportedValue(string name, JsValue value)
  117. {
  118. _environment.CreateImmutableBindingAndInitialize(name, true, value);
  119. _localExportEntries.Add(new ExportEntry(name, null, null, null));
  120. }
  121. /// <summary>
  122. /// https://tc39.es/ecma262/#sec-getexportednames
  123. /// </summary>
  124. public List<string> GetExportedNames(List<JsModule> exportStarSet = null)
  125. {
  126. exportStarSet ??= new();
  127. if (exportStarSet.Contains(this))
  128. {
  129. //Reached the starting point of an export * circularity
  130. return new();
  131. }
  132. exportStarSet.Add(this);
  133. var exportedNames = new List<string>();
  134. for (var i = 0; i < _localExportEntries.Count; i++)
  135. {
  136. var e = _localExportEntries[i];
  137. exportedNames.Add(e.ImportName ?? e.ExportName);
  138. }
  139. for (var i = 0; i < _indirectExportEntries.Count; i++)
  140. {
  141. var e = _indirectExportEntries[i];
  142. exportedNames.Add(e.ImportName ?? e.ExportName);
  143. }
  144. for(var i = 0; i < _starExportEntries.Count; i++)
  145. {
  146. var e = _starExportEntries[i];
  147. var requestedModule = _engine._host.ResolveImportedModule(this, e.ModuleRequest);
  148. var starNames = requestedModule.GetExportedNames(exportStarSet);
  149. for (var j = 0; j < starNames.Count; j++)
  150. {
  151. var n = starNames[j];
  152. if (!"default".Equals(n) && !exportedNames.Contains(n))
  153. {
  154. exportedNames.Add(n);
  155. }
  156. }
  157. }
  158. return exportedNames;
  159. }
  160. /// <summary>
  161. /// https://tc39.es/ecma262/#sec-resolveexport
  162. /// </summary>
  163. internal ResolvedBinding ResolveExport(string exportName, List<ExportResolveSetItem> resolveSet = null)
  164. {
  165. resolveSet ??= new();
  166. for(var i = 0; i < resolveSet.Count; i++)
  167. {
  168. var r = resolveSet[i];
  169. if(this == r.Module && exportName == r.ExportName)
  170. {
  171. //circular import request
  172. return null;
  173. }
  174. }
  175. resolveSet.Add(new(this, exportName));
  176. for(var i = 0; i < _localExportEntries.Count; i++)
  177. {
  178. var e = _localExportEntries[i];
  179. if (exportName == (e.ImportName ?? e.ExportName))
  180. {
  181. return new ResolvedBinding(this, e.LocalName ?? e.ExportName);
  182. }
  183. }
  184. for(var i = 0; i < _indirectExportEntries.Count; i++)
  185. {
  186. var e = _localExportEntries[i];
  187. if (exportName.Equals(e.ImportName ?? e.ExportName))
  188. {
  189. var importedModule = _engine._host.ResolveImportedModule(this, e.ModuleRequest);
  190. if(e.ImportName == "*")
  191. {
  192. return new ResolvedBinding(importedModule, "*namespace*");
  193. }
  194. else
  195. {
  196. return importedModule.ResolveExport(e.ImportName, resolveSet);
  197. }
  198. }
  199. }
  200. if ("default".Equals(exportName))
  201. {
  202. return null;
  203. }
  204. ResolvedBinding starResolution = null;
  205. for(var i = 0; i < _starExportEntries.Count; i++)
  206. {
  207. var e = _starExportEntries[i];
  208. var importedModule = _engine._host.ResolveImportedModule(this, e.ModuleRequest);
  209. var resolution = importedModule.ResolveExport(exportName, resolveSet);
  210. if(resolution == ResolvedBinding.Ambiguous)
  211. {
  212. return resolution;
  213. }
  214. if(resolution is not null)
  215. {
  216. if(starResolution is null)
  217. {
  218. starResolution = resolution;
  219. }
  220. else
  221. {
  222. if(resolution.Module != starResolution.Module || resolution.BindingName != starResolution.BindingName)
  223. {
  224. return ResolvedBinding.Ambiguous;
  225. }
  226. }
  227. }
  228. }
  229. return starResolution;
  230. }
  231. /// <summary>
  232. /// https://tc39.es/ecma262/#sec-moduledeclarationlinking
  233. /// </summary>
  234. public void Link()
  235. {
  236. if (Status == ModuleStatus.Linking || Status == ModuleStatus.Evaluating)
  237. {
  238. ExceptionHelper.ThrowInvalidOperationException("Error while linking module: Module is already either linking or evaluating");
  239. }
  240. var stack = new Stack<JsModule>();
  241. try
  242. {
  243. Link(this, stack, 0);
  244. }
  245. catch
  246. {
  247. foreach (var m in stack)
  248. {
  249. m.Status = ModuleStatus.Unlinked;
  250. m._environment = null;
  251. m._dfsIndex = -1;
  252. m._dfsAncestorIndex = -1;
  253. }
  254. Status = ModuleStatus.Unlinked;
  255. throw;
  256. }
  257. if (Status != ModuleStatus.Linked && Status != ModuleStatus.Unlinked)
  258. {
  259. ExceptionHelper.ThrowInvalidOperationException("Error while linking module: Module is neither linked or unlinked");
  260. }
  261. if(stack.Any())
  262. {
  263. ExceptionHelper.ThrowInvalidOperationException("Error while linking module: One or more modules were not linked");
  264. }
  265. }
  266. /// <summary>
  267. /// https://tc39.es/ecma262/#sec-moduleevaluation
  268. /// </summary>
  269. public JsValue Evaluate()
  270. {
  271. var module = this;
  272. if (module.Status != ModuleStatus.Linked &&
  273. module.Status != ModuleStatus.EvaluatingAsync &&
  274. module.Status != ModuleStatus.Evaluated)
  275. {
  276. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  277. }
  278. if (module.Status == ModuleStatus.EvaluatingAsync || module.Status == ModuleStatus.Evaluated)
  279. {
  280. module = module._cycleRoot;
  281. }
  282. if (module._topLevelCapability is not null)
  283. {
  284. return module._topLevelCapability.PromiseInstance;
  285. }
  286. var stack = new Stack<JsModule>();
  287. var capability = PromiseConstructor.NewPromiseCapability(_engine, _realm.Intrinsics.Promise);
  288. int asyncEvalOrder = 0;
  289. module._topLevelCapability = capability;
  290. var result = Evaluate(module, stack, 0, ref asyncEvalOrder);
  291. if(result.Type != CompletionType.Normal)
  292. {
  293. foreach(var m in stack)
  294. {
  295. m.Status = ModuleStatus.Evaluated;
  296. m._evalError = result;
  297. }
  298. capability.Reject.Call(Undefined, new [] { result.Value });
  299. }
  300. else
  301. {
  302. if (module.Status != ModuleStatus.EvaluatingAsync && module.Status != ModuleStatus.Evaluated)
  303. {
  304. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  305. }
  306. if (module._evalError is not null)
  307. {
  308. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  309. }
  310. if (!module._asyncEvaluation)
  311. {
  312. if(module.Status != ModuleStatus.Evaluated)
  313. {
  314. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  315. }
  316. capability.Resolve.Call(Undefined, Array.Empty<JsValue>());
  317. }
  318. if (stack.Any())
  319. {
  320. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  321. }
  322. }
  323. return capability.PromiseInstance;
  324. }
  325. /// <summary>
  326. /// https://tc39.es/ecma262/#sec-InnerModuleLinking
  327. /// </summary>
  328. private int Link(JsModule module, Stack<JsModule> stack, int index)
  329. {
  330. if(module.Status is
  331. ModuleStatus.Linking or
  332. ModuleStatus.Linked or
  333. ModuleStatus.EvaluatingAsync or
  334. ModuleStatus.Evaluating)
  335. {
  336. return index;
  337. }
  338. if(module.Status != ModuleStatus.Unlinked)
  339. {
  340. ExceptionHelper.ThrowInvalidOperationException("Error while linking module: Module in an invalid state");
  341. }
  342. module.Status = ModuleStatus.Linking;
  343. module._dfsIndex = index;
  344. module._dfsAncestorIndex = index;
  345. index++;
  346. stack.Push(module);
  347. var requestedModules = module._requestedModules;
  348. foreach (var moduleSpecifier in requestedModules)
  349. {
  350. var requiredModule = _engine._host.ResolveImportedModule(module, moduleSpecifier);
  351. //TODO: Should we link only when a module is requested? https://tc39.es/ecma262/#sec-example-cyclic-module-record-graphs Should we support retry?
  352. if (requiredModule.Status == ModuleStatus.Unlinked)
  353. requiredModule.Link();
  354. if (requiredModule.Status != ModuleStatus.Linking &&
  355. requiredModule.Status != ModuleStatus.Linked &&
  356. requiredModule.Status != ModuleStatus.Evaluated)
  357. {
  358. ExceptionHelper.ThrowInvalidOperationException($"Error while linking module: Required module is in an invalid state: {requiredModule.Status}");
  359. }
  360. if(requiredModule.Status == ModuleStatus.Linking && !stack.Contains(requiredModule))
  361. {
  362. ExceptionHelper.ThrowInvalidOperationException($"Error while linking module: Required module is in an invalid state: {requiredModule.Status}");
  363. }
  364. if (requiredModule.Status == ModuleStatus.Linking)
  365. {
  366. module._dfsAncestorIndex = System.Math.Min(module._dfsAncestorIndex, requiredModule._dfsAncestorIndex);
  367. }
  368. }
  369. module.InitializeEnvironment();
  370. if (stack.Count(m => m == module) != 1)
  371. {
  372. ExceptionHelper.ThrowInvalidOperationException("Error while linking module: Recursive dependency detected");
  373. }
  374. if (module._dfsIndex > module._dfsAncestorIndex)
  375. {
  376. ExceptionHelper.ThrowInvalidOperationException("Error while linking module: Recursive dependency detected");
  377. }
  378. if (module._dfsIndex == module._dfsAncestorIndex)
  379. {
  380. while (true)
  381. {
  382. var requiredModule = stack.Pop();
  383. requiredModule.Status = ModuleStatus.Linked;
  384. if (requiredModule == module)
  385. {
  386. break;
  387. }
  388. }
  389. }
  390. return index;
  391. }
  392. /// <summary>
  393. /// https://tc39.es/ecma262/#sec-innermoduleevaluation
  394. /// </summary>
  395. private Completion Evaluate(JsModule module, Stack<JsModule> stack, int index, ref int asyncEvalOrder)
  396. {
  397. if(module.Status == ModuleStatus.EvaluatingAsync || module.Status == ModuleStatus.Evaluated)
  398. {
  399. if(module._evalError is null)
  400. {
  401. return new Completion(CompletionType.Normal, index, null, default);
  402. }
  403. return module._evalError.Value;
  404. }
  405. if(module.Status == ModuleStatus.Evaluating)
  406. {
  407. return new Completion(CompletionType.Normal, index, null, default);
  408. }
  409. if (module.Status != ModuleStatus.Linked)
  410. {
  411. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  412. }
  413. module.Status = ModuleStatus.Evaluating;
  414. module._dfsIndex = index;
  415. module._dfsAncestorIndex = index;
  416. module._pendingAsyncDependencies = 0;
  417. index++;
  418. stack.Push(module);
  419. var requestedModules = module._requestedModules;
  420. foreach (var moduleSpecifier in requestedModules)
  421. {
  422. var requiredModule = _engine._host.ResolveImportedModule(module, moduleSpecifier);
  423. var result = Evaluate(module, stack, index, ref asyncEvalOrder);
  424. if(result.Type != CompletionType.Normal)
  425. {
  426. return result;
  427. }
  428. index = TypeConverter.ToInt32(result.Value);
  429. // TODO: Validate this behavior: https://tc39.es/ecma262/#sec-example-cyclic-module-record-graphs
  430. if (requiredModule.Status == ModuleStatus.Linked)
  431. {
  432. var evaluationResult = requiredModule.Evaluate();
  433. if (evaluationResult == null)
  434. {
  435. ExceptionHelper.ThrowInvalidOperationException($"Error while evaluating module: Module evaluation did not return a promise");
  436. }
  437. else if (evaluationResult is not PromiseInstance promise)
  438. {
  439. ExceptionHelper.ThrowInvalidOperationException($"Error while evaluating module: Module evaluation did not return a promise: {evaluationResult.Type}");
  440. }
  441. else if (promise.State == PromiseState.Rejected)
  442. {
  443. ExceptionHelper.ThrowJavaScriptException(_engine, promise.Value, new Completion(CompletionType.Throw, promise.Value, null, new Location(new Position(), new Position(), moduleSpecifier)));
  444. }
  445. else if (promise.State != PromiseState.Fulfilled)
  446. {
  447. ExceptionHelper.ThrowInvalidOperationException($"Error while evaluating module: Module evaluation did not return a fulfilled promise: {promise.State}");
  448. }
  449. }
  450. if (requiredModule.Status != ModuleStatus.Evaluating &&
  451. requiredModule.Status != ModuleStatus.EvaluatingAsync &&
  452. requiredModule.Status != ModuleStatus.Evaluated)
  453. {
  454. ExceptionHelper.ThrowInvalidOperationException($"Error while evaluating module: Module is in an invalid state: {requiredModule.Status}");
  455. }
  456. if (requiredModule.Status == ModuleStatus.Evaluating && !stack.Contains(requiredModule))
  457. {
  458. ExceptionHelper.ThrowInvalidOperationException($"Error while evaluating module: Module is in an invalid state: {requiredModule.Status}");
  459. }
  460. if(requiredModule.Status == ModuleStatus.Evaluating)
  461. {
  462. module._dfsAncestorIndex = System.Math.Min(module._dfsAncestorIndex, requiredModule._dfsAncestorIndex);
  463. }
  464. else
  465. {
  466. requiredModule = requiredModule._cycleRoot;
  467. if(requiredModule.Status != ModuleStatus.EvaluatingAsync && requiredModule.Status != ModuleStatus.Evaluated)
  468. {
  469. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  470. }
  471. }
  472. if (requiredModule._asyncEvaluation)
  473. {
  474. module._pendingAsyncDependencies++;
  475. requiredModule._asyncParentModules.Add(module);
  476. }
  477. }
  478. Completion completion;
  479. if(module._pendingAsyncDependencies > 0 || module._hasTLA)
  480. {
  481. if (module._asyncEvaluation)
  482. {
  483. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  484. }
  485. module._asyncEvaluation = true;
  486. module._asyncEvalOrder = asyncEvalOrder++;
  487. if (module._pendingAsyncDependencies == 0)
  488. {
  489. completion = module.ExecuteAsync();
  490. }
  491. else
  492. {
  493. completion = module.Execute();
  494. }
  495. }
  496. else
  497. {
  498. completion = module.Execute();
  499. }
  500. if(stack.Count(x => x == module) != 1)
  501. {
  502. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  503. }
  504. if (module._dfsAncestorIndex > module._dfsIndex)
  505. {
  506. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  507. }
  508. if(module._dfsIndex == module._dfsAncestorIndex)
  509. {
  510. bool done = false;
  511. while (!done)
  512. {
  513. var requiredModule = stack.Pop();
  514. if (!requiredModule._asyncEvaluation)
  515. {
  516. requiredModule.Status = ModuleStatus.Evaluated;
  517. }
  518. else
  519. {
  520. requiredModule.Status = ModuleStatus.EvaluatingAsync;
  521. }
  522. done = requiredModule == module;
  523. requiredModule._cycleRoot = module;
  524. }
  525. }
  526. return completion;
  527. }
  528. /// <summary>
  529. /// https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment
  530. /// </summary>
  531. private void InitializeEnvironment()
  532. {
  533. for(var i = 0; i < _indirectExportEntries.Count; i++)
  534. {
  535. var e = _indirectExportEntries[i];
  536. var resolution = ResolveExport(e.ExportName);
  537. if (resolution is null || resolution == ResolvedBinding.Ambiguous)
  538. {
  539. ExceptionHelper.ThrowSyntaxError(_realm, "Ambiguous import statement for identifier: " + e.ExportName);
  540. }
  541. }
  542. var realm = _realm;
  543. var env = JintEnvironment.NewModuleEnvironment(_engine, realm.GlobalEnv);
  544. _environment = env;
  545. if (_importEntries != null)
  546. {
  547. for (var i = 0; i < _importEntries.Count; i++)
  548. {
  549. var ie = _importEntries[i];
  550. var importedModule = _engine._host.ResolveImportedModule(this, ie.ModuleRequest);
  551. if (ie.ImportName == "*")
  552. {
  553. var ns = GetModuleNamespace(importedModule);
  554. env.CreateImmutableBinding(ie.LocalName, true);
  555. env.InitializeBinding(ie.LocalName, ns);
  556. }
  557. else
  558. {
  559. var resolution = importedModule.ResolveExport(ie.ImportName);
  560. if (resolution is null || resolution == ResolvedBinding.Ambiguous)
  561. {
  562. ExceptionHelper.ThrowSyntaxError(_realm, "Ambigous import statement for identifier " + ie.ImportName);
  563. }
  564. if (resolution.BindingName == "*namespace*")
  565. {
  566. var ns = GetModuleNamespace(resolution.Module);
  567. env.CreateImmutableBinding(ie.LocalName, true);
  568. env.InitializeBinding(ie.LocalName, ns);
  569. }
  570. else
  571. {
  572. env.CreateImportBinding(ie.LocalName, resolution.Module, resolution.BindingName);
  573. }
  574. }
  575. }
  576. }
  577. var moduleContext = new ExecutionContext(this, _environment, _environment, null, realm, null);
  578. _context = moduleContext;
  579. _engine.EnterExecutionContext(_context);
  580. var hoistingScope = HoistingScope.GetModuleLevelDeclarations(_source);
  581. var varDeclarations = hoistingScope._variablesDeclarations;
  582. var declaredVarNames = new List<string>();
  583. if(varDeclarations != null)
  584. {
  585. var boundNames = new List<string>();
  586. for(var i = 0; i < varDeclarations.Count; i++)
  587. {
  588. var d = varDeclarations[i];
  589. boundNames.Clear();
  590. d.GetBoundNames(boundNames);
  591. for(var j = 0; j < boundNames.Count; j++)
  592. {
  593. var dn = boundNames[j];
  594. if (!declaredVarNames.Contains(dn))
  595. {
  596. env.CreateMutableBinding(dn, false);
  597. env.InitializeBinding(dn, Undefined);
  598. declaredVarNames.Add(dn);
  599. }
  600. }
  601. }
  602. }
  603. var lexDeclarations = hoistingScope._lexicalDeclarations;
  604. if(lexDeclarations != null)
  605. {
  606. var boundNames = new List<string>();
  607. for(var i = 0; i < lexDeclarations.Count; i++)
  608. {
  609. var d = lexDeclarations[i];
  610. boundNames.Clear();
  611. d.GetBoundNames(boundNames);
  612. for (var j = 0; j < boundNames.Count; j++)
  613. {
  614. var dn = boundNames[j];
  615. if(d.Kind == VariableDeclarationKind.Const)
  616. {
  617. env.CreateImmutableBinding(dn, true);
  618. }
  619. else
  620. {
  621. env.CreateMutableBinding(dn, false);
  622. }
  623. }
  624. }
  625. }
  626. var functionDeclarations = hoistingScope._functionDeclarations;
  627. if(functionDeclarations != null)
  628. {
  629. for(var i = 0; i < functionDeclarations.Count; i++)
  630. {
  631. var d = functionDeclarations[i];
  632. var fn = d.Id.Name;
  633. var fd = new JintFunctionDefinition(_engine, d);
  634. env.CreateImmutableBinding(fn, true);
  635. var fo = realm.Intrinsics.Function.InstantiateFunctionObject(fd, env);
  636. env.InitializeBinding(fn, fo);
  637. }
  638. }
  639. _engine.LeaveExecutionContext();
  640. }
  641. /// <summary>
  642. /// https://tc39.es/ecma262/#sec-source-text-module-record-execute-module
  643. /// </summary>
  644. private Completion Execute(PromiseCapability capability = null)
  645. {
  646. var moduleContext = new ExecutionContext(this, _environment, _environment, null, _realm);
  647. if (!_hasTLA)
  648. {
  649. using (new StrictModeScope(strict: true))
  650. {
  651. _engine.EnterExecutionContext(moduleContext);
  652. var statementList = new JintStatementList(null, _source.Body);
  653. var result = statementList.Execute(_engine._activeEvaluationContext ?? new EvaluationContext(_engine)); //Create new evaluation context when called from e.g. module tests
  654. _engine.LeaveExecutionContext();
  655. return result;
  656. }
  657. }
  658. else
  659. {
  660. //ToDo async modules
  661. return default;
  662. }
  663. }
  664. /// <summary>
  665. /// https://tc39.es/ecma262/#sec-execute-async-module
  666. /// </summary>
  667. private Completion ExecuteAsync()
  668. {
  669. if((Status != ModuleStatus.Evaluating && Status != ModuleStatus.EvaluatingAsync) || !_hasTLA)
  670. {
  671. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  672. }
  673. var capability = PromiseConstructor.NewPromiseCapability(_engine, _realm.Intrinsics.Promise);
  674. var onFullfilled = new ClrFunctionInstance(_engine, "fulfilled", AsyncModuleExecutionFulfilled, 1, PropertyFlag.Configurable);
  675. var onRejected = new ClrFunctionInstance(_engine, "rejected", AsyncModuleExecutionRejected, 1, PropertyFlag.Configurable);
  676. PromiseOperations.PerformPromiseThen(_engine, (PromiseInstance)capability.PromiseInstance, onFullfilled, onRejected, null);
  677. return Execute(capability);
  678. }
  679. /// <summary>
  680. /// https://tc39.es/ecma262/#sec-gather-available-ancestors
  681. /// </summary>
  682. private void GatherAvailableAncestors(List<JsModule> execList)
  683. {
  684. foreach(var m in _asyncParentModules)
  685. {
  686. if(!execList.Contains(m) && m._cycleRoot._evalError is null)
  687. {
  688. if(m.Status != ModuleStatus.EvaluatingAsync ||
  689. m._evalError is not null ||
  690. !m._asyncEvaluation ||
  691. m._pendingAsyncDependencies <= 0)
  692. {
  693. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  694. }
  695. if(--m._pendingAsyncDependencies == 0)
  696. {
  697. execList.Add(m);
  698. if (!m._hasTLA)
  699. {
  700. m.GatherAvailableAncestors(execList);
  701. }
  702. }
  703. }
  704. }
  705. }
  706. /// <summary>
  707. /// https://tc39.es/ecma262/#sec-async-module-execution-fulfilled
  708. /// </summary>
  709. private JsValue AsyncModuleExecutionFulfilled(JsValue thisObj, JsValue[] arguments)
  710. {
  711. var module = (JsModule)arguments.At(0);
  712. if (module.Status == ModuleStatus.Evaluated)
  713. {
  714. if(module._evalError is not null)
  715. {
  716. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  717. }
  718. return Undefined;
  719. }
  720. if (module.Status != ModuleStatus.EvaluatingAsync ||
  721. !module._asyncEvaluation ||
  722. module._evalError is not null)
  723. {
  724. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  725. }
  726. if (module._topLevelCapability is not null)
  727. {
  728. if(module._cycleRoot is null)
  729. {
  730. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  731. }
  732. module._topLevelCapability.Resolve.Call(Undefined, Array.Empty<JsValue>());
  733. }
  734. var execList = new List<JsModule>();
  735. module.GatherAvailableAncestors(execList);
  736. execList.Sort((x, y) => x._asyncEvalOrder - y._asyncEvalOrder);
  737. for(var i = 0; i < execList.Count; i++)
  738. {
  739. var m = execList[i];
  740. if (m.Status == ModuleStatus.Evaluated && m._evalError is null)
  741. {
  742. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  743. }
  744. else if (m._hasTLA)
  745. {
  746. m.ExecuteAsync();
  747. }
  748. else
  749. {
  750. var result = m.Execute();
  751. if(result.Type != CompletionType.Normal)
  752. {
  753. AsyncModuleExecutionRejected(Undefined, new[] { m, result.Value });
  754. }
  755. else
  756. {
  757. m.Status = ModuleStatus.Evaluated;
  758. if(m._topLevelCapability is not null)
  759. {
  760. if (m._cycleRoot is null)
  761. {
  762. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  763. }
  764. m._topLevelCapability.Resolve.Call(Undefined, Array.Empty<JsValue>());
  765. }
  766. }
  767. }
  768. }
  769. return Undefined;
  770. }
  771. /// <summary>
  772. /// https://tc39.es/ecma262/#sec-async-module-execution-rejected
  773. /// </summary>
  774. private JsValue AsyncModuleExecutionRejected(JsValue thisObj, JsValue[] arguments)
  775. {
  776. JsModule module = (JsModule)arguments.At(0);
  777. JsValue error = arguments.At(1);
  778. if (module.Status == ModuleStatus.Evaluated)
  779. {
  780. if(module._evalError is null)
  781. {
  782. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  783. }
  784. return Undefined;
  785. }
  786. if (module.Status != ModuleStatus.EvaluatingAsync ||
  787. !module._asyncEvaluation ||
  788. module._evalError is not null)
  789. {
  790. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  791. }
  792. module._evalError = new Completion(CompletionType.Throw, error, null, default);
  793. module.Status = ModuleStatus.Evaluated;
  794. var asyncParentModules = module._asyncParentModules;
  795. for (var i = 0; i < asyncParentModules.Count; i++)
  796. {
  797. var m = asyncParentModules[i];
  798. AsyncModuleExecutionRejected(thisObj, new[] { m, error });
  799. }
  800. if (module._topLevelCapability is not null)
  801. {
  802. if (module._cycleRoot is null)
  803. {
  804. ExceptionHelper.ThrowInvalidOperationException("Error while evaluating module: Module is in an invalid state");
  805. }
  806. module._topLevelCapability.Reject.Call(Undefined, new [] { error });
  807. }
  808. return Undefined;
  809. }
  810. public override object ToObject()
  811. {
  812. ExceptionHelper.ThrowNotSupportedException();
  813. return null;
  814. }
  815. public override string ToString()
  816. {
  817. return $"{Type}: {Location}";
  818. }
  819. }