#nullable disable
using Jint.Native;
using Jint.Native.Array;
using Jint.Native.Object;
using Jint.Native.Symbol;
using Jint.Runtime.Descriptors;
namespace Jint.Runtime.Modules;
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects
///
internal sealed class ModuleNamespace : ObjectInstance
{
private readonly Module _module;
private readonly HashSet _exports;
public ModuleNamespace(Engine engine, Module module, List exports) : base(engine)
{
_module = module;
_exports = new HashSet(exports, StringComparer.Ordinal);
}
protected override void Initialize()
{
var symbols = new SymbolDictionary(1)
{
[GlobalSymbolRegistry.ToStringTag] = new("Module", false, false, false)
};
SetSymbols(symbols);
}
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-getprototypeof
///
protected internal override ObjectInstance GetPrototypeOf() => null;
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-setprototypeof-v
///
internal override bool SetPrototypeOf(JsValue value) => SetImmutablePrototype(value);
///
/// https://tc39.es/ecma262/#sec-set-immutable-prototype
///
private bool SetImmutablePrototype(JsValue value)
{
var current = GetPrototypeOf();
return SameValue(value, current ?? Null);
}
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-isextensible
///
public override bool Extensible => false;
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-preventextensions
///
public override bool PreventExtensions() => true;
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-getownproperty-p
///
public override PropertyDescriptor GetOwnProperty(JsValue property)
{
if (property.IsSymbol())
{
return base.GetOwnProperty(property);
}
var p = TypeConverter.ToString(property);
if (!_exports.Contains(p))
{
return PropertyDescriptor.Undefined;
}
var value = Get(property);
return new PropertyDescriptor(value, true, true, false);
}
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-defineownproperty-p-desc
///
public override bool DefineOwnProperty(JsValue property, PropertyDescriptor desc)
{
if (property.IsSymbol())
{
return base.DefineOwnProperty(property, desc);
}
var current = GetOwnProperty(property);
if (current == PropertyDescriptor.Undefined)
{
return false;
}
if (desc.Configurable)
{
return false;
}
if (desc.EnumerableSet && !desc.Enumerable)
{
return false;
}
if (desc.IsAccessorDescriptor())
{
return false;
}
if (desc.WritableSet && !desc.Writable)
{
return false;
}
if (desc.Value is not null)
{
return SameValue(desc.Value, current.Value);
}
return true;
}
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-hasproperty-p
///
public override bool HasProperty(JsValue property)
{
if (property.IsSymbol())
{
return base.HasProperty(property);
}
var p = TypeConverter.ToString(property);
return _exports.Contains(p);
}
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver
///
public override JsValue Get(JsValue property, JsValue receiver)
{
if (property.IsSymbol())
{
return base.Get(property, receiver);
}
var p = TypeConverter.ToString(property);
if (!_exports.Contains(p))
{
return Undefined;
}
var m = _module;
var binding = m.ResolveExport(p);
var targetModule = binding.Module;
if (string.Equals(binding.BindingName, "*namespace*", StringComparison.Ordinal))
{
return Module.GetModuleNamespace(targetModule);
}
var targetEnv = targetModule._environment;
if (targetEnv is null)
{
Throw.ReferenceError(_engine.Realm, "Module's environment hasn't been initialized");
}
return targetEnv.GetBindingValue(binding.BindingName, true);
}
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-set-p-v-receiver
///
public override bool Set(JsValue property, JsValue value, JsValue receiver)
{
return false;
}
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-delete-p
///
public override bool Delete(JsValue property)
{
if (property.IsSymbol())
{
return base.Delete(property);
}
var p = TypeConverter.ToString(property);
return !_exports.Contains(p);
}
///
/// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-ownpropertykeys
///
public override List GetOwnPropertyKeys(Types types = Types.String | Types.Symbol)
{
var result = new List();
if ((types & Types.String) != Types.Empty)
{
result.Capacity = _exports.Count;
foreach (var export in _exports)
{
result.Add(export);
}
result.Sort(ArrayPrototype.ArrayComparer.Default);
}
result.AddRange(base.GetOwnPropertyKeys(types));
return result;
}
}