using System.Linq; using Jint.Native; using Jint.Native.Object; using Jint.Native.Symbol; using Jint.Runtime.Descriptors; namespace Jint.Runtime.Environments { /// /// Represents an object environment record /// https://tc39.es/ecma262/#sec-object-environment-records /// internal sealed class ObjectEnvironmentRecord : EnvironmentRecord { internal readonly ObjectInstance _bindingObject; private readonly bool _provideThis; private readonly bool _withEnvironment; public ObjectEnvironmentRecord( Engine engine, ObjectInstance bindingObject, bool provideThis, bool withEnvironment) : base(engine) { _bindingObject = bindingObject; _provideThis = provideThis; _withEnvironment = withEnvironment; } public override bool HasBinding(string name) { var property = new JsString(name); var foundBinding = HasProperty(property); if (!foundBinding) { return false; } if (!_withEnvironment) { return true; } return !IsBlocked(name); } private bool HasProperty(JsValue property) { return _bindingObject.HasProperty(property); } internal override bool TryGetBinding( in BindingName name, bool strict, out Binding binding, out JsValue value) { // we unwrap by name binding = default; if (!HasProperty(name.StringValue)) { value = default; return false; } if (_withEnvironment && IsBlocked(name.StringValue)) { value = default; return false; } var desc = _bindingObject.GetProperty(name.StringValue); value = ObjectInstance.UnwrapJsValue(desc, _bindingObject); return true; } private bool IsBlocked(JsValue property) { var unscopables = _bindingObject.Get(GlobalSymbolRegistry.Unscopables); if (unscopables is ObjectInstance oi) { var blocked = TypeConverter.ToBoolean(oi.Get(property)); if (blocked) { return true; } } return false; } /// /// http://www.ecma-international.org/ecma-262/6.0/#sec-object-environment-records-createmutablebinding-n-d /// public override void CreateMutableBinding(string name, bool canBeDeleted = false) { var propertyDescriptor = canBeDeleted ? new PropertyDescriptor(Undefined, PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding) : new PropertyDescriptor(Undefined, PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding); _bindingObject.DefinePropertyOrThrow(name, propertyDescriptor); } /// /// http://www.ecma-international.org/ecma-262/6.0/#sec-object-environment-records-createmutablebinding-n-d /// internal void CreateMutableBindingAndInitialize(string name, JsValue value, bool canBeDeleted = false) { var propertyDescriptor = canBeDeleted ? new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding) : new PropertyDescriptor(value, PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding); _bindingObject.DefinePropertyOrThrow(name, propertyDescriptor); } /// /// http://www.ecma-international.org/ecma-262/6.0/#sec-object-environment-records-createimmutablebinding-n-s /// public override void CreateImmutableBinding(string name, bool strict = true) { ExceptionHelper.ThrowInvalidOperationException("The concrete Environment Record method CreateImmutableBinding is never used within this specification in association with Object Environment Records."); } /// /// http://www.ecma-international.org/ecma-262/6.0/#sec-object-environment-records-initializebinding-n-v /// public override void InitializeBinding(string name, JsValue value) { SetMutableBinding(name, value, false); } public override void SetMutableBinding(string name, JsValue value, bool strict) { SetMutableBinding(new BindingName(name), value, strict); } internal override void SetMutableBinding(in BindingName name, JsValue value, bool strict) { if (!_bindingObject.Set(name.StringValue, value) && strict) { ExceptionHelper.ThrowTypeError(_engine.Realm); } } public override JsValue GetBindingValue(string name, bool strict) { var desc = _bindingObject.GetProperty(name); if (strict && desc == PropertyDescriptor.Undefined) { ExceptionHelper.ThrowReferenceError(_engine.Realm, name.ToString()); } return ObjectInstance.UnwrapJsValue(desc, this); } public override bool DeleteBinding(string name) { return _bindingObject.Delete(name); } public override bool HasThisBinding() => false; public override bool HasSuperBinding() => false; public override JsValue WithBaseObject() => _withEnvironment ? _bindingObject : Undefined; internal override string[] GetAllBindingNames() { if (!ReferenceEquals(_bindingObject, null)) { return _bindingObject.GetOwnProperties().Select( x=> x.Key.ToString()).ToArray(); } return System.Array.Empty(); } public override bool Equals(JsValue other) { return ReferenceEquals(_bindingObject, other); } public override JsValue GetThisBinding() { throw new System.NotImplementedException(); } } }