ObjectEnvironment.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. using System.Diagnostics.CodeAnalysis;
  2. using Jint.Native;
  3. using Jint.Native.Object;
  4. using Jint.Native.Symbol;
  5. using Jint.Runtime.Descriptors;
  6. namespace Jint.Runtime.Environments;
  7. /// <summary>
  8. /// Represents an object environment record
  9. /// https://tc39.es/ecma262/#sec-object-environment-records
  10. /// </summary>
  11. internal sealed class ObjectEnvironment : Environment
  12. {
  13. internal readonly ObjectInstance _bindingObject;
  14. private readonly bool _provideThis;
  15. private readonly bool _withEnvironment;
  16. public ObjectEnvironment(
  17. Engine engine,
  18. ObjectInstance bindingObject,
  19. bool provideThis,
  20. bool withEnvironment) : base(engine)
  21. {
  22. _bindingObject = bindingObject;
  23. _provideThis = provideThis;
  24. _withEnvironment = withEnvironment;
  25. }
  26. internal override bool HasBinding(Key name) => HasBinding(JsString.Create(name.Name));
  27. internal override bool HasBinding(BindingName name) => HasBinding(name.Value);
  28. private bool HasBinding(JsString nameValue)
  29. {
  30. var foundBinding = _bindingObject.HasProperty(nameValue);
  31. if (!foundBinding)
  32. {
  33. return false;
  34. }
  35. if (!_withEnvironment)
  36. {
  37. return true;
  38. }
  39. return !IsBlocked(nameValue);
  40. }
  41. internal override bool TryGetBinding(BindingName name, bool strict, [NotNullWhen(true)] out JsValue? value)
  42. {
  43. // we unwrap by name
  44. if (!_bindingObject.HasProperty(name.Value))
  45. {
  46. value = default;
  47. return false;
  48. }
  49. if (_withEnvironment && IsBlocked(name.Value))
  50. {
  51. value = default;
  52. return false;
  53. }
  54. if (!_bindingObject.HasProperty(name.Value))
  55. {
  56. if (strict)
  57. {
  58. // data was deleted during reading of unscopable information, of course...
  59. Throw.ReferenceNameError(_engine.Realm, name.Key);
  60. }
  61. }
  62. value = _bindingObject.Get(name.Value);
  63. return true;
  64. }
  65. private bool IsBlocked(JsValue property)
  66. {
  67. var unscopables = _bindingObject.Get(GlobalSymbolRegistry.Unscopables);
  68. if (unscopables is ObjectInstance oi)
  69. {
  70. var blocked = TypeConverter.ToBoolean(oi.Get(property));
  71. if (blocked)
  72. {
  73. return true;
  74. }
  75. }
  76. return false;
  77. }
  78. /// <summary>
  79. /// http://www.ecma-international.org/ecma-262/6.0/#sec-object-environment-records-createmutablebinding-n-d
  80. /// </summary>
  81. internal override void CreateMutableBinding(Key name, bool canBeDeleted = false)
  82. {
  83. _bindingObject.DefinePropertyOrThrow(name.Name, new PropertyDescriptor(Undefined, canBeDeleted
  84. ? PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding
  85. : PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding));
  86. }
  87. /// <summary>
  88. /// https://tc39.es/ecma262/#sec-object-environment-records-createimmutablebinding-n-s
  89. /// </summary>
  90. internal override void CreateImmutableBinding(Key name, bool strict = true)
  91. {
  92. Throw.InvalidOperationException("The concrete Environment Record method CreateImmutableBinding is never used within this specification in association with Object Environment Records.");
  93. }
  94. /// <summary>
  95. /// https://tc39.es/ecma262/#sec-object-environment-records-initializebinding-n-v
  96. /// </summary>
  97. internal override void InitializeBinding(Key name, JsValue value, DisposeHint hint) => SetMutableBinding(name, value, strict: false);
  98. internal override void SetMutableBinding(Key name, JsValue value, bool strict)
  99. {
  100. var jsString = new JsString(name);
  101. if (!_bindingObject.HasProperty(jsString))
  102. {
  103. if (strict)
  104. {
  105. Throw.ReferenceNameError(_engine.Realm, name);
  106. }
  107. }
  108. _bindingObject.Set(jsString, value, strict);
  109. }
  110. internal override void SetMutableBinding(BindingName name, JsValue value, bool strict)
  111. {
  112. if (!_bindingObject.HasProperty(name.Value))
  113. {
  114. if (strict)
  115. {
  116. Throw.ReferenceNameError(_engine.Realm, name.Key);
  117. }
  118. }
  119. _bindingObject.Set(name.Value, value, strict);
  120. }
  121. internal override JsValue GetBindingValue(Key name, bool strict)
  122. {
  123. if (!_bindingObject.HasProperty(name.Name))
  124. {
  125. if (strict)
  126. {
  127. Throw.ReferenceNameError(_engine.Realm, name.Name);
  128. }
  129. }
  130. return _bindingObject.Get(name.Name);
  131. }
  132. internal override bool DeleteBinding(Key name) => _bindingObject.Delete(name.Name);
  133. internal override bool HasThisBinding() => false;
  134. internal override bool HasSuperBinding() => false;
  135. internal override JsValue WithBaseObject() => _withEnvironment ? _bindingObject : Undefined;
  136. internal override bool HasBindings() => _bindingObject._properties?.Count > 0;
  137. internal override string[] GetAllBindingNames()
  138. {
  139. if (_bindingObject is not null)
  140. {
  141. var names = new List<string>(_bindingObject._properties?.Count ?? 0);
  142. foreach (var name in _bindingObject.GetOwnProperties())
  143. {
  144. names.Add(name.Key.ToString());
  145. }
  146. return names.ToArray();
  147. }
  148. return [];
  149. }
  150. public override bool Equals(JsValue? other)
  151. {
  152. return ReferenceEquals(_bindingObject, other);
  153. }
  154. internal override JsValue GetThisBinding()
  155. {
  156. throw new NotImplementedException();
  157. }
  158. }