using System.Collections.Generic; using Jint.Native; using Jint.Native.Object; using Jint.Runtime.Descriptors; namespace Jint.Runtime.Modules; /// /// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects /// internal sealed class ModuleNamespace : ObjectInstance { private readonly JsModule _module; private readonly HashSet _exports; public ModuleNamespace(Engine engine, JsModule module, List exports) : base(engine) { _module = module; exports.Sort(); _exports = new HashSet(exports); } protected internal override ObjectInstance GetPrototypeOf() => null; public override bool SetPrototypeOf(JsValue value) => SetImmutablePrototype(value); private bool SetImmutablePrototype(JsValue value) { var current = GetPrototypeOf(); return SameValue(value, current ?? Null); } public override bool Extensible => false; public override JsValue PreventExtensions() => JsBoolean.True; 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); } 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 || desc.Enumerable || desc.IsAccessorDescriptor() || !desc.Writable) { return false; } if (desc.Value is not null) { return SameValue(desc.Value, current.Value); } return true; } public override bool HasProperty(JsValue property) { if (property.IsSymbol()) { return base.HasProperty(property); } var p = TypeConverter.ToString(property); return _exports.Contains(p); } 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 (binding.BindingName == "*namespace*") { return JsModule.GetModuleNamespace(targetModule); } var targetEnv = targetModule._environment; if (targetEnv is null) { ExceptionHelper.ThrowReferenceError(_engine.Realm, "environment"); } return targetEnv.GetBindingValue(binding.BindingName, true); } public override bool Set(JsValue property, JsValue value, JsValue receiver) { return false; } public override bool Delete(JsValue property) { if (property.IsSymbol()) { return base.Delete(property); } var p = TypeConverter.ToString(property); return !_exports.Contains(p); } public override List GetOwnPropertyKeys(Types types = Types.String | Types.Symbol) { var keys = base.GetOwnPropertyKeys(types); if ((types & Types.String) != 0) { foreach (var export in _exports) { keys.Add(export); } } return keys; } }