JintImportExpression.cs 4.0 KB

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