JintImportExpression.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. using Esprima.Ast;
  2. using Jint.Native;
  3. using Jint.Native.Object;
  4. using Jint.Native.Promise;
  5. using Jint.Runtime.Modules;
  6. namespace Jint.Runtime.Interpreter.Expressions;
  7. internal sealed class JintImportExpression : JintExpression
  8. {
  9. private JintExpression _specifierExpression;
  10. private bool _initialized;
  11. private JintExpression? _optionsExpression;
  12. public JintImportExpression(ImportExpression expression) : base(expression)
  13. {
  14. _specifierExpression = null!;
  15. }
  16. /// <summary>
  17. /// https://tc39.es/proposal-import-attributes/#sec-evaluate-import-call
  18. /// </summary>
  19. protected override object EvaluateInternal(EvaluationContext context)
  20. {
  21. if (!_initialized)
  22. {
  23. var expression = (ImportExpression) _expression;
  24. _specifierExpression = Build(expression.Source);
  25. _optionsExpression = expression.Options is not null ? Build(expression.Options) : null;
  26. _initialized = true;
  27. }
  28. var referrer = context.Engine.GetActiveScriptOrModule();
  29. var specifier = _specifierExpression.GetValue(context); //.UnwrapIfPromise();
  30. var options = _optionsExpression?.GetValue(context) ?? JsValue.Undefined;
  31. var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise);
  32. try
  33. {
  34. var specifierString = TypeConverter.ToString(specifier);
  35. var attributes = new List<ModuleImportAttribute>();
  36. if (!options.IsUndefined())
  37. {
  38. if (!options.IsObject())
  39. {
  40. ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Invalid options object");
  41. return JsValue.Undefined;
  42. }
  43. var attributesObj = options.Get("with");
  44. if (!attributesObj.IsUndefined())
  45. {
  46. if (attributesObj is not ObjectInstance oi)
  47. {
  48. ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Invalid options.with object");
  49. return JsValue.Undefined;
  50. }
  51. var entries = oi.EnumerableOwnProperties(ObjectInstance.EnumerableOwnPropertyNamesKind.KeyValue);
  52. attributes.Capacity = (int) entries.GetLength();
  53. foreach (var entry in entries)
  54. {
  55. var key = entry.Get("0");
  56. var value = entry.Get("1");
  57. if (!value.IsString())
  58. {
  59. ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Invalid option value " + value);
  60. return JsValue.Undefined;
  61. }
  62. attributes.Add(new ModuleImportAttribute(key.ToString(), TypeConverter.ToString(value)));
  63. }
  64. if (!AllImportAttributesSupported(context.Engine._host, attributes))
  65. {
  66. ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Unsupported import attributes detected");
  67. }
  68. attributes.Sort(static (item1, item2) => StringComparer.Ordinal.Compare(item1.Key, item2.Key));
  69. }
  70. }
  71. var moduleRequest = new ModuleRequest(Specifier: specifierString, Attributes: attributes.ToArray());
  72. context.Engine._host.LoadImportedModule(referrer, moduleRequest, promiseCapability);
  73. }
  74. catch (JavaScriptException e)
  75. {
  76. promiseCapability.Reject.Call(JsValue.Undefined, new[] { e.Error });
  77. }
  78. return promiseCapability.PromiseInstance;
  79. }
  80. private static bool AllImportAttributesSupported(Host host, List<ModuleImportAttribute> attributes)
  81. {
  82. var supported = host.GetSupportedImportAttributes();
  83. foreach (var pair in attributes)
  84. {
  85. if (!supported.Contains(pair.Key))
  86. {
  87. return false;
  88. }
  89. }
  90. return true;
  91. }
  92. }