using Jint.Native;
using Jint.Native.Object;
using Jint.Native.Promise;
using Jint.Runtime.Modules;
namespace Jint.Runtime.Interpreter.Expressions;
internal sealed class JintImportExpression : JintExpression
{
private JintExpression _specifierExpression;
private bool _initialized;
private JintExpression? _optionsExpression;
public JintImportExpression(ImportExpression expression) : base(expression)
{
_specifierExpression = null!;
}
///
/// https://tc39.es/proposal-import-attributes/#sec-evaluate-import-call
///
protected override object EvaluateInternal(EvaluationContext context)
{
if (!_initialized)
{
var expression = (ImportExpression) _expression;
_specifierExpression = Build(expression.Source);
_optionsExpression = expression.Options is not null ? Build(expression.Options) : null;
_initialized = true;
}
var referrer = context.Engine.GetActiveScriptOrModule();
var specifier = _specifierExpression.GetValue(context); //.UnwrapIfPromise();
var options = _optionsExpression?.GetValue(context) ?? JsValue.Undefined;
var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise);
try
{
var specifierString = TypeConverter.ToString(specifier);
var attributes = new List();
if (!options.IsUndefined())
{
if (!options.IsObject())
{
ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Invalid options object");
return JsValue.Undefined;
}
var attributesObj = options.Get("with");
if (!attributesObj.IsUndefined())
{
if (attributesObj is not ObjectInstance oi)
{
ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Invalid options.with object");
return JsValue.Undefined;
}
var entries = oi.EnumerableOwnProperties(ObjectInstance.EnumerableOwnPropertyNamesKind.KeyValue);
attributes.Capacity = (int) entries.GetLength();
foreach (var entry in entries)
{
var key = entry.Get("0");
var value = entry.Get("1");
if (!value.IsString())
{
ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Invalid option value " + value);
return JsValue.Undefined;
}
attributes.Add(new ModuleImportAttribute(key.ToString(), TypeConverter.ToString(value)));
}
if (!AllImportAttributesSupported(context.Engine._host, attributes))
{
ExceptionHelper.ThrowTypeError(context.Engine.Realm, "Unsupported import attributes detected");
}
attributes.Sort(static (item1, item2) => StringComparer.Ordinal.Compare(item1.Key, item2.Key));
}
}
var moduleRequest = new ModuleRequest(Specifier: specifierString, Attributes: attributes.ToArray());
context.Engine._host.LoadImportedModule(referrer, moduleRequest, promiseCapability);
}
catch (JavaScriptException e)
{
promiseCapability.Reject.Call(JsValue.Undefined, new[] { e.Error });
}
return promiseCapability.PromiseInstance;
}
private static bool AllImportAttributesSupported(Host host, List attributes)
{
var supported = host.GetSupportedImportAttributes();
foreach (var pair in attributes)
{
if (!supported.Contains(pair.Key))
{
return false;
}
}
return true;
}
}