JsModule.cs 30 KB

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