Engine.Modules.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using Esprima;
  2. using Jint.Native;
  3. using Jint.Native.Object;
  4. using Jint.Native.Promise;
  5. using Jint.Runtime;
  6. using Jint.Runtime.Interpreter;
  7. using Jint.Runtime.Modules;
  8. namespace Jint
  9. {
  10. public partial class Engine
  11. {
  12. public ModuleOperations Modules { get; internal set; } = null!;
  13. /// <summary>
  14. /// https://tc39.es/ecma262/#sec-getactivescriptormodule
  15. /// </summary>
  16. internal IScriptOrModule? GetActiveScriptOrModule()
  17. {
  18. return _executionContexts?.GetActiveScriptOrModule();
  19. }
  20. }
  21. public class ModuleOperations
  22. {
  23. private readonly Engine _engine;
  24. private readonly Dictionary<string, Module> _modules = new(StringComparer.Ordinal);
  25. private readonly Dictionary<string, ModuleBuilder> _builders = new(StringComparer.Ordinal);
  26. public ModuleOperations(Engine engine, IModuleLoader moduleLoader)
  27. {
  28. ModuleLoader = moduleLoader;
  29. _engine = engine;
  30. }
  31. internal IModuleLoader ModuleLoader { get; }
  32. internal Module Load(string? referencingModuleLocation, ModuleRequest request)
  33. {
  34. var specifier = request.Specifier;
  35. var moduleResolution = ModuleLoader.Resolve(referencingModuleLocation, request);
  36. if (_modules.TryGetValue(moduleResolution.Key, out var module))
  37. {
  38. return module;
  39. }
  40. if (_builders.TryGetValue(specifier, out var moduleBuilder))
  41. {
  42. module = LoadFromBuilder(specifier, moduleBuilder, moduleResolution);
  43. }
  44. else
  45. {
  46. module = LoadFromModuleLoader(moduleResolution);
  47. }
  48. if (module is SourceTextModule sourceTextModule)
  49. {
  50. _engine.Debugger.OnBeforeEvaluate(sourceTextModule._source);
  51. }
  52. return module;
  53. }
  54. private BuilderModule LoadFromBuilder(string specifier, ModuleBuilder moduleBuilder, ResolvedSpecifier moduleResolution)
  55. {
  56. var parsedModule = moduleBuilder.Parse();
  57. var module = new BuilderModule(_engine, _engine.Realm, parsedModule, location: null, async: false);
  58. _modules[moduleResolution.Key] = module;
  59. moduleBuilder.BindExportedValues(module);
  60. _builders.Remove(specifier);
  61. return module;
  62. }
  63. private Module LoadFromModuleLoader(ResolvedSpecifier moduleResolution)
  64. {
  65. var module = ModuleLoader.LoadModule(_engine, moduleResolution);
  66. _modules[moduleResolution.Key] = module;
  67. return module;
  68. }
  69. public void Add(string specifier, string code)
  70. {
  71. var moduleBuilder = new ModuleBuilder(_engine, specifier);
  72. moduleBuilder.AddSource(code);
  73. Add(specifier, moduleBuilder);
  74. }
  75. public void Add(string specifier, Action<ModuleBuilder> buildModule)
  76. {
  77. var moduleBuilder = new ModuleBuilder(_engine, specifier);
  78. buildModule(moduleBuilder);
  79. Add(specifier, moduleBuilder);
  80. }
  81. public void Add(string specifier, ModuleBuilder moduleBuilder)
  82. {
  83. _builders.Add(specifier, moduleBuilder);
  84. }
  85. public ObjectInstance Import(string specifier)
  86. {
  87. return Import(specifier, referencingModuleLocation: null);
  88. }
  89. internal ObjectInstance Import(string specifier, string? referencingModuleLocation)
  90. {
  91. return Import(new ModuleRequest(specifier, []), referencingModuleLocation);
  92. }
  93. internal ObjectInstance Import(ModuleRequest request, string? referencingModuleLocation)
  94. {
  95. var moduleResolution = ModuleLoader.Resolve(referencingModuleLocation, request);
  96. if (!_modules.TryGetValue(moduleResolution.Key, out var module))
  97. {
  98. module = Load(referencingModuleLocation: null, request);
  99. }
  100. if (module is not CyclicModule cyclicModule)
  101. {
  102. LinkModule(request.Specifier, module);
  103. EvaluateModule(request.Specifier, module);
  104. }
  105. else if (cyclicModule.Status == ModuleStatus.Unlinked)
  106. {
  107. LinkModule(request.Specifier, cyclicModule);
  108. if (cyclicModule.Status == ModuleStatus.Linked)
  109. {
  110. _engine.ExecuteWithConstraints(true, () => EvaluateModule(request.Specifier, cyclicModule));
  111. }
  112. if (cyclicModule.Status != ModuleStatus.Evaluated)
  113. {
  114. ExceptionHelper.ThrowNotSupportedException($"Error while evaluating module: Module is in an invalid state: '{cyclicModule.Status}'");
  115. }
  116. }
  117. _engine.RunAvailableContinuations();
  118. return Module.GetModuleNamespace(module);
  119. }
  120. private static void LinkModule(string specifier, Module module)
  121. {
  122. module.Link();
  123. }
  124. private JsValue EvaluateModule(string specifier, Module module)
  125. {
  126. var ownsContext = _engine._activeEvaluationContext is null;
  127. _engine. _activeEvaluationContext ??= new EvaluationContext(_engine);
  128. JsValue evaluationResult;
  129. try
  130. {
  131. evaluationResult = module.Evaluate();
  132. }
  133. finally
  134. {
  135. if (ownsContext)
  136. {
  137. _engine._activeEvaluationContext = null!;
  138. }
  139. }
  140. // This should instead be returned and resolved in ImportModule(specifier) only so Host.ImportModuleDynamically can use this promise
  141. if (evaluationResult is not JsPromise promise)
  142. {
  143. ExceptionHelper.ThrowInvalidOperationException($"Error while evaluating module: Module evaluation did not return a promise: {evaluationResult.Type}");
  144. }
  145. else if (promise.State == PromiseState.Rejected)
  146. {
  147. var location = module is CyclicModule cyclicModuleRecord
  148. ? cyclicModuleRecord.AbnormalCompletionLocation
  149. : Location.From(new Position(), new Position());
  150. var node = EsprimaExtensions.CreateLocationNode(location);
  151. ExceptionHelper.ThrowJavaScriptException(_engine, promise.Value, node.Location);
  152. }
  153. else if (promise.State != PromiseState.Fulfilled)
  154. {
  155. ExceptionHelper.ThrowInvalidOperationException($"Error while evaluating module: Module evaluation did not return a fulfilled promise: {promise.State}");
  156. }
  157. return evaluationResult;
  158. }
  159. }
  160. }