SourceTextModuleRecord.cs 12 KB

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