JsModule.cs 30 KB

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